@wafertools/wafermap 0.30.0 → 0.30.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (86) hide show
  1. package/AGENTS.md +68 -9
  2. package/CHANGELOG.md +247 -0
  3. package/README.md +3 -3
  4. package/dist/packages/canvas-adapter/charts/barPanel.js +1 -1
  5. package/dist/packages/canvas-adapter/charts/binCluster.js +1 -1
  6. package/dist/packages/canvas-adapter/charts/boxplot.js +1 -1
  7. package/dist/packages/canvas-adapter/charts/capability.js +1 -1
  8. package/dist/packages/canvas-adapter/charts/chartShell.js +1 -1
  9. package/dist/packages/canvas-adapter/charts/correlation.js +2 -2
  10. package/dist/packages/canvas-adapter/charts/groupedBarPlot.d.ts +1 -0
  11. package/dist/packages/canvas-adapter/charts/histogram.js +1 -1
  12. package/dist/packages/canvas-adapter/charts/scatter.js +1 -1
  13. package/dist/packages/canvas-adapter/charts/testPassRate.js +1 -1
  14. package/dist/packages/canvas-adapter/charts/trend.js +1 -1
  15. package/dist/packages/canvas-adapter/chunked.d.ts +45 -0
  16. package/dist/packages/canvas-adapter/chunked.js +1 -0
  17. package/dist/packages/canvas-adapter/deprecated.d.ts +0 -3
  18. package/dist/packages/canvas-adapter/deprecated.js +1 -1
  19. package/dist/packages/canvas-adapter/dieList.js +1 -1
  20. package/dist/packages/canvas-adapter/exportName.d.ts +68 -0
  21. package/dist/packages/canvas-adapter/exportName.js +1 -0
  22. package/dist/packages/canvas-adapter/icons.js +2 -2
  23. package/dist/packages/canvas-adapter/index.d.ts +2 -1
  24. package/dist/packages/canvas-adapter/index.js +1 -1
  25. package/dist/packages/canvas-adapter/insightsTab.js +1 -1
  26. package/dist/packages/canvas-adapter/renderWaferGallery.d.ts +80 -3
  27. package/dist/packages/canvas-adapter/renderWaferGallery.js +1 -1
  28. package/dist/packages/canvas-adapter/renderWaferMap.d.ts +24 -2
  29. package/dist/packages/canvas-adapter/renderWaferMap.js +1 -1
  30. package/dist/packages/canvas-adapter/summaryPanel.d.ts +76 -15
  31. package/dist/packages/canvas-adapter/summaryPanel.js +3 -3
  32. package/dist/packages/canvas-adapter/toCanvas.js +1 -1
  33. package/dist/packages/canvas-adapter/toolbar.d.ts +6 -0
  34. package/dist/packages/canvas-adapter/toolbar.js +2 -2
  35. package/dist/packages/canvas-adapter/userGuideHtml.d.ts +1 -1
  36. package/dist/packages/canvas-adapter/userGuideHtml.js +15 -8
  37. package/dist/packages/canvas-adapter/version.d.ts +2 -2
  38. package/dist/packages/canvas-adapter/version.js +1 -1
  39. package/dist/packages/canvas-adapter/warnings.js +1 -1
  40. package/dist/packages/core/aggregates.js +1 -1
  41. package/dist/packages/core/deprecated.d.ts +8 -12
  42. package/dist/packages/core/deprecated.js +1 -1
  43. package/dist/packages/core/index.d.ts +1 -1
  44. package/dist/packages/core/index.js +1 -1
  45. package/dist/packages/core/utils.d.ts +67 -0
  46. package/dist/packages/core/utils.js +1 -1
  47. package/dist/packages/core/waferLabel.d.ts +12 -2
  48. package/dist/packages/core/waferLabel.js +1 -1
  49. package/dist/packages/renderer/binColors.d.ts +52 -0
  50. package/dist/packages/renderer/binColors.js +1 -1
  51. package/dist/packages/renderer/buildView.js +1 -1
  52. package/dist/packages/renderer/buildWaferMap.d.ts +30 -5
  53. package/dist/packages/renderer/buildWaferMap.js +1 -1
  54. package/dist/packages/renderer/deprecate.d.ts +7 -0
  55. package/dist/packages/renderer/deprecate.js +1 -1
  56. package/dist/packages/renderer/deprecated.d.ts +2 -2
  57. package/dist/packages/renderer/deprecated.js +1 -1
  58. package/dist/packages/renderer/index.d.ts +2 -1
  59. package/dist/packages/renderer/index.js +1 -1
  60. package/dist/packages/stats/analyzeWaferLot.js +1 -1
  61. package/dist/packages/stats/analyzeWaferMap.js +1 -1
  62. package/dist/packages/stats/capability.d.ts +72 -1
  63. package/dist/packages/stats/capability.js +1 -1
  64. package/dist/packages/stats/correlation.d.ts +30 -0
  65. package/dist/packages/stats/correlation.js +1 -1
  66. package/dist/packages/stats/deprecated.d.ts +30 -35
  67. package/dist/packages/stats/deprecated.js +1 -1
  68. package/dist/packages/stats/findingsNarrative.js +1 -1
  69. package/dist/packages/stats/index.d.ts +4 -2
  70. package/dist/packages/stats/index.js +1 -1
  71. package/dist/packages/stats/math.d.ts +27 -2
  72. package/dist/packages/stats/math.js +1 -1
  73. package/dist/packages/stats/normalizeInput.d.ts +10 -0
  74. package/dist/packages/stats/normalizeInput.js +1 -0
  75. package/dist/packages/stats/regions.d.ts +2 -0
  76. package/dist/packages/stats/regions.js +1 -1
  77. package/dist/packages/stats/renderFindingsReport.js +1 -1
  78. package/dist/packages/stats/renderSummaryReport.d.ts +39 -1
  79. package/dist/packages/stats/renderSummaryReport.js +27 -27
  80. package/dist/packages/stats/reportHtml.d.ts +0 -1
  81. package/dist/packages/stats/reportHtml.js +10 -10
  82. package/dist/packages/stats/summaryFigures.d.ts +47 -0
  83. package/dist/packages/stats/summaryFigures.js +1 -0
  84. package/dist/packages/stats/types.d.ts +118 -0
  85. package/llms.txt +12 -7
  86. package/package.json +2 -2
@@ -1 +1 @@
1
- import{buildFacetTable as De,facetValueOf as $e,FACET_NONE_VALUE as Le}from"../stats/facets.js";import{mergeTestDefs as Ee}from"../stats/mergeTestDefs.js";import{isParametricTest as Ie}from"../renderer/buildWaferMap.js";import{waferDisplayLabel as Oe}from"../core/waferLabel.js";import{INPUT_DEFAULT_PASS_BINS as A,itemPassBins as Pe,passBinsLabel as ne}from"../core/passBins.js";import{NO_DATA_FILL as ae}from"../renderer/colorMap.js";import{describeWaferPopulation as Ae,populationStat as Re}from"../stats/population.js";import{LEADING as We,ALPHA as je,SPACE as S,EDGE_GUTTER as K,FONT as G,CLR as k,RADIUS as Ge,SHADOW as He,controlStyle as Ye,wireControlHover as oe,wireTooltip as Fe}from"./toolbar.js";import{ICONS as Ne}from"./icons.js";import{renderCapabilityPanel as Me}from"./charts/capability.js";import{renderBoxplotPanel as Ve}from"./charts/boxplot.js";import{renderTrendPanel as _e}from"./charts/trend.js";import{renderHistogramPanel as Ue}from"./charts/histogram.js";import{renderCorrelationPanel as ze}from"./charts/correlation.js";import{renderScatterPanel as qe}from"./charts/scatter.js";import{renderBarPanel as re}from"./charts/barPanel.js";import{renderBinClusterPanel as Qe}from"./charts/binCluster.js";import{renderTestPassRatePanel as Ke}from"./charts/testPassRate.js";import{QUANTITY as Xe}from"./charts/palette.js";import{cardFrameStyle as Je,makeChartGridWrap as X,makeLabeledSelect as Ze,makeLinkedGroupSelect as et}from"./charts/chartShell.js";import{buildYieldData as M,buildYieldDataCombined as se}from"../stats/yield.js";import{buildBinParetoData as tt}from"../stats/binPareto.js";import{buildLotTestSection as nt,buildLotFunctionalSection as at,buildMetadataStripBox as ot}from"./summaryPanel.js";import{buildRegionYieldData as ie,buildRingRegions as rt,buildQuadrantRegions as st}from"../stats/regions.js";import{renderRegionYieldDiagram as le}from"./charts/regionYieldDiagram.js";const it=[{key:"overview",label:"Overview"},{key:"distributions",label:"Distributions"},{key:"correlation",label:"Correlation"}];export function createInsightsTab(D){const{getItems:de,getLotStats:ce,getBinColors:J,getRingCount:pe,onSaveImage:T,onSaveText:V,openWafer:H,focusTest:Z}=D,ue=D.showMetadataStrip??!0,R=D.contentInset??K,d=D.ownerDocument??document,I=d.createElement("div");Object.assign(I.style,{display:"none",flexDirection:"column",width:"100%",paddingTop:"0"});const fe=K,O=d.createElement("div");Object.assign(O.style,{paddingLeft:fe});const B=d.createElement("div");Object.assign(B.style,{display:"flex",gap:S.xs,alignItems:"center",background:k.menuBg,border:`1px solid ${k.menuBorder}`,borderRadius:Ge.container,boxShadow:He.panel,padding:`0 ${S.lg}`,marginBottom:S.lg,marginLeft:R,marginRight:R}),B.setAttribute("role","tablist"),B.addEventListener("keydown",e=>{if(e.key!=="ArrowLeft"&&e.key!=="ArrowRight")return;const n=Array.from(B.querySelectorAll('[role="tab"]')),o=n.indexOf(d.activeElement);if(o===-1)return;e.preventDefault();const c=n[(o+(e.key==="ArrowRight"?1:-1)+n.length)%n.length];c.focus(),c.click()});const W=d.createElement("div");Object.assign(W.style,{display:"flex",flexDirection:"column",gap:S.lg,marginTop:"0",paddingLeft:R,paddingRight:R}),I.appendChild(O),I.appendChild(B),I.appendChild(W);let j=D.defaultView??"overview",P,L=null,$=null,Y={includeLimits:void 0,clipOutliers:!1};const _=e=>{$!==e&&($=e,N())};let F=[];function U(e,n,o){H?.(e,n,o)}function me(e){return H?{open:(n,o)=>U(n,`Wafer ${e.find(c=>c.waferIndex===n)?.label??n}`,o)}:Z&&e.length===1?{open:(n,o)=>Z(o),label:"show this test on the map"}:null}function be(){return de().map((e,n)=>e==null?null:{metadata:e.wafer.metadata??void 0,dies:e.dies,wafer:e.wafer,label:Oe(e,n),waferIndex:n,passBins:Pe(e),statsSummary:e.statsSummary,testDefs:e.testDefs}).filter(e=>e!=null)}function ee(e,n){Object.assign(e.style,{background:"none",border:"none",borderBottom:n?`2px solid ${k.iconActive}`:"2px solid transparent",color:n?k.iconActive:k.label,fontWeight:n?"700":"500",fontSize:G.body,padding:`${S.sm} ${K} ${S.md}`,cursor:"pointer",marginBottom:"-1px"})}function ge(e,n){const o=d.createElement("button");o.type="button",o.textContent=n;const c=e===j;return ee(o,c),o.dataset.on=c?"true":"false",oe(o,"bare"),o.dataset.wmapInsightsTab=e,o.setAttribute("role","tab"),o.setAttribute("aria-selected",c?"true":"false"),o.tabIndex=c?0:-1,o.addEventListener("click",()=>{j!==e&&(j=e,N())}),o}function ye(e){const n=d.createElement("button");return n.type="button",n.textContent=`\u2039 ${e.label}`,ee(n,!1),n.dataset.wmapInsightsBack="1",n.addEventListener("click",e.onBack),n}function he(e,n,o,c=!0){const l=X(d),f=J();let y="label";const u=o??"group",m=ne(e.map(s=>s.passBins)),i=ce?.(),b=new Map(i?.lotYieldSeries.map(s=>[s.waferIndex,s.yieldPercent])),h=s=>({...s,label:s.label.replace(/\s*·\s*\d+(\.\d+)?%$/,""),yieldPercent:b.get(s.waferIndex),key:s.waferIndex}),g=e.map(h),p=n?.map(s=>({key:s.key,items:s.items.map(h)})),t=()=>p?se(p,A,y):M(g,A,y),x={title:n?`Yield by ${u} (pass: ${m})`:`Yield by wafer (pass: ${m})`,data:t(),selfControl:{current:y,options:[["yield","Sort: yield"],["label",n?`Sort: ${u}`:"Sort: wafer ID"]],onChange:s=>(y=s,{data:t()})},barColor:()=>Xe,valueLabel:s=>`${s.percent.toFixed(1)}%`,reference:s=>{if(s.length<3)return null;const v=s.map(Q=>Q.percent).sort((Q,ke)=>Q-ke),E=v.length%2?v[(v.length-1)/2]:(v[v.length/2-1]+v[v.length/2])/2;return{value:E,label:`median ${E.toFixed(1)}%`}},drill:p?{onOpenGroup:s=>{const v=p.find(E=>E.key===s.label)?.items??[];return{data:M(v,A,y),title:`Yield by wafer \u2014 ${u}: ${s.label} (pass: ${m})`}},onBack:()=>({data:t(),title:`Yield by ${u} (pass: ${m})`}),groupLabelText:u}:void 0,onOpen:H?s=>{typeof s.key=="number"&&U(s.key,`Wafer ${s.label}`)}:void 0,ownerDocument:d};let C=null;if(c&&(C=re(x,T),C.card.style.minHeight="360px",l.appendChild(C.card)),n){const s=Qe({title:"Hard bin pareto",groups:n.map(v=>({key:v.key,items:v.items.map(E=>({dies:E.dies,hardBinCounts:E.statsSummary?.stats.hardBinCounts,softBinCounts:E.statsSummary?.stats.softBinCounts}))})),onSaveImage:T,ownerDocument:d});return s.card.style.minHeight="360px",l.appendChild(s.card),{card:l,destroy:()=>{C?.destroy(),s.destroy()}}}let a="hbin";const r=s=>s===void 0?ae:(a==="hbin"?f.hard:f.soft).get(s)??ae,w=e.map(s=>({dies:s.dies,hardBinCounts:s.statsSummary?.stats.hardBinCounts,softBinCounts:s.statsSummary?.stats.softBinCounts})),te=()=>tt(w,a),Be={title:"Hard bin pareto",data:te(),selfControl:{current:a,options:[["hbin","Hard bins"],["sbin","Soft bins"]],onChange:s=>(a=s,{data:te(),title:`${a==="hbin"?"Hard":"Soft"} bin pareto`})},barColor:s=>r(s.binCode),ownerDocument:d},q=re(Be,T);return q.card.style.minHeight="360px",l.appendChild(q.card),{card:l,destroy:()=>{C?.destroy(),q.destroy()}}}function z(){const e=d.createElement("div");return e.dataset.wmapChartCard="1",Object.assign(e.style,{...Je(),display:"flex",flexDirection:"column",gap:S.lg,minWidth:"0"}),e}function Ce(e){if(O.innerHTML="",O.style.marginBottom="0",!ue)return;const n=ot(e.map(o=>({metadata:o.metadata})),void 0,{facetableOnly:!1});n&&(n.style.marginBottom="0",O.appendChild(n),O.style.marginBottom=S.xs)}function xe(e,n,o,c){if(!e.length)return{elements:[],testValuesCard:null,functionalCard:null,destroy:()=>{}};const l=e.map(r=>r.wafer),f=e.map(r=>r.dies),y=e.flatMap(r=>r.dies),u=e.map(r=>r.statsSummary),m=u.every(r=>r!==void 0)?u:void 0,i=pe?.()??4,b=[],h=[],p=Ke({groups:(c??[{key:"",items:e}]).map(r=>({key:r.key,items:r.items.map(w=>({dies:w.dies,testSpecYield:w.statsSummary?.stats.testSpecYield,functionalYield:w.statsSummary?.stats.functionalYield}))})),testDefs:o,onSaveImage:T,ownerDocument:d});p.card.style.minHeight="300px",b.push(p.card),h.push(p.destroy);const t=ie(f,l,i,r=>e[r].passBins,rt);if(t.length){const r=le({title:"Ring yield",mode:"ring",rows:t,onSaveImage:T,ownerDocument:d});b.push(r.card),h.push(r.destroy)}const x=ie(f,l,i,r=>e[r].passBins,st);if(x.length){const r=le({title:"Quadrant yield",mode:"quadrant",rows:x,onSaveImage:T,ownerDocument:d});b.push(r.card),h.push(r.destroy)}let C=null;if(n.length){const r=nt(y,n,void 0,m,V,f.map(w=>({dies:w})),void 0,"full");if(r){const w=z();w.appendChild(r),C=w}}let a=null;if(o.length){const r=at(y,o,m,V);if(r){const w=z();w.appendChild(r),a=w}}return{elements:b,testValuesCard:C,functionalCard:a,destroy:()=>{for(const r of h)r()}}}function Se(e){const n=ne(e.map(i=>i.passBins)),o=e.length===1,c=e[0],l=z();Object.assign(l.style,{flexDirection:"row",flexWrap:"wrap",gap:S.md});const f=i=>{const b=i.lastElementChild;return b&&Object.assign(b.style,{borderRight:"none",paddingRight:"0",marginRight:"0"}),i};function y(i,b,h){const g=d.createElement("div");Object.assign(g.style,{display:"flex",alignItems:"baseline",gap:S.sm,paddingRight:S.xxl,marginRight:S.md,borderRight:`1px solid ${k.menuBorder}`});const p=d.createElement("div");p.textContent=i,Object.assign(p.style,{fontSize:G.heading,fontWeight:"700",color:k.value,lineHeight:We.tight});const t=d.createElement("div");if(t.textContent=b,Object.assign(t.style,{fontSize:G.body,color:k.label}),g.append(p,t),h){const x=d.createElement("div");x.textContent=h,Object.assign(x.style,{fontSize:G.meta,color:k.label,opacity:je.muted}),g.appendChild(x)}return g}if(o){const i=M([{...c,key:c.waferIndex}],A)[0]?.percent;return i!==void 0&&l.appendChild(y(`${i.toFixed(1)}%`,`Yield \xB7 pass: ${n}`)),l.appendChild(y(String(c.dies.length),"Total dies")),f(l)}l.appendChild(y(String(e.length),"Wafers"));const u=M(e.map(i=>({...i,key:i.waferIndex})),A).map(i=>i.percent).filter(i=>Number.isFinite(i));if(u.length){const i=u.reduce((g,p)=>g+p,0)/u.length,b=se([{key:"all",items:e.map(g=>({...g,key:g.waferIndex}))}],A)[0]?.percent,h=b!==void 0&&b.toFixed(1)!==i.toFixed(1);l.appendChild(y(`${i.toFixed(1)}%`,`Mean per-wafer yield \xB7 pass: ${n}`,h?`${b.toFixed(1)}% by dies`:void 0))}const m=e.map(i=>i.statsSummary);if(m.every(i=>i!==void 0)){const i=m.reduce((h,g)=>h+g.stats.analyzedDies,0),b=m.reduce((h,g)=>h+g.stats.excludedDies,0);l.appendChild(y(i.toLocaleString(),"Dies analysed",b?`${b.toLocaleString()} excluded`:void 0))}else l.appendChild(y(e.reduce((i,b)=>i+b.dies.length,0).toLocaleString(),"Total dies"));return f(l)}function we(e,n,o,c,l){const f=d.createElement("div");Object.assign(f.style,{display:"flex",flexDirection:"column",gap:S.lg});const y=e.length===1&&!c;e.length&&!c&&f.appendChild(Se(e));const u=he(e,c,l,!y);f.appendChild(u.card);const m=xe(e,n,o,c);for(const i of m.elements)u.card.appendChild(i);return m.testValuesCard&&f.appendChild(m.testValuesCard),m.functionalCard&&f.appendChild(m.functionalCard),{card:f,destroy:()=>{u.destroy(),m.destroy()}}}function Te(e,n,o,c){const l=X(d),f=me(e),y=a=>{Y=a;for(const r of[g,p,t])r!==u&&r?.setAxisPrefs(a)};let u=null;const m=a=>r=>{u=a(),y(r),u=null},i=a=>({...a,testStats:a.statsSummary?.stats.perTestStats}),b=e.map(i),h=o?.map(a=>({key:a.key,items:a.items.map(i)})),g=Ve({title:"Test value distribution",axisPrefs:Y,onAxisPrefsChange:m(()=>g),onTestChange:a=>x(a),selectedTestNumber:L??void 0,items:b,testDefs:n,groups:h,groupLabelText:c,onSaveImage:T,onGroupChange:a=>_(a),onOpen:f?.open,openActionLabel:f?.label,ownerDocument:d}),p=Ue({title:"Value histogram",axisPrefs:Y,onAxisPrefsChange:m(()=>p),onTestChange:a=>x(a),selectedTestNumber:L??void 0,items:e,testDefs:n,groups:o,onGroupChange:a=>_(a),onSaveImage:T,ownerDocument:d}),t=_e({title:"Wafer-to-wafer trend",axisPrefs:Y,onAxisPrefsChange:m(()=>t),onTestChange:a=>x(a),selectedTestNumber:L??void 0,items:e.map(a=>({label:a.label,key:a.waferIndex,dies:a.dies,testStats:a.statsSummary?.stats.perTestStats})),testDefs:n,onSaveImage:T,centreLabel:Re(Ae(e.map(a=>a.wafer.metadata)),"mean"),onOpen:H?(a,r)=>U(a,`Wafer ${e.find(w=>w.waferIndex===a)?.label??a}`,r):void 0,ownerDocument:d}),x=a=>{L=a,g.setTest(a),p.setTest(a),t.setTest(a),C.setTest(a)},C=Me({title:"Process capability",items:e,testDefs:n,onSaveImage:T,selectedTestNumber:L??void 0,onSelectTest:a=>x(a),ownerDocument:d});return l.append(C.card,g.card,p.card,t.card),{card:l,destroy:()=>{C.destroy(),g.destroy(),p.destroy(),t.destroy()}}}function ve(e,n,o){const c=X(d),l=qe({title:"Test scatter",items:e,testDefs:n,groups:o,binColors:J().hard,onSaveImage:T,ownerDocument:d}),f=ze({title:"Test correlation matrix",items:e,testDefs:n,onSaveImage:T,onSaveText:V,onSelectPair:(y,u)=>l.setXY(y,u),ownerDocument:d});return c.append(f.card,l.card),{card:c,destroy:()=>{f.destroy(),l.destroy()}}}function N(){for(const t of F)t.destroy();F=[],W.innerHTML="",B.innerHTML="",D.backTab&&B.appendChild(ye(D.backTab));for(const t of it)B.appendChild(ge(t.key,t.label));const e=be();Ce(e);const n=De(e,{facetableOnly:!0}).filter(t=>t.splittable),o=d.createElement("div");Object.assign(o.style,{display:"flex",gap:S.md,alignItems:"center",marginLeft:S.xxl,paddingBottom:S.xs});let c;n.length>0?(o.appendChild(Ze("Group by:",[{value:"",label:"None"},...n.map(t=>({value:t.key,label:`${t.label} (${t.values.length})`}))],P??"",t=>{P=t||void 0,$=null,N()},{hook:"group-by",ownerDocument:d})),c=n.find(t=>t.key===P)?.label):P=void 0;let l;if(P){const t=new Map,x=[];for(const C of e){const a=$e(C.metadata,P)??Le;t.has(a)||(t.set(a,[]),x.push(a)),t.get(a).push(C)}l=x.map(C=>({key:C,items:t.get(C)}))}$!==null&&!l?.some(t=>t.key===$)&&($=null),l&&l.length>0&&o.appendChild(et(l.map(t=>t.key),"Show:",$,t=>_(t),{ownerDocument:d}).el);let f=null;if(D.onOpenGuide){const t=d.createElement("button");t.type="button",t.innerHTML=Ne.help,t.setAttribute("aria-label","User guide"),Object.assign(t.style,{...Ye("bare"),display:"inline-flex",alignItems:"center",justifyContent:"center",width:"24px",height:"24px",flexShrink:"0"}),oe(t,"bare"),Fe(t,"User guide"),t.addEventListener("click",()=>D.onOpenGuide()),Object.assign(t.style,{marginLeft:"auto"}),f=t}B.appendChild(o),f&&B.appendChild(f);const y=$!==null?l?.find(t=>t.key===$):void 0,u=y?.items??e,m=y?void 0:l,i=Ee(u),b=i.defs,h=b.filter(Ie);L!==null&&!h.some(t=>t.testNumber===L)&&(L=null);const g=j==="overview"?we(u,h,b,m,c):j==="distributions"?Te(u,h,m,c):ve(u,h,m),p=i.conflicts.filter(t=>t.excluded);if(p.length>0){const t=d.createElement("div");Object.assign(t.style,{color:k.label,fontSize:G.body,padding:`0 ${R} ${S.sm}`});const x=p.slice(0,6).map(r=>r.testNumber).join(", "),C=p.length>6?`, and ${p.length-6} more`:"",a=p.length===1?"it":"them";t.textContent=`${p.length} test ${p.length===1?"number is":"numbers are"} hidden (${x}${C}) \u2014 the wafers in view describe ${a} differently, so their values cannot be pooled. `+(l&&l.length>0?`Pick one ${c??"group"} under "Show:" to analyse ${a}.`:"Load one test program at a time to analyse them."),t.dataset.wmapWithheldTests=String(p.length),W.appendChild(t)}F.push(g),W.appendChild(g.card)}return{el:I,render:N,destroy:()=>{for(const e of F)e.destroy();I.remove()}}}
1
+ import{buildFacetTable as $e,facetValueOf as Le,FACET_NONE_VALUE as Ee}from"../stats/facets.js";import{mergeTestDefs as Ie}from"../stats/mergeTestDefs.js";import{isParametricTest as Oe}from"../renderer/buildWaferMap.js";import{waferDisplayLabel as Pe}from"../core/waferLabel.js";import{INPUT_DEFAULT_PASS_BINS as R,itemPassBins as Ae,passBinsLabel as ae}from"../core/passBins.js";import{NO_DATA_FILL as oe}from"../renderer/colorMap.js";import{describeWaferPopulation as Re,populationStat as We}from"../stats/population.js";import{LEADING as je,ALPHA as Ge,SPACE as w,EDGE_GUTTER as X,FONT as G,CLR as B,RADIUS as Ye,SHADOW as He,controlStyle as Fe,wireControlHover as re,wireTooltip as Me}from"./toolbar.js";import{ICONS as Ne}from"./icons.js";import{renderCapabilityPanel as Ve}from"./charts/capability.js";import{renderBoxplotPanel as _e}from"./charts/boxplot.js";import{renderTrendPanel as Ue}from"./charts/trend.js";import{renderHistogramPanel as ze}from"./charts/histogram.js";import{renderCorrelationPanel as qe}from"./charts/correlation.js";import{renderScatterPanel as Qe}from"./charts/scatter.js";import{renderBarPanel as se}from"./charts/barPanel.js";import{renderBinClusterPanel as Ke}from"./charts/binCluster.js";import{renderTestPassRatePanel as Xe}from"./charts/testPassRate.js";import{QUANTITY as Je}from"./charts/palette.js";import{cardFrameStyle as Ze,makeChartGridWrap as J,makeLabeledSelect as et,makeLinkedGroupSelect as tt}from"./charts/chartShell.js";import{buildYieldData as V,buildYieldDataCombined as ie}from"../stats/yield.js";import{buildBinParetoData as nt}from"../stats/binPareto.js";import{buildLotTestSection as at,buildLotFunctionalSection as ot,buildMetadataStripBox as rt}from"./summaryPanel.js";import{buildRegionYieldData as le,buildRingRegions as st,buildQuadrantRegions as it}from"../stats/regions.js";import{renderRegionYieldDiagram as de}from"./charts/regionYieldDiagram.js";const lt=[{key:"overview",label:"Overview"},{key:"distributions",label:"Distributions"},{key:"correlation",label:"Correlation"}];export function createInsightsTab(D){const{getItems:ce,getLotStats:pe,getBinColors:Z,getRingCount:ue,onSaveImage:T,onSaveText:_,openWafer:Y,focusTest:ee}=D,fe=D.showMetadataStrip??!0,W=D.contentInset??X,l=D.ownerDocument??document,O=l.createElement("div");Object.assign(O.style,{display:"none",flexDirection:"column",width:"100%",paddingTop:"0"});const me=X,P=l.createElement("div");Object.assign(P.style,{paddingLeft:me});const k=l.createElement("div");Object.assign(k.style,{display:"flex",gap:w.xs,alignItems:"center",background:B.menuBg,border:`1px solid ${B.menuBorder}`,borderRadius:Ye.container,boxShadow:He.panel,padding:`0 ${w.lg}`,marginBottom:w.lg,marginLeft:W,marginRight:W}),k.setAttribute("role","tablist"),k.addEventListener("keydown",e=>{if(e.key!=="ArrowLeft"&&e.key!=="ArrowRight")return;const t=Array.from(k.querySelectorAll('[role="tab"]')),r=t.indexOf(l.activeElement);if(r===-1)return;e.preventDefault();const d=t[(r+(e.key==="ArrowRight"?1:-1)+t.length)%t.length];d.focus(),d.click()});const j=l.createElement("div");Object.assign(j.style,{display:"flex",flexDirection:"column",gap:w.lg,marginTop:"0",paddingLeft:W,paddingRight:W}),O.appendChild(P),O.appendChild(k),O.appendChild(j);let I=D.defaultView??"overview",A,L=null,$=null,H={includeLimits:void 0,clipOutliers:!1};const U=e=>{$!==e&&($=e,M())};let F=new Map;function te(){for(const e of F.values())e.destroy();F=new Map}function z(e,t,r){Y?.(e,t,r)}function be(e){return Y?{open:(t,r)=>z(t,`Wafer ${e.find(d=>d.waferIndex===t)?.label??t}`,r)}:ee&&e.length===1?{open:(t,r)=>ee(r),label:"show this test on the map"}:null}function ge(){return ce().map((e,t)=>e==null?null:{metadata:e.wafer.metadata??void 0,dies:e.dies,wafer:e.wafer,label:Pe(e,t),waferIndex:t,passBins:Ae(e),statsSummary:e.statsSummary,testDefs:e.testDefs}).filter(e=>e!=null)}function ne(e,t){Object.assign(e.style,{background:"none",border:"none",borderBottom:t?`2px solid ${B.iconActive}`:"2px solid transparent",color:t?B.iconActive:B.label,fontWeight:t?"700":"500",fontSize:G.body,padding:`${w.sm} ${X} ${w.md}`,cursor:"pointer",marginBottom:"-1px"})}function ye(e,t){const r=l.createElement("button");r.type="button",r.textContent=t;const d=e===I;return ne(r,d),r.dataset.on=d?"true":"false",re(r,"bare"),r.dataset.wmapInsightsTab=e,r.setAttribute("role","tab"),r.setAttribute("aria-selected",d?"true":"false"),r.tabIndex=d?0:-1,r.addEventListener("click",()=>{I!==e&&(I=e,M({keepSections:!0}))}),r}function he(e){const t=l.createElement("button");return t.type="button",t.textContent=`\u2039 ${e.label}`,ne(t,!1),t.dataset.wmapInsightsBack="1",t.addEventListener("click",e.onBack),t}function Ce(e,t,r,d=!0){const c=J(l),p=Z();let m="label";const b=r??"group",u=ae(e.map(i=>i.passBins)),s=pe?.(),g=new Map(s?.lotYieldSeries.map(i=>[i.waferIndex,i.yieldPercent])),S=i=>({...i,label:i.label.replace(/\s*·\s*\d+(\.\d+)?%$/,""),yieldPercent:g.get(i.waferIndex),key:i.waferIndex}),f=e.map(S),y=t?.map(i=>({key:i.key,items:i.items.map(S)})),x=()=>y?ie(y,R,m):V(f,R,m),C={title:t?`Yield by ${b} (pass: ${u})`:`Yield by wafer (pass: ${u})`,data:x(),selfControl:{current:m,options:[["yield","Sort: yield"],["label",t?`Sort: ${b}`:"Sort: wafer ID"]],onChange:i=>(m=i,{data:x()})},barColor:()=>Je,valueLabel:i=>`${i.percent.toFixed(1)}%`,reference:i=>{if(i.length<3)return null;const v=i.map(K=>K.percent).sort((K,De)=>K-De),E=v.length%2?v[(v.length-1)/2]:(v[v.length/2-1]+v[v.length/2])/2;return{value:E,label:`median ${E.toFixed(1)}%`}},drill:y?{onOpenGroup:i=>{const v=y.find(E=>E.key===i.label)?.items??[];return{data:V(v,R,m),title:`Yield by wafer \u2014 ${b}: ${i.label} (pass: ${u})`}},onBack:()=>({data:x(),title:`Yield by ${b} (pass: ${u})`}),groupLabelText:b}:void 0,onOpen:Y?i=>{typeof i.key=="number"&&z(i.key,`Wafer ${i.label}`)}:void 0,ownerDocument:l};let n=null;if(d&&(n=se(C,T),n.card.style.minHeight="360px",c.appendChild(n.card)),t){const i=Ke({title:"Hard bin pareto",groups:t.map(v=>({key:v.key,items:v.items.map(E=>({dies:E.dies,hardBinCounts:E.statsSummary?.stats.hardBinCounts,softBinCounts:E.statsSummary?.stats.softBinCounts}))})),onSaveImage:T,ownerDocument:l});return i.card.style.minHeight="360px",c.appendChild(i.card),{card:c,destroy:()=>{n?.destroy(),i.destroy()}}}let a="hbin";const o=i=>i===void 0?oe:(a==="hbin"?p.hard:p.soft).get(i)??oe,h=e.map(i=>({dies:i.dies,hardBinCounts:i.statsSummary?.stats.hardBinCounts,softBinCounts:i.statsSummary?.stats.softBinCounts})),N=()=>nt(h,a),Be={title:"Hard bin pareto",data:N(),selfControl:{current:a,options:[["hbin","Hard bins"],["sbin","Soft bins"]],onChange:i=>(a=i,{data:N(),title:`${a==="hbin"?"Hard":"Soft"} bin pareto`})},barColor:i=>o(i.binCode),ownerDocument:l},Q=se(Be,T);return Q.card.style.minHeight="360px",c.appendChild(Q.card),{card:c,destroy:()=>{n?.destroy(),Q.destroy()}}}function q(){const e=l.createElement("div");return e.dataset.wmapChartCard="1",Object.assign(e.style,{...Ze(),display:"flex",flexDirection:"column",gap:w.lg,minWidth:"0"}),e}function Se(e){if(P.innerHTML="",P.style.marginBottom="0",!fe)return;const t=rt(e.map(r=>({metadata:r.metadata})),void 0,{facetableOnly:!1});t&&(t.style.marginBottom="0",P.appendChild(t),P.style.marginBottom=w.xs)}function xe(e,t,r,d){if(!e.length)return{elements:[],testValuesCard:null,functionalCard:null,destroy:()=>{}};const c=e.map(o=>o.wafer),p=e.map(o=>o.dies),m=e.flatMap(o=>o.dies),b=e.map(o=>o.statsSummary),u=b.every(o=>o!==void 0)?b:void 0,s=ue?.()??4,g=[],S=[],y=Xe({groups:(d??[{key:"",items:e}]).map(o=>({key:o.key,items:o.items.map(h=>({dies:h.dies,testSpecYield:h.statsSummary?.stats.testSpecYield,functionalYield:h.statsSummary?.stats.functionalYield}))})),testDefs:r,onSaveImage:T,ownerDocument:l});y.card.style.minHeight="300px",g.push(y.card),S.push(y.destroy);const x=le(p,c,s,o=>e[o].passBins,st);if(x.length){const o=de({title:"Ring yield",mode:"ring",rows:x,onSaveImage:T,ownerDocument:l});g.push(o.card),S.push(o.destroy)}const C=le(p,c,s,o=>e[o].passBins,it);if(C.length){const o=de({title:"Quadrant yield",mode:"quadrant",rows:C,onSaveImage:T,ownerDocument:l});g.push(o.card),S.push(o.destroy)}let n=null;if(t.length){const o=at(m,t,void 0,u,_,p.map(h=>({dies:h})),void 0,"full");if(o){const h=q();h.appendChild(o),n=h}}let a=null;if(r.length){const o=ot(m,r,u,_);if(o){const h=q();h.appendChild(o),a=h}}return{elements:g,testValuesCard:n,functionalCard:a,destroy:()=>{for(const o of S)o()}}}function we(e){const t=ae(e.map(s=>s.passBins)),r=e.length===1,d=e[0],c=q();Object.assign(c.style,{flexDirection:"row",flexWrap:"wrap",gap:w.md});const p=s=>{const g=s.lastElementChild;return g&&Object.assign(g.style,{borderRight:"none",paddingRight:"0",marginRight:"0"}),s};function m(s,g,S){const f=l.createElement("div");Object.assign(f.style,{display:"flex",alignItems:"baseline",gap:w.sm,paddingRight:w.xxl,marginRight:w.md,borderRight:`1px solid ${B.menuBorder}`});const y=l.createElement("div");y.textContent=s,Object.assign(y.style,{fontSize:G.heading,fontWeight:"700",color:B.value,lineHeight:je.tight});const x=l.createElement("div");if(x.textContent=g,Object.assign(x.style,{fontSize:G.body,color:B.label}),f.append(y,x),S){const C=l.createElement("div");C.textContent=S,Object.assign(C.style,{fontSize:G.meta,color:B.label,opacity:Ge.muted}),f.appendChild(C)}return f}if(r){const s=V([{...d,key:d.waferIndex}],R)[0]?.percent;return s!==void 0&&c.appendChild(m(`${s.toFixed(1)}%`,`Yield \xB7 pass: ${t}`)),c.appendChild(m(String(d.dies.length),"Total dies")),p(c)}c.appendChild(m(String(e.length),"Wafers"));const b=V(e.map(s=>({...s,key:s.waferIndex})),R).map(s=>s.percent).filter(s=>Number.isFinite(s));if(b.length){const s=b.reduce((f,y)=>f+y,0)/b.length,g=ie([{key:"all",items:e.map(f=>({...f,key:f.waferIndex}))}],R)[0]?.percent,S=g!==void 0&&g.toFixed(1)!==s.toFixed(1);c.appendChild(m(`${s.toFixed(1)}%`,`Mean per-wafer yield \xB7 pass: ${t}`,S?`${g.toFixed(1)}% by dies`:void 0))}const u=e.map(s=>s.statsSummary);if(u.every(s=>s!==void 0)){const s=u.reduce((S,f)=>S+f.stats.analyzedDies,0),g=u.reduce((S,f)=>S+f.stats.excludedDies,0);c.appendChild(m(s.toLocaleString(),"Dies analysed",g?`${g.toLocaleString()} excluded`:void 0))}else c.appendChild(m(e.reduce((s,g)=>s+g.dies.length,0).toLocaleString(),"Total dies"));return p(c)}function Te(e,t,r,d,c){const p=l.createElement("div");Object.assign(p.style,{display:"flex",flexDirection:"column",gap:w.lg});const m=e.length===1&&!d;e.length&&!d&&p.appendChild(we(e));const b=Ce(e,d,c,!m);p.appendChild(b.card);const u=xe(e,t,r,d);for(const s of u.elements)b.card.appendChild(s);return u.testValuesCard&&p.appendChild(u.testValuesCard),u.functionalCard&&p.appendChild(u.functionalCard),{card:p,destroy:()=>{b.destroy(),u.destroy()}}}function ve(e,t,r,d){const c=J(l),p=be(e),m=a=>{H=a;for(const o of[f,y,x])o!==b&&o?.setAxisPrefs(a)};let b=null;const u=a=>o=>{b=a(),m(o),b=null},s=a=>({...a,testStats:a.statsSummary?.stats.perTestStats}),g=e.map(s),S=r?.map(a=>({key:a.key,items:a.items.map(s)})),f=_e({title:"Test value distribution",axisPrefs:H,onAxisPrefsChange:u(()=>f),onTestChange:a=>C(a),selectedTestNumber:L??void 0,items:g,testDefs:t,groups:S,groupLabelText:d,onSaveImage:T,onGroupChange:a=>U(a),onOpen:p?.open,openActionLabel:p?.label,ownerDocument:l}),y=ze({title:"Value histogram",axisPrefs:H,onAxisPrefsChange:u(()=>y),onTestChange:a=>C(a),selectedTestNumber:L??void 0,items:e,testDefs:t,groups:r,onGroupChange:a=>U(a),onSaveImage:T,ownerDocument:l}),x=Ue({title:"Wafer-to-wafer trend",axisPrefs:H,onAxisPrefsChange:u(()=>x),onTestChange:a=>C(a),selectedTestNumber:L??void 0,items:e.map(a=>({label:a.label,key:a.waferIndex,dies:a.dies,testStats:a.statsSummary?.stats.perTestStats})),testDefs:t,onSaveImage:T,centreLabel:We(Re(e.map(a=>a.wafer.metadata)),"mean"),onOpen:Y?(a,o)=>z(a,`Wafer ${e.find(h=>h.waferIndex===a)?.label??a}`,o):void 0,ownerDocument:l}),C=a=>{L=a,f.setTest(a),y.setTest(a),x.setTest(a),n.setTest(a)},n=Ve({title:"Process capability",items:e,testDefs:t,onSaveImage:T,selectedTestNumber:L??void 0,onSelectTest:a=>C(a),ownerDocument:l});return c.append(n.card,f.card,y.card,x.card),{card:c,destroy:()=>{n.destroy(),f.destroy(),y.destroy(),x.destroy()}}}function ke(e,t,r){const d=J(l),c=Qe({title:"Test scatter",items:e,testDefs:t,groups:r,binColors:Z().hard,onSaveImage:T,ownerDocument:l}),p=qe({title:"Test correlation matrix",items:e,testDefs:t,onSaveImage:T,onSaveText:_,onSelectPair:(m,b)=>c.setXY(m,b),ownerDocument:l});return d.append(p.card,c.card),{card:d,destroy:()=>{p.destroy(),c.destroy()}}}function M(e){e?.keepSections||te(),j.innerHTML="",k.innerHTML="",D.backTab&&k.appendChild(he(D.backTab));for(const n of lt)k.appendChild(ye(n.key,n.label));const t=ge();Se(t);const r=$e(t,{facetableOnly:!0}).filter(n=>n.splittable),d=l.createElement("div");Object.assign(d.style,{display:"flex",gap:w.md,alignItems:"center",marginLeft:w.xxl,paddingBottom:w.xs});let c;r.length>0?(d.appendChild(et("Group by:",[{value:"",label:"None"},...r.map(n=>({value:n.key,label:`${n.label} (${n.values.length})`}))],A??"",n=>{A=n||void 0,$=null,M()},{hook:"group-by",ownerDocument:l})),c=r.find(n=>n.key===A)?.label):A=void 0;let p;if(A){const n=new Map,a=[];for(const o of t){const h=Le(o.metadata,A)??Ee;n.has(h)||(n.set(h,[]),a.push(h)),n.get(h).push(o)}p=a.map(o=>({key:o,items:n.get(o)}))}$!==null&&!p?.some(n=>n.key===$)&&($=null),p&&p.length>0&&d.appendChild(tt(p.map(n=>n.key),"Show:",$,n=>U(n),{ownerDocument:l}).el);let m=null;if(D.onOpenGuide){const n=l.createElement("button");n.type="button",n.innerHTML=Ne.help,n.setAttribute("aria-label","User guide"),Object.assign(n.style,{...Fe("bare"),display:"inline-flex",alignItems:"center",justifyContent:"center",width:"24px",height:"24px",flexShrink:"0"}),re(n,"bare"),Me(n,"User guide"),n.addEventListener("click",()=>D.onOpenGuide()),Object.assign(n.style,{marginLeft:"auto"}),m=n}k.appendChild(d),m&&k.appendChild(m);const b=$!==null?p?.find(n=>n.key===$):void 0,u=b?.items??t,s=b?void 0:p,g=Ie(u),S=g.defs,f=S.filter(Oe);L!==null&&!f.some(n=>n.testNumber===L)&&(L=null);const y=e?.keepSections?F.get(I):void 0,x=y??(I==="overview"?Te(u,f,S,s,c):I==="distributions"?ve(u,f,s,c):ke(u,f,s));y||F.set(I,x);const C=g.conflicts.filter(n=>n.excluded);if(C.length>0){const n=l.createElement("div");Object.assign(n.style,{color:B.label,fontSize:G.body,padding:`0 ${W} ${w.sm}`});const a=C.slice(0,6).map(N=>N.testNumber).join(", "),o=C.length>6?`, and ${C.length-6} more`:"",h=C.length===1?"it":"them";n.textContent=`${C.length} test ${C.length===1?"number is":"numbers are"} hidden (${a}${o}) \u2014 the wafers in view describe ${h} differently, so their values cannot be pooled. `+(p&&p.length>0?`Pick one ${c??"group"} under "Show:" to analyse ${h}.`:"Load one test program at a time to analyse them."),n.dataset.wmapWithheldTests=String(C.length),j.appendChild(n)}j.appendChild(x.card)}return{el:O,render:M,destroy:()=>{te(),O.remove()}}}
@@ -1,3 +1,4 @@
1
+ import { type BinColors } from '../renderer/binColors.js';
1
2
  import { type SaveImageHandler, type SaveTextHandler, type UserGuideExtension } from './toolbar.js';
2
3
  import { type Die } from '../core/dies.js';
3
4
  import type { WaferViewOptions } from './renderWaferMap.js';
@@ -40,6 +41,61 @@ export interface GalleryOptions {
40
41
  viewOptions?: WaferViewOptions;
41
42
  /** Called whenever a shared gallery option changes. */
42
43
  onViewOptionsChange?: (opts: WaferViewOptions, changed: (keyof WaferViewOptions)[], category: 'preference' | 'state' | 'mixed') => void;
44
+ /**
45
+ * Called once the gallery has finished building: every factory resolved, and
46
+ * the lot-wide surfaces (legend, shared colours, summary panel) settled.
47
+ *
48
+ * Exists so a host can hold its own progress indicator over a large lot's
49
+ * load without polling the gallery's DOM. **Always fires, and always
50
+ * asynchronously, whichever form the items took** — a host passing factories
51
+ * and a host passing pre-built items get the same one signal, so neither has
52
+ * to know which path the gallery took. With factories it fires after the last
53
+ * card; with pre-built items, on the next task after mount (never before
54
+ * `renderWaferGallery` returns, so the controller is always in hand).
55
+ *
56
+ * **It waits for the Summary panel's own render to finish**, which on a large
57
+ * lot is seconds after the last card appears — the panel is staged across
58
+ * tasks, and on 50 wafers x 8,000 dies x 50 tests in Chrome the cards are in
59
+ * at 3.6 s and the panel at 8.2 s. That is the point of it: a host clearing
60
+ * its indicator when the cards land would leave the rest of the wait
61
+ * unexplained, which is the failure this signal exists to prevent. There is
62
+ * deliberately no earlier "cards are in" variant — one settle signal, one
63
+ * meaning.
64
+ *
65
+ * Fires once per build, so a rebuild fires it again — `setItems`, and the
66
+ * internal rebuild that switching into a stacked mode does. It says "the
67
+ * gallery is settled", not "this happened for the first time".
68
+ */
69
+ onItemsResolved?: () => void;
70
+ /**
71
+ * Called as each card is built, with how many of the expected items now
72
+ * exist and how many there will be. `total` is the count `setItems` was
73
+ * given, so it is correct from the first call and a progress bar can be
74
+ * sized before anything arrives.
75
+ *
76
+ * This is the *advance* signal; `onItemsResolved` is the *settled* one, and
77
+ * they are not interchangeable. A progressive mount of a large lot runs for
78
+ * 10-25 s, and a host indicator that can only say "loading" for that long
79
+ * reads as a hang — which is the failure the progressive path exists to
80
+ * prevent, reintroduced one layer up.
81
+ *
82
+ * **Fires on the pre-built path too**, so no host has to branch on which
83
+ * form it passed: a fully pre-built mount is one call with
84
+ * `resolved === total`. A mixed set reports its pre-built items in one call
85
+ * and then one call per factory.
86
+ *
87
+ * It covers the cards only. On a large lot the Summary panel keeps filling
88
+ * in for seconds after the last card, with no card activity to report —
89
+ * `onItemsResolved` is what marks the end of *that*. There is deliberately
90
+ * no third callback between them: if a host needs to name that phase, it
91
+ * knows it has begun when `resolved === total` and ended when
92
+ * `onItemsResolved` fires.
93
+ *
94
+ * Deferred a task and generation-guarded like every callback here, so a
95
+ * gallery rebuilt or destroyed before the turn arrives stays quiet. Not
96
+ * called at all for an empty item list.
97
+ */
98
+ onItemResolved?: (resolved: number, total: number) => void;
43
99
  /**
44
100
  * Draw a legend on every card as well as the lot-level one. Default false.
45
101
  *
@@ -53,14 +109,22 @@ export interface GalleryOptions {
53
109
  * disabled with that reason rather than appearing to do nothing.
54
110
  */
55
111
  perCardLegend?: boolean;
56
- /** Filename stem for the composite gallery PNG. Default 'wafer-gallery'. */
112
+ /**
113
+ * Name for the composite gallery PNG, without extension. When omitted, it is
114
+ * named for its lots, wafer count and plot mode
115
+ * (`LOT123_25-wafers_gallery-hard-bin.png`); cards, CSV exports and charts are
116
+ * always named that way. See docs/api.md §5.4.5.
117
+ *
118
+ * **Changes in 0.31.0:** this becomes a prefix for every file the gallery and
119
+ * its cards save. Passing it logs a one-time notice.
120
+ */
57
121
  downloadFilename?: string;
58
122
  /**
59
123
  * Host hook for persisting the composite gallery PNG. When provided, the save
60
124
  * action calls `onSaveImage(blob, suggestedName)` instead of triggering a
61
125
  * browser `<a download>` — letting embedded hosts (Tauri, Electron, WebView2)
62
126
  * route the image through a native save dialog. `suggestedName` includes the
63
- * `.png` extension and is derived from `downloadFilename`. When omitted, the
127
+ * `.png` extension and is the generated name (see `downloadFilename`). When omitted, the
64
128
  * default browser download behaviour is unchanged.
65
129
  */
66
130
  onSaveImage?: SaveImageHandler;
@@ -104,7 +168,8 @@ export interface GalleryOptions {
104
168
  dieList?: DieListDisplayOptions;
105
169
  /**
106
170
  * Fix the number of columns in the gallery grid. When set, overrides the
107
- * auto-computed minimum card width and the toolbar columns control.
171
+ * auto-computed layout and the toolbar columns control: the columns share the
172
+ * full grid width, with no density-derived card size cap.
108
173
  * Omit (default) to let the gallery auto-size cards based on die pitch.
109
174
  */
110
175
  columns?: number;
@@ -160,6 +225,18 @@ export interface GalleryController {
160
225
  * `undefined` to clear it once the offered analysis has been run.
161
226
  */
162
227
  setFindingsNotice(notice: FindingsNotice | undefined): void;
228
+ /**
229
+ * Set the number of grid columns, as the toolbar's Columns control does: the
230
+ * columns share the full grid width. Pass `undefined` to return to the
231
+ * automatic layout, which sizes cards by die pitch.
232
+ */
233
+ setColumns(columns: number | undefined): void;
234
+ /**
235
+ * The bin colours every card draws, resolved once over the whole gallery with
236
+ * the palette currently chosen — for a host surface of its own (a table
237
+ * swatch, an export). Hard and soft bins are separate maps.
238
+ */
239
+ getBinColors(): BinColors;
163
240
  /**
164
241
  * Opens the built-in end-user guide window — the same action the help
165
242
  * toolbar button performs, but callable directly. Works regardless of
@@ -1 +1 @@
1
- import{getUniqueTestNumbers as go,resolveTestNumber as mo,findTestDef as ho,collectMetadataValues as bo}from"../renderer/buildView.js";import{metadataCategoricalValue as yo}from"../core/metadata.js";import{resolveBinColorsByWafer as wo,binColorWarning as vo,mixedPassBinsWarning as xo}from"../renderer/binColors.js";import{itemPassBins as Co}from"../core/passBins.js";import{NO_DATA_FILL as So}from"../renderer/colorMap.js";import{metadataValueColor as Mo}from"../renderer/colorMap.js";import{resolveCanvasTheme as Do}from"./canvasTheme.js";import{ICONS as $e}from"./icons.js";import{SHADOW as We,LEADING as Xe,TRACKING as Bo,controlStyle as ko,wireControlHover as ht,SPACE as v,EDGE_GUTTER as oe,MAP_CHROME_INSET as Eo,RADIUS as z,FONT as O,CLR as m,sevColor as ln,MODE_LABELS as Oo,BIN_LEGEND_MODES as rn,STACKED_MODES as be,Z_ABOVE as dn,applyOverlayZ as To,getTooltip as Lo,hideTooltip as cn,createToolbarHelpers as $o,buildModeMenuEl as Wo,openDetachWindow as Ro,openFloatingWindow as Io,openModal as Ao,openReportModal as Po,copyWmapThemeTokens as Ho,syncWmapPopupTheme as Fo,openUserGuideWindow as jo,makePaletteBtn as Vo,makeLogScaleBtn as zo,makeLegendStyleBtn as No,makeOverlaysBtn as _o,makeOrientationBtn as Go,menuLayerFor as fn,saveImageBlob as Ko,markMenuTrigger as Re,wireMenuA11y as un,wireExpandToggle as pn,wireTooltip as gn,requestedPassFailDisplay as mn,overlayMenuRows as qo,anyOverlayActive as Uo,logWmapVersionOnce as Xo,buildDataModeEntries as Zo,metadataKeyHasData as Yo,metadataModeEntry as Qo}from"./toolbar.js";import{waferDisplayLabel as Ie}from"../core/waferLabel.js";import{sortBinsForDisplay as Jo}from"../stats/binPareto.js";import{diePassStatus as ei}from"../core/dies.js";import{aggregateValues as ti,aggregateBinCounts as hn}from"../core/aggregates.js";import{renderWaferMap as ni,renderWaferMapCard as bn,toPublicViewOptions as yn}from"./renderWaferMap.js";import{classifyChanged as oi,COLOR_KEYS as ii}from"./renderWaferMap.js";import{buildWaferMap as ai,getTestPassStatus as wn,isParametricTest as bt,getDieTestValue as si}from"../renderer/buildWaferMap.js";import{analyzeWaferMap as Ze}from"../stats/analyzeWaferMap.js";import{collectWarnings as li,buildWarningsMenuEl as ri,severityOf as di}from"./warnings.js";import{compareNatural as vn}from"../core/utils.js";import{createSummaryPanelEl as yt,buildMetadataStripRow as ci,buildCompactMetadataRows as xn,metadataEntries as Cn,renderLotSummaryContent as fi}from"./summaryPanel.js";import{renderLotSummaryReportHtml as ui}from"../stats/renderSummaryReport.js";import{prettyKey as pi}from"../stats/facets.js";import{mergeTestDefs as gi}from"../stats/mergeTestDefs.js";import{getDieKey as mi,hasPosition as hi}from"../core/dies.js";function ie(u){const x=new Set;return u.filter(p=>x.has(p.bin)?!1:(x.add(p.bin),!0))}const wt=12;function bi(u,x,p){let I=x;if(x.length>wt){const C=x.slice(0,wt-1),L=x.slice(wt-1),N=L.reduce((F,ae)=>F+ae.count,0);I=[...C,{color:m.label,count:N,label:`Other (${L.length} bins)`}]}const A=u.createElement("div");Object.assign(A.style,{display:"flex",columnGap:"1px",width:"100%",height:"8px",borderRadius:z.control,overflow:"hidden",border:`1px solid ${m.menuBorder}`,boxSizing:"border-box",marginTop:v.xs,flexShrink:"0"}),A.setAttribute("role","img"),A.setAttribute("aria-label",`Population split: ${I.map(C=>`${C.label} ${Ye(C.count/p*100)}`).join(", ")}`);for(const C of I){const L=C.count/p*100,N=u.createElement("div");Object.assign(N.style,{flex:`${L} 1 0`,minWidth:L>0?"2px":"0",background:C.color}),gn(N,`${C.label} \xB7 ${C.count.toLocaleString()} dies \xB7 ${Ye(L)}`),A.appendChild(N)}return A}function Ye(u){return u>0&&u<.1?"<0.1%":`${u.toFixed(1)}%`}function Sn(u,x,p){const I=new Map;let A=0,C=0;for(const L of u){const N=p?new Set(p(L)):void 0;for(const F of L.dies){if(F.partial||F.edgeExcluded)continue;const ae=x(F);ae!==void 0&&(I.set(ae,(I.get(ae)??0)+1),A++,N&&ei(F,N)&&C++)}}return{counts:I,total:A,pass:C}}function Mn(u,x){const p=u.ownerDocument.createElement("div");Object.assign(p.style,{display:"flex",alignItems:"center",gap:"5px",cursor:"pointer",userSelect:"none",padding:`${v.xxs} ${v.xs}`,borderRadius:z.control});const I=u.ownerDocument.createElement("span");Object.assign(I.style,{display:"inline-block",width:"13px",height:"13px",flexShrink:"0",background:x.color,border:x.isActive?`2px solid ${m.iconActive}`:`1px solid ${m.menuBorder}`,borderRadius:z.control,boxSizing:"border-box"});const A=u.ownerDocument.createElement("span");if(A.textContent=x.label,Object.assign(A.style,{fontWeight:x.isActive?"700":"400",color:x.isActive?m.iconActive:m.text,whiteSpace:"nowrap"}),p.appendChild(I),p.appendChild(A),x.count!==void 0){const C=u.ownerDocument.createElement("span");if(C.textContent=x.count.toLocaleString(),Object.assign(C.style,{color:x.isActive?m.iconActive:m.text,fontWeight:x.isActive?"700":"400",whiteSpace:"nowrap"}),p.appendChild(C),x.percent!==void 0){const L=u.ownerDocument.createElement("span");L.textContent=Ye(x.percent),Object.assign(L.style,{color:m.label,whiteSpace:"nowrap"}),p.appendChild(L)}}p.setAttribute("role","button"),p.setAttribute("aria-pressed",x.isActive?"true":"false"),p.tabIndex=0,p.addEventListener("mouseenter",()=>{p.style.background=m.bgHover}),p.addEventListener("mouseleave",()=>{p.style.background="transparent"}),p.addEventListener("focus",()=>{p.style.background=m.bgHover}),p.addEventListener("blur",()=>{p.style.background="transparent"}),p.addEventListener("click",x.onClick),p.addEventListener("keydown",C=>{(C.key==="Enter"||C.key===" ")&&(C.preventDefault(),x.onClick())}),u.appendChild(p)}export function renderWaferGallery(u,x,p={}){Xo();const I=6,A=p.downloadFilename??"wafer-gallery";let C=p.columns;const L=p.showHelpButton??!1,N=p.userGuideExtension,F=p.insights?.enabled??!1,ae=To(p.zIndex),ce=p.summaryPanel,Qe=e=>[...Co(e)],vt=()=>w.flatMap(e=>e?[e.ringCount??4]:[]),Je=()=>vt()[0]??4;function Dn(e){const t=[...new Set(e)].sort((n,o)=>n-o);return t.length<2?null:{code:"ring-count-mixed",severity:"warning",message:`These wafers were built with different ring counts (${t.join(", ")}). Each card and each wafer's findings use their own; the lot's ring yield in the Summary panel, report and Insights uses ${e[0]} rings.`}}let j=p.lotStatsSummary,ye=p.viewOptions?.legendPosition??"default",Ae=p.perCardLegend??!1;function we(){const e=d.plotMode??"hardBin";return rn.has(e)?null:d.colorbarRangeMode==="data"?"Each map has its own value range, so it keeps its own colour bar":"These wafers have no shared colour bar for value mode, so each map keeps its own"}function xt(){const e=Ae||we()!==null;for(const t of S)t&&t.setOptions({showLegend:e})}let d={plotMode:"hardBin",showDieLabels:!1,showRingBoundaries:!1,showQuadrantBoundaries:!1,rotation:0,flipX:!1,flipY:!1,...p.viewOptions},S=[],se=[],fe=[],T=[],w=x.map(e=>typeof e=="function"?null:e);function Ct(){return gi(w)}function ue(){if(w.some(e=>e?.testDefs?.length))return Ct().defs}let et=0,Pe=0;const _=new Map;let Bn=0,P=null,ve=null,St=new Set,h=null,kn={};function Mt(e){return w[e]?.statsSummary??j?.perWafer.find(t=>t.waferIndex===e)?.summary}function tt(){return!!(w.some(e=>e?.statsSummary?.findings.length)||j?.perWafer.some(e=>e.summary.findings.length))}const En=Lo(),pe=$o(En),{makeBtn:Q,setActive:xe,makeSep:G,makeMenuRow:On,makeMenuSection:Tn,makeDropdown:Dt,closeOpenMenu:He,getOpenMenu:Ce,setOpenMenu:Se}=pe;u.ownerDocument.addEventListener("click",He,!0);const Bt=()=>cn();window.addEventListener("blur",Bt);let kt=p.findingsNotice;function Et(e){St=new Set(e);const t=[...W.querySelectorAll(".wmap-gallery-card")];let n;t.forEach((o,l)=>{const a=St.has(l);o.style.outline=a?`3px solid ${m.findingHighlight}`:"",o.style.outlineOffset=a?"-3px":"",a&&n===void 0&&(n=o)}),n?.scrollIntoView?.({behavior:"smooth",block:"nearest"})}function Ot(){Et([])}function Tt(){for(const e of S)e&&e.clearSelection()}function nt(e,t){const n=new Set(e),o=t??S.map((l,a)=>a);for(const l of o){const a=T[l],s=S[l];if(!a||!s)continue;const r=a.dies.filter(i=>n.has(mi(i)));s.setSelection(r)}}function Ln(){const e=[];for(let t=0;t<w.length;t++){const n=w[t];if(!n)continue;const o=Mt(t)?.findings??[];o.length&&e.push({index:t,item:n,unusualCount:o.filter(l=>l.severity==="unusual").length,notableCount:o.filter(l=>l.severity==="notable").length,totalCount:o.length})}return e}function $n(e){const t=T[e]??w[e];t&&Le(e,t)}function Wn(e){const t=Mt(e)?.findings??[];if(t.length)return{total:t.length,unusual:t.filter(n=>n.severity==="unusual").length,notable:t.filter(n=>n.severity==="notable").length}}function Rn(){const e=ie(w.flatMap(n=>n?.hbinDefs??[])),t=ie(w.flatMap(n=>n?.sbinDefs??[]));Po(ui({items:w.map((n,o)=>({label:Ie(n,o),wafer:n?.wafer,dies:n?.dies,passBins:Qe(n),statsSummary:n?.statsSummary})),hbinDefs:e.length?e:void 0,sbinDefs:t.length?t:void 0,testDefs:ue(),ringCount:Je()}),{anchor:u})}function In(){if(!h)return;h.innerHTML="";const e=Ln();if(w.length>0){const n=u.ownerDocument.createElement("div"),o=e.length,l=w.filter(Boolean).length;n.textContent=o===0?`No findings on any of the ${l} wafers`:`${o} of ${l} wafer${l===1?"":"s"} with findings`,Object.assign(n.style,{fontSize:O.body,color:m.label,marginBottom:v.md}),h.appendChild(n)}if(e.length>0){const n=u.ownerDocument.createElement("button");n.type="button",n.textContent="Summary report",Object.assign(n.style,{...ko("outlined"),marginBottom:v.lg,display:"block",width:"100%",textAlign:"left"}),ht(n),n.addEventListener("click",Rn),h.appendChild(n)}if(e.length===0){const n=u.ownerDocument.createElement("div");Object.assign(n.style,{color:m.icon,fontSize:O.body,padding:"4px 0"}),n.textContent="No findings on any wafer.",h.appendChild(n)}else for(const{index:n,item:o,unusualCount:l,notableCount:a,totalCount:s}of e){const r=l?"unusual":a?"notable":"info",i=l+a||s,c=u.ownerDocument.createElement("button");c.type="button",Object.assign(c.style,{display:"flex",alignItems:"center",justifyContent:"space-between",width:"100%",padding:"5px 8px",marginBottom:v.xs,border:"none",borderLeft:`3px solid ${ln(r)}`,borderRadius:z.control,background:m.bgHover,cursor:"pointer",fontSize:O.body,textAlign:"left",boxSizing:"border-box"}),c.addEventListener("mouseover",()=>{c.style.background=m.bgActive}),c.addEventListener("mouseout",()=>{c.style.background=m.bgHover});const f=u.ownerDocument.createElement("span");f.textContent=Ie(o,n),Object.assign(f.style,{color:m.iconHover,overflow:"hidden",textOverflow:"ellipsis",whiteSpace:"nowrap"});const b=u.ownerDocument.createElement("span");b.textContent=String(i),c.setAttribute("aria-label",`${Ie(o,n)} \u2014 ${i} finding${i===1?"":"s"}${l?`, ${l} unusual`:""} \u2014 view wafer`),Object.assign(b.style,{marginLeft:v.sm,flexShrink:"0",background:ln(r),color:"#fff",borderRadius:z.container,padding:"1px 5px",fontSize:O.body,fontWeight:"600"}),c.appendChild(f),c.appendChild(b),c.addEventListener("click",()=>Le(n,o)),h.appendChild(c)}const t=u.ownerDocument.createElement("div");t.style.height="12px",h.appendChild(t)}function V(){if(h)if(j){const e=ie(w.flatMap(n=>n?.hbinDefs??[])),t=ie(w.flatMap(n=>n?.sbinDefs??[]));fi(h,{lotSummary:j,items:w,hbinDefs:e.length?e:void 0,sbinDefs:t.length?t:void 0,testDefs:ue(),ringCount:Je(),binColors:d.binColors,plotMode:d.plotMode??"hardBin",fallbackFormat:d.fallbackFormat,activeFindingId:ve,warnings:p.warnings?.display??!0?J:[],findingsFilter:kn,findingsNotice:kt,onFindingsFilterChange:V,onSaveText:p.onSaveText,onFindingClick:(n,o)=>{ve===n.id?ot():Pn(n,o),V()},onWaferClick:$n,findingsFor:Wn,dieListOptions:p.dieList})}else In()}function An(e){const t=e.highlight;return t?.kind==="wafer"&&t.waferIndices?.length?t.waferIndices:null}function Lt(e){return[e.variable.kind,e.variable.index??"",e.variable.bin??"",e.comparison.family,e.comparison.left,e.effect.direction].join("|")}let Fe=[];function $t(){if(Fe.length!==0){for(const e of Fe)S[e]?.setOptions({plotMode:d.plotMode,activeTest:d.activeTest});Fe=[]}}function ot(){ve=null,Ot(),Tt(),$t(),E({highlightBin:void 0},{fireCallback:!1})}function Pn(e,t){if(ve===e.id){ot();return}ve=e.id,t.style.background=m.bgActive,t.style.fontWeight="600",$t();const{kind:n,index:o}=e.variable;if(n==="test"){const a=o??0,s=ue();if(s===void 0||s.some(i=>i.testNumber===a))E({plotMode:"value",activeTest:a,highlightBin:void 0},{fireCallback:!1});else{E({highlightBin:void 0},{fireCallback:!1});const c=be.has(d.plotMode??"hardBin")?null:An(e);if(c){for(const f of c)S[f]?.setOptions({plotMode:"value",activeTest:a});Fe=[...c]}}}else n==="softBin"?E({plotMode:"softBin",highlightBin:void 0},{fireCallback:!1}):E({plotMode:"hardBin",highlightBin:void 0},{fireCallback:!1});Ot(),Tt();const l=e.highlight;if(l.kind==="wafer"){Et(l.waferIndices);const a=Lt(e);for(const s of l.waferIndices){const c=T[s]?.statsSummary?.findings.find(f=>Lt(f)===a)?.highlight?.dieKeys;c?.length&&nt(c,[s])}}else l.kind==="bin"?l.dieKeys?.length&&nt(l.dieKeys):(l.kind==="region"||l.kind==="dies")&&l.dieKeys?.length&&nt(l.dieKeys)}function le(){if(!P)return;const e=!!h;P.style.display=(j||tt())&&e&&!Ee?"flex":"none";const t=h?h.style.display!=="none":!1;(j?.hasNotableFindings||w.some(o=>o?.statsSummary?.hasNotableFindings))&&!t?P.style.color=m.findingIndicator:P.dataset.active||(P.style.color=m.icon)}const $=u.ownerDocument.createElement("div");$.dataset.wmapToolbar="gallery",Object.assign($.style,{flexShrink:"0",marginLeft:"auto",display:"inline-flex",flexDirection:"row",alignItems:"center",gap:"0",background:m.menuBg,border:`1px solid ${m.menuBorder}`,borderRadius:z.control,padding:"0",boxShadow:We.panel,flexWrap:"wrap",minWidth:"0",overflowX:"auto"});const Wt=()=>{const e=Ce();e&&(e.remove(),Se(null)),Re(X,!1)},X=Q("mode","Plot mode",()=>{if(Ce()){Wt();return}const e=w.flatMap(y=>y?.dies??[]),t=ue(),{testEntries:n,binEntries:o,stackedEntries:l}=Zo(e,t,{includeStacked:!0}),a=w.filter(Boolean).length,s=y=>{if(y===void 0)return a;let R=0;for(const de of w)de?.dies?.some(U=>wn(U,y)!==void 0||U.testValues?.[y]!==void 0)&&R++;return R},r=a<2?n:n.map(y=>{const R=s(y.activeTest);return R===a?y:{...y,label:`${y.label} \u2014 ${R} of ${a} wafers`}}),i=d.plotMode??"hardBin",c=d.activeTest??0;function f(y){return y.plotMode!==i?!1:y.plotMode==="value"?c===(y.activeTest??0):y.plotMode==="metadata"?d.activeMetadataKey===y.activeMetadataKey:!0}function b(y,R){y.activeTest!==void 0?E({plotMode:"value",activeTest:y.activeTest,activeMetadataKey:void 0,logScale:y.logScale}):y.activeMetadataKey!==void 0?E({plotMode:y.plotMode,activeTest:void 0,activeMetadataKey:y.activeMetadataKey,passFailDisplay:"off"}):E({plotMode:y.plotMode,activeTest:void 0,activeMetadataKey:void 0,passFailDisplay:"off"}),R.remove(),Se(null),Re(X,!1)}const k=new Set,D=w.flatMap(y=>y?.metadataFields??[]).filter(y=>!k.has(y.key)&&k.add(y.key)).filter(y=>Yo(e,y.key)).map(Qo),M=Wo(X.getBoundingClientRect(),r,o,l,f,b,{makeMenuRow:On,makeMenuSection:Tn},i,X.ownerDocument.defaultView??window,D);fn(X).appendChild(M),Se(M),Re(X,!0),un(M,X,Wt)});Re(X,!1);const Hn=()=>T.flatMap(e=>e?[...e.hbinDefs??[],...e.sbinDefs??[]]:[]).some(e=>e.color),{btn:Fn,sync:Rt}=Vo(pe,()=>({...d,plotMode:d.plotMode??"hardBin"}),Hn,e=>E(e));Rt();const jn=x.some(e=>typeof e!="function"&&(e.reticles?.length??0)>0);function je(){const e=ue(),{testNumber:t}=mo(d.activeTest??0,e);return{testNumber:t,td:ho(e,t)}}function It(){if((d.plotMode??"hardBin")!=="value")return!1;const{td:e}=je();return e!==void 0&&(e.limitLow!==void 0||e.limitHigh!==void 0)}function Me(){if((d.plotMode??"hardBin")!=="value")return!1;const{td:e}=je();return e!==void 0&&!bt(e)}function Vn(){if((d.plotMode??"hardBin")!=="value")return!1;const{testNumber:e,td:t}=je();return w.some(n=>n?.dies?.some(o=>wn(o,e,t)!==void 0))}function zn(e,t){if(!t||!bt(t))return;const n=t.limitLow!==void 0||t.limitHigh!==void 0;if((mn(d)==="spec"&&n?"spec":d.colorbarRangeMode??"spec")==="spec"&&n)return;let a=1/0,s=-1/0;for(const r of w)if(r)for(const i of r.dies){const c=si(i,e);c!==void 0&&(c<a&&(a=c),c>s&&(s=c))}if(isFinite(a))return a===s?[a,a+1]:[a,s]}function At(){if((d.plotMode??"hardBin")!=="metadata")return;const e=d.activeMetadataKey;if(!e)return;const t=new Set;for(const n of w)if(n)for(const o of bo(n.dies,e))t.add(o);return{key:e,values:[...t].sort(vn)}}function it(){const e=At();if(!(e===void 0&&d.metadataValueOrder===void 0)){d={...d,metadataValueOrder:e};for(const t of S)t&&t.setOptions({metadataValueOrder:e})}}function at(){if((d.plotMode??"hardBin")!=="value")return;const{testNumber:e,td:t}=je(),n=zn(e,t),o=n?{test:e,range:n}:void 0;d={...d,valueRange:o};for(const l of S)l&&l.setOptions({valueRange:o})}let st=!1;function Nn(){if(st)return;st=!0,(u.ownerDocument.defaultView?.requestAnimationFrame??(t=>setTimeout(()=>t(0),0)))(()=>{st=!1,at(),it(),Ht()})}let Pt=[];function De(){const e=ie(w.flatMap(l=>l?.hbinDefs??[])),t=ie(w.flatMap(l=>l?.sbinDefs??[])),{colors:n,mixedHardBins:o}=wo(w.flatMap(l=>l?[{dies:l.dies,passBins:Qe(l)}]:[]),{binColorScheme:d.binColorScheme,useDefinedBinColors:d.useDefinedBinColors,hbinDefs:e,sbinDefs:t});return Pt=o,n}function Ht(){const e=De();d={...d,binColors:e};for(const t of S)t&&t.setOptions({binColors:e});Be()}const _n=_o(pe,()=>qo(d,jn,{functionalActive:Me(),hasLimits:It()&&!Me(),hasRecorded:Vn()&&!Me(),binMode:(d.plotMode??"hardBin")==="hardBin"||(d.plotMode??"hardBin")==="softBin"},e=>E(e)),()=>Uo(d)),{btn:Gn,sync:Ft}=No(pe,()=>({plotMode:d.plotMode,legendPosition:ye}),e=>{ye=e;for(const t of S)t&&t.setOptions({legendPosition:ye})},{get:()=>Ae,set:e=>{Ae=e,xt()},blockedReason:we});Ft();const Kn=[{value:"mean",label:"Mean"},{value:"median",label:"Median"},{value:"stddev",label:"Std Dev"},{value:"min",label:"Min"},{value:"max",label:"Max"},{value:"count",label:"Count"}],jt=Dt("aggr","Aggregation method",()=>Kn,()=>d.aggregationMethod??"mean",e=>E({aggregationMethod:e}));function Vt(){const e=d.plotMode==="stackedValues";jt.style.display=e?"":"none"}Vt();const{btn:qn,sync:zt}=zo(pe,()=>({...d,functionalActive:Me()}),e=>E(e));zt();const Ve=Q("specRange","Colorbar range: spec limits",()=>{const e=d.colorbarRangeMode==="data"?"spec":"data";E({colorbarRangeMode:e})});function Nt(){const e=(d.plotMode??"hardBin")==="value"&&It()&&mn(d)==="off"&&!Me();Ve.style.display=e?"":"none";const t=(d.colorbarRangeMode??"spec")==="spec";xe(Ve,t),Ve.ariaLabel=t?"Colorbar range: spec limits (click for data range)":"Colorbar range: data range (click for spec limits)"}Nt();const Un=Go(pe,()=>d,e=>E(e)),Xn=Q("downloadAll","Download gallery PNG",fo),Zn=Dt("columns","Columns",()=>[{value:"auto",label:"Auto"},{value:"1",label:"1 column"},{value:"2",label:"2 columns"},{value:"3",label:"3 columns"},{value:"4",label:"4 columns"},{value:"5",label:"5 columns"}],()=>C!=null?String(C):"auto",e=>ao(e==="auto"?void 0:Number(e))),B=u.ownerDocument.createElement("div");Object.assign(B.style,{display:"inline-flex",alignItems:"center",gap:"0"}),$.appendChild(B),B.appendChild(X),B.appendChild(Fn),B.appendChild(jt),B.appendChild(qn),B.appendChild(Ve),B.appendChild(G()),B.appendChild(_n),B.appendChild(G()),B.appendChild(Gn),B.appendChild(G()),B.appendChild(Un),B.appendChild(G()),B.appendChild(Zn),B.appendChild(G()),B.appendChild(Xn),(j||tt())&&(P=Q("findings","Summary panel",()=>{if(!h)return;const e=h.style.display!=="none";e||V(),h.style.display=e?"none":"flex",xe(P,!e),le()}),$.appendChild(G()),$.appendChild(P));let K=null,ze=null,J=[],_t=!1;function Be(){const e=li({lotStatsSummary:j,result:{warnings:[...w.flatMap(n=>n?.warnings??[]),...Ct().warnings]},extra:[d.binColors&&vo(d.binColors,d.plotMode),xo(Pt),Dn(vt())].filter(n=>!!n)}),t=e.length!==J.length||e.some((n,o)=>n.code!==J[o]?.code||n.message!==J[o]?.message);J=e,(t||!_t)&&(_t=!0,p.warnings?.onWarning?.(e)),Yn()}function Yn(){if(!K||!ze)return;const e=J.length,t=e>0;if(K.style.display=t?"flex":"none",ze.style.display=t?"":"none",!t)return;const n=di(J[0]),o=`${e} data ${e===1?"warning":"warnings"}`;K.style.color=n==="error"?m.errText:m.warnText,K.ariaLabel=n==="error"?`${o} \u2014 some wafers may be positionally wrong`:o}(p.warnings?.display??!0)&&(ze=G(),K=Q("warning","Data warnings",()=>{const e=Ce();if(He(new MouseEvent("click")),e)return;const t=ri(K.getBoundingClientRect(),J,K.ownerDocument.defaultView??window);fn(u).appendChild(t),Se(t),un(t,K,()=>He(new MouseEvent("click")))}),Re(K,!1),$.appendChild(ze),$.appendChild(K)),Be();let re=null;F&&(re=Q("analysis","Insights",()=>{qt(!Ee)}),re.dataset.wmapInsightsBtn="1",$.appendChild(G()),$.appendChild(re));function Gt(){import("./userGuideHtml.js").then(e=>jo({buildWaferMap:ai,renderWaferMap:ni,renderWaferGallery,analyzeWaferMap:Ze},e.USER_GUIDE_HTML,N,u))}L&&($.appendChild(G()),$.appendChild(Q("help","User guide",()=>Gt())));const ge=u.ownerDocument.createElement("div");ge.dataset.wmapGalleryMeta="1",Object.assign(ge.style,{flex:"1",minWidth:"0",overflow:"hidden",display:"flex",alignItems:"center",padding:`${v.sm} ${v.lg}`,fontSize:O.sub,lineHeight:Xe.none,boxSizing:"border-box"});const Ne=u.ownerDocument.createElement("div");Object.assign(Ne.style,{background:m.canvasBg,display:"flex",alignItems:"stretch",gap:v.lg,paddingBottom:v.lg,minWidth:"0"});const ee=u.ownerDocument.createElement("div");ee.dataset.wmapGalleryLegend="1",Object.assign(ee.style,{display:"flex",flexDirection:"column",gap:v.sm,background:m.menuBg,border:`1px solid ${m.menuBorder}`,borderRadius:z.container,padding:`${v.sm} ${v.lg}`,marginBottom:v.lg,boxShadow:We.panel,fontSize:O.body,lineHeight:Xe.none,boxSizing:"border-box",width:"100%",minWidth:"0"});const q=u.ownerDocument.createElement("div");Object.assign(q.style,{display:"flex",flexDirection:"row",alignItems:"flex-start"});let me=null,ke=null,Kt=null;function Qn(){return F?me?Promise.resolve(me):(Kt??=import("./insightsTab.js").then(({createInsightsTab:e})=>(me=e({getItems:()=>w,getLotStats:()=>j,getBinColors:()=>d.binColors??De(),getRingCount:Je,defaultView:p.insights?.defaultView,onOpenGuide:void 0,showMetadataStrip:!1,onSaveImage:p.onSaveImage,onSaveText:p.onSaveText,ownerDocument:u.ownerDocument,openWafer:(t,n,o)=>{const l=w[t];if(!l)return;let a=null;const s=Ao({title:n,onClose:()=>a?.destroy(),anchor:u});en(s,n,l.wafer.metadata??void 0),a=ft(s.contentWrap,l,o)}}),ke=me.el,ke.style.display="none",u.appendChild(ke),me)),Kt):Promise.resolve(null)}let Ee=!1;function qt(e){F&&(Ee=e,ke&&(ke.style.display=e?"flex":"none"),q.style.display=e?"none":"flex",B.style.display=e?"none":"inline-flex",$.style.display="inline-flex",re&&(xe(re,e),re.innerHTML=e?$e.wafer:$e.analysis,re.ariaLabel=e?"Back to gallery view":"Insights"),le(),Ke(),e&&Qn().then(t=>{!t||!Ee||(t.el.style.display="flex",t.render())}))}F&&p.insights?.defaultOpen&&queueMicrotask(()=>qt(!0));const Jn=4,lt=240,Ut=480,eo=720,to=1.25;let Z=Ut;function Xt(e){const t=I*2+110+2;let n=null;for(const o of e){if(o==null||!o.dies?.length)continue;const l=o.dies[0].width;if(!(l>0))continue;const a=o.wafer.radius*2*(Jn/l),s=Math.ceil(a+t);(n==null||s>n)&&(n=s)}return n}function no(e){const t=Xt(e);return t==null?lt:Math.min(Z,Math.max(lt,t))}function oo(e){const t=Xt(e);if(t==null)return!1;const n=Math.min(eo,Math.max(Ut,t));return n<=Z?!1:(Z=n,!0)}function io(){for(const e of Array.from(W.children))e.classList.contains("wmap-gallery-card")&&(e.style.maxWidth=`${Z}px`,e.style.maxHeight=`${Z}px`)}let _e=lt,Zt=0;function Yt(e){const t=oo(e);if(t&&io(),C!=null){t&&Ge();return}const n=no(e);n>_e&&(_e=n),Ge()}function ao(e){C=e,Ge()}function rt(e){return`repeat(${e}, minmax(0, ${Z}px))`}function Qt(e){W.style.gridTemplateColumns!==e&&(W.style.gridTemplateColumns=e)}function Ge(){if(C!=null){Qt(rt(C));return}const e=Math.max(1,Zt),t=12,n=W.clientWidth||0,o=a=>(n-t*(a-1))/a;let l=Math.max(1,Math.ceil(Math.sqrt(e)));if(n>0){const a=Math.min(Z,_e*to),s=r=>{let i=0;for(;i<e&&o(i+1)>=r;)i++;return i};l=s(a)||s(_e)||1}Qt(rt(l))}const W=u.ownerDocument.createElement("div");if(Object.assign(W.style,{flex:"1 1 0",minWidth:"0",display:"grid",gridTemplateColumns:rt(1),gap:v.xl,paddingLeft:oe,paddingRight:oe,justifyContent:"start",alignContent:"start",isolation:"isolate"}),ce?.placement){const e=ce.placement;h=yt(e,oe,u.ownerDocument),h.style.maxHeight="calc(100vh - 80px)",h.style.position="sticky",h.style.top="8px",h.style.display="flex",h.style.flexDirection="column"}else if(j||tt()){const e=!!ce?.defaultOpen;h=yt("right",oe,u.ownerDocument),h.style.maxHeight="calc(100vh - 80px)",h.style.position="sticky",h.style.top="8px",h.style.flexDirection="column",h.style.display=e?"flex":"none",V()}le(),h?.style.display!=="none"&&P&&xe(P,!0),(ce?.placement??"right")==="left"?(h&&q.appendChild(h),q.appendChild(W)):(q.appendChild(W),h&&q.appendChild(h));const Oe=u.ownerDocument.createElement("div");Object.assign(Oe.style,{position:"sticky",top:"0",display:"flex",flexDirection:"column",paddingLeft:oe,paddingRight:oe,paddingTop:oe,zIndex:"1",background:m.menuBg}),Ne.appendChild(ge),Ne.appendChild($),Oe.appendChild(Ne),Oe.appendChild(ee),u.appendChild(Oe),u.appendChild(q);function Ke(){ee.innerHTML="";const e=d.plotMode??"hardBin",t=!Ee&&rn.has(e),n=T.filter(g=>g!=null),o=n.find(g=>g.isLotStack),l=ci(n.map(g=>({metadata:g.wafer.metadata??void 0})),o?{lotSize:o.lotSize??n.length,aggrMethod:o.aggrMethod}:void 0),a=e==="metadata",s=d.activeMetadataKey,r=t&&!a?Sn(n,g=>(e==="softBin"?g.sbin:g.hbin)??void 0,Qe):{counts:new Map,total:0,pass:0},i=t&&!a?d.binColors??De():void 0,c=i?e==="softBin"?i.pass.soft:i.pass.hard:new Set,f=Jo(r.counts.entries(),c).map(([g])=>g),b=t&&a&&s?Sn(n,g=>yo(g.metadata?.[s])):{counts:new Map,total:0,pass:0},k=a?At()?.values:void 0,D=t&&a?(k??[...b.counts.keys()].sort(vn)).filter(g=>b.counts.has(g)):[];if(ge.innerHTML="",l&&ge.appendChild(l),ge.style.display=l?"flex":"none",!f.length&&!D.length){ee.style.display="none";return}ee.style.display="flex";const M=u.ownerDocument.createElement("div");Object.assign(M.style,{display:"flex",flexWrap:"wrap",alignItems:"center",gap:`${v.sm} ${v.xl}`,maxHeight:"86px",overflowY:"auto",overflowX:"hidden"}),ee.appendChild(M);const y=d.highlightBin,R=T.flatMap(g=>g?.metadataFields??[]).find(g=>g.key===s),de=a?R?.label??(s?pi(s):"Metadata"):Oo[e],U=u.ownerDocument.createElement("span");if(U.textContent=de,Object.assign(U.style,{fontSize:O.body,fontWeight:"700",letterSpacing:Bo,textTransform:"uppercase",color:m.label,flexShrink:"0"}),M.appendChild(U),!a&&r.total>0){const g=r.pass,H=g/r.total*100,te=u.ownerDocument.createElement("span");te.textContent=`Yield ${Ye(H)}`,Object.assign(te.style,{fontSize:O.body,fontWeight:"700",color:m.value,whiteSpace:"nowrap",flexShrink:"0"});const ne=u.ownerDocument.createElement("span");ne.textContent=`${g.toLocaleString()} / ${r.total.toLocaleString()} dies`,Object.assign(ne.style,{fontSize:O.body,color:m.label,whiteSpace:"nowrap",flexShrink:"0"}),M.appendChild(te),M.appendChild(ne)}if(a){const g=d.highlightMetadataValue;D.forEach((H,te)=>{const ne=g===H,Ue=R?.values?.find(mt=>mt.value===H),po=Ue?.color??Mo(te),sn=b.counts.get(H)??0;Mn(M,{color:po,isActive:ne,label:Ue?.label??H,count:sn,percent:b.total>0?sn/b.total*100:void 0,onClick:()=>{const mt=d.highlightMetadataValue===H?void 0:H;E({highlightMetadataValue:mt})}})});return}const ut=e==="softBin"?T.flatMap(g=>g?.sbinDefs??[]):T.flatMap(g=>g?.hbinDefs??[]),nn=new Set,pt=[];for(const g of ut)nn.has(g.bin)||(nn.add(g.bin),pt.push(g));const gt=pt.length>0?new Map(pt.map(g=>[g.bin,g])):null,on=i??d.binColors??De(),uo=e==="softBin"?on.soft:on.hard,an=g=>uo.get(g)??So;for(const g of f){const H=y===g,te=gt?.get(g),ne=r.counts.get(g)??0;Mn(M,{color:an(g),isActive:H,label:te?.name?`${g} \xB7 ${te.name}`:`Bin ${g}`,count:ne,percent:r.total>0?ne/r.total*100:void 0,onClick:()=>{const Ue=d.highlightBin===g?void 0:g;E({highlightBin:Ue})}})}r.total>0&&f.length>1&&ee.appendChild(bi(u.ownerDocument,[...r.counts.entries()].sort(([,g],[,H])=>H-g).map(([g,H])=>({color:an(g),count:H,label:gt?.get(g)?.name?`${g} \xB7 ${gt.get(g).name}`:`Bin ${g}`})),r.total))}function Te(e){const t=w.filter(r=>r!==null);if(!t.length)return[];const n=t.map(r=>r.dies.filter(hi)),o=t[0].wafer,l=t.length,a=o.metadata?{...o,metadata:(({wafer:r,waferId:i,...c})=>c)(o.metadata)}:o;function s(r,i){return{...r,stats:{...r.stats,isLotStack:!0,aggregationMethod:i,lotSize:l}}}if(e==="stackedValues"){const r=ue();let i=r?.filter(bt);if(r!==void 0&&i?.length===0)return[];if(r?.length||(i=go(t.flatMap(b=>b.dies)).map(b=>({testNumber:b,name:`Test ${b}`}))),!i?.length)return[];const c=d.aggregationMethod??"mean";return i.map(f=>{const b=ti(n,c,f.testNumber),k={testNumber:0,name:f.name,unit:f.unit};return{wafer:a,dies:b,testDefs:[k],label:`${f.name} \xB7 ${c}`,isLotStack:!0,aggrMethod:c,lotSize:l,statsSummary:s(Ze({wafer:a,dies:b,testDefs:[k]},{testNumbers:[0]}),c)}})}if(e==="stackedBins"){let r=ie(t.flatMap(i=>i.hbinDefs??[]));return(!r||r.length===0)&&(r=[...new Set(t.flatMap(c=>c.dies.map(f=>f.hbin).filter(f=>f!=null)))].sort((c,f)=>c-f).map(c=>({bin:c,name:`Bin ${c}`}))),r.map(i=>{const c=hn(n,i.bin,"hard"),f=[{bin:i.bin,name:i.name}];return{wafer:a,dies:c,hbinDefs:f,label:`${i.bin} \xB7 ${i.name}`,isLotStack:!0,aggrMethod:"countBin",lotSize:l,statsSummary:s(Ze({wafer:a,dies:c,hbinDefs:f}),"countBin")}})}if(e==="stackedSoftBins"){let r=ie(t.flatMap(i=>i.sbinDefs??[]));return(!r||r.length===0)&&(r=[...new Set(t.flatMap(c=>c.dies.map(f=>f.sbin).filter(f=>f!=null)))].sort((c,f)=>c-f).map(c=>({bin:c,name:`Bin ${c}`}))),r.map(i=>{const c=hn(n,i.bin,"soft"),f=[{bin:i.bin,name:i.name}];return{wafer:a,dies:c,sbinDefs:f,label:`${i.bin} \xB7 ${i.name}`,isLotStack:!0,aggrMethod:"countBin",lotSize:l,statsSummary:s(Ze({wafer:a,dies:c,sbinDefs:f}),"countBin")}})}return t}function dt(e){const t=w.length;return e==="stackedBins"||e==="stackedSoftBins"?{valueRange:[0,t],lotSize:t}:e==="stackedValues"?{aggregationMethod:d.aggregationMethod??"mean",valueRange:void 0,lotSize:void 0}:{}}function E(e,{fireCallback:t=!0}={}){const n=Object.keys(e),o=d.plotMode,l=we()!==null;d={...d,...e};const a=d.plotMode,s=be.has(a),r=o!==void 0&&be.has(o),i=Pe>0;if("binColorScheme"in e||"useDefinedBinColors"in e){const f=De();d={...d,binColors:f},e={...e,binColors:f}}if(e.plotMode!==void 0)if(s){const f=dt(a);if(d={...d,...f},i)for(const b of S)b&&b.setOptions(e);else Y(Te(a))}else if(r){const{valueRange:f,lotSize:b,aggregationMethod:k,...D}=d;if(d=D,i)for(const M of S)M&&M.setOptions(e);else Y(w.filter(M=>M!==null))}else for(const f of S)f&&f.setOptions(e);else if(e.aggregationMethod!==void 0&&a==="stackedValues")if(i)for(const f of S)f&&f.setOptions(e);else Y(Te("stackedValues"));else for(const f of S)f&&f.setOptions(e);we()!==null!==l&&xt(),Ke(),Vt(),Ft(),Rt();const c=e.plotMode!==void 0&&e.plotMode!==o;(ii.some(f=>f in e)||c)&&V(),Be(),zt(),Nt(),at(),it(),t&&p.onViewOptionsChange?.(yn(d),n,oi(n))}function Jt(e,t,n){const o=e.createElement("div");Object.assign(o.style,{display:"flex",alignItems:"center",gap:v.xs,flex:"1",minWidth:"0"});const l=e.createElement("span");l.textContent=t,Object.assign(l.style,{fontWeight:"700",fontSize:O.sub,overflow:"hidden",textOverflow:"ellipsis",whiteSpace:"nowrap"}),o.appendChild(l);const a=Cn(n??{});let s=null;if(a.length>0){const r=e.createElement("span");Object.assign(r.style,{fontSize:O.body,lineHeight:Xe.none,color:m.label,flexShrink:"0"}),r.textContent="\u25BE",o.appendChild(r),Object.assign(o.style,{cursor:"pointer",borderRadius:z.control}),o.addEventListener("mouseenter",()=>{o.style.background=m.bgHover}),o.addEventListener("mouseleave",()=>{o.style.background="none"}),s=e.createElement("div"),s.dataset.wmapCardMetaPanel="1",Object.assign(s.style,{position:"absolute",top:"0",left:"0",right:"0",zIndex:dn,background:m.menuBg,borderBottom:`1px solid ${m.menuBorder}`,boxShadow:We.menu,padding:`${v.md} ${v.lg}`,fontSize:O.body,display:"none"});const i=xn(n??{});i&&s.appendChild(i),o.setAttribute("aria-expanded","false"),o.setAttribute("aria-label",`Wafer info for ${t||"this card"}. Click to expand.`),pn(o,c=>{r.textContent=c?"\u25B4":"\u25BE",o.setAttribute("aria-expanded",String(c)),s&&(s.style.display=c?"block":"none")})}return{wrap:o,metaPanel:s}}function en(e,t,n){const o=e.box.querySelector("[data-wmap-window-title]"),l=Cn(n??{});if(!o||l.length===0)return;const a=o.parentElement,s=u.ownerDocument.createElement("div");Object.assign(s.style,{display:"flex",alignItems:"center",gap:v.xs,flex:"1",minWidth:"0",cursor:"pointer"}),ht(s,"bare"),a?.insertBefore(s,o),s.appendChild(o);const r=u.ownerDocument.createElement("span");Object.assign(r.style,{fontSize:O.body,lineHeight:Xe.none,color:m.label,flexShrink:"0"}),r.textContent="\u25BE",s.appendChild(r);const i=u.ownerDocument.createElement("div");i.dataset.wmapCardMetaPanel="1",Object.assign(i.style,{position:"absolute",top:"0",left:"0",right:"0",zIndex:dn,background:m.menuBg,borderBottom:`1px solid ${m.menuBorder}`,boxShadow:We.menu,padding:`${v.md} ${v.lg}`,fontSize:O.body,display:"none"});const c=xn(n??{});c&&i.appendChild(c),e.contentWrap.style.position="relative",e.contentWrap.appendChild(i),s.setAttribute("aria-expanded","false"),s.setAttribute("aria-label",`Wafer info for ${t}. Click to expand.`),pn(s,f=>{r.textContent=f?"\u25B4":"\u25BE",s.setAttribute("aria-expanded",String(f)),i.style.display=f?"block":"none"})}function ct(e,t,n){const o=u.ownerDocument.createElement("div");o.className="wmap-gallery-card",Object.assign(o.style,{background:m.menuBg,border:`1px solid ${m.menuBorder}`,borderRadius:z.container,boxShadow:We.panel,overflow:"hidden",display:"flex",flexDirection:"column",position:"relative",aspectRatio:"1",maxWidth:`${Z}px`,maxHeight:`${Z}px`});const l=u.ownerDocument.createElement("div");Object.assign(l.style,{display:"flex",alignItems:"center",padding:`${v.md} ${v.lg} ${v.sm}`,borderBottom:`1px solid ${m.menuBorder}`,flexShrink:"0",gap:v.sm});const{wrap:a,metaPanel:s}=Jt(document,Ie(e,t),e.wafer.metadata??void 0);l.appendChild(a);const r=u.ownerDocument.createElement("button");r.dataset.wmapExpandBtn="1",r.setAttribute("aria-label","Open full view"),gn(r),r.innerHTML=$e.expand,Object.assign(r.style,{display:"flex",alignItems:"center",justifyContent:"center",border:`1px solid ${m.menuBorder}`,borderRadius:z.control,background:m.panelBg,color:m.label,padding:v.xxs,cursor:"pointer",flexShrink:"0",width:"22px",height:"22px"}),ht(r),l.appendChild(r),o.appendChild(l);const i=u.ownerDocument.createElement("div");Object.assign(i.style,{position:"relative",flex:"1",minHeight:"0",overflow:"hidden",display:"flex",flexDirection:"column"}),o.appendChild(i),s&&i.appendChild(s),W.appendChild(o);const c=Ae||we()!==null,f=e.viewOptions?{...d,...e.viewOptions}:d,b=bn(i,e,{viewOptions:{...f,showLegend:c,legendPosition:ye},showTooltip:!0,padding:I,statsSummary:e.statsSummary,onSaveImage:p.onSaveImage,onSaveText:p.onSaveText,onClick:e.onClick,onSelect:e.onSelect,onExpand:()=>Le(t,e),showIdentity:!1,chromeInset:Eo});return b.setViewControlsVisible(!1),b.setSummaryVisible(!1),r.onclick=()=>Le(t,e),{card:o,ctrl:b,canvasWrapper:i,expandBtn:r}}function so(e){const t=fe[e];if(!t)return;const n=[..._.values()].find(o=>o.cardIndex===e);n?(t.innerHTML=$e.minimize,t.setAttribute("aria-label","Reattach to gallery"),t.onclick=()=>ro(n.id)):(t.innerHTML=$e.expand,t.setAttribute("aria-label","Open full view"),t.onclick=()=>Le(e,T[e]))}function Y(e){co(),Ce()?.remove(),Se(null),ot(),T=[];for(const a of S)a&&a.destroy();S=[],se=[],fe=[],Pe=0,W.innerHTML="",Zt=e.length,Yt(e.map(a=>typeof a=="function"?null:a));const t=[];for(let a=0;a<e.length;a++){const s=e[a];if(typeof s=="function"){const r=u.ownerDocument.createElement("div");r.className="wmap-gallery-card",Object.assign(r.style,{background:m.menuBg,border:`1px solid ${m.menuBorder}`,borderRadius:z.container,aspectRatio:"1",display:"flex",alignItems:"center",justifyContent:"center"});const i=u.ownerDocument.createElement("span");i.textContent="\u2026",Object.assign(i.style,{color:m.label,fontSize:"18px"}),r.appendChild(i),W.appendChild(r),T.push(null),S.push(null),se.push(null),fe.push(null),t.push({index:a,factory:s,placeholder:r}),Pe++}else{const{ctrl:r,canvasWrapper:i,expandBtn:c}=ct(s,a,e.length);T.push(s),S.push(r),se.push(i),fe.push(c)}}Ht(),Ke(),at(),it();const n=++et;let o=0;function l(){if(n!==et)return;if(o>=t.length){if(be.has(d.plotMode)){const D=d.plotMode;Y(Te(D))}return}const{index:a,factory:s,placeholder:r}=t[o++],i=s();T[a]=i,w[a]=i,Yt([i]);const{card:c,ctrl:f,canvasWrapper:b,expandBtn:k}=ct(i,a,e.length);S[a]=f,se[a]=b,fe[a]=k,Pe--,r.replaceWith(c),Ke(),Nn(),!h&&!ce?.placement&&i.statsSummary?.findings.length?(h=yt("right",oe,u.ownerDocument),h.style.maxHeight="calc(100vh - 80px)",h.style.position="sticky",h.style.top="8px",h.style.flexDirection="column",h.style.display="none",(ce?.placement??"right")==="left"?q.insertBefore(h,W):q.appendChild(h),V(),P||(P=Q("findings","Summary panel",()=>{if(!h)return;const M=h.style.display!=="none";M||V(),h.style.display=M?"none":"flex",xe(P,!M),le()}),$.appendChild(G()),$.appendChild(P)),le()):h&&h.style.display!=="none"&&V(),le(),setTimeout(l,0)}t.length>0&&setTimeout(l,0)}if(be.has(d.plotMode)&&w.length>0){const e=dt(d.plotMode);d={...d,...e},Y(Te(d.plotMode))}else Y(x);Be();let he=0;const tn=new(u.ownerDocument.defaultView??window).ResizeObserver(()=>{if(he)return;he=(u.ownerDocument.defaultView??window).requestAnimationFrame(()=>{he=0,C==null&&Ge()})});tn.observe(W),h&&V();function lo(e,t){const n=se[e];if(!n)return;n.innerHTML="",Object.assign(n.style,{alignItems:"center",justifyContent:"center"});const o=u.ownerDocument.createElement("span");o.textContent=t?"Opened in its own window":"Opened in the wafer viewer",Object.assign(o.style,{color:m.label,fontSize:O.body,textAlign:"center",padding:"0 12px"}),n.appendChild(o)}function Le(e,t){for(const s of _.values())if(s.cardIndex===e)return;const n=Ie(t,e),o=Bn++,l=S[e]?.getOptions(),a=Ro(n);if(a){const s=a.document;s.title=n,Object.assign(s.documentElement.style,{height:"100%"}),Object.assign(s.body.style,{margin:"0",height:"100vh",overflow:"hidden",display:"flex",flexDirection:"column",fontFamily:O.family}),Ho(u,s.documentElement);const r=Fo(u,s.documentElement,()=>M.setOptions({})),i=s.createElement("div");i.dataset.wmapWindowBanner="1",i.textContent=n,Object.assign(i.style,{display:"none",padding:`${v.sm} ${v.xl}`,fontSize:O.body,fontWeight:"700",background:m.warnBg,color:m.warnText,borderBottom:`1px solid ${m.warnBorder}`,flexShrink:"0"}),s.body.appendChild(i);const c=s.createElement("div");Object.assign(c.style,{flex:"1",minHeight:"0",display:"flex",flexDirection:"column"}),s.body.appendChild(c);const{wrap:f,metaPanel:b}=Jt(s,n,t.wafer.metadata??void 0),k=s.createElement("div");Object.assign(k.style,{display:"flex",alignItems:"center",padding:`${v.md} ${v.lg} ${v.sm}`,borderBottom:`1px solid ${m.menuBorder}`,flexShrink:"0",gap:v.sm}),k.appendChild(f),c.appendChild(k);const D=s.createElement("div");Object.assign(D.style,{position:"relative",flex:"1",minHeight:"0",overflow:"hidden",display:"flex",flexDirection:"column"}),b&&D.appendChild(b),c.appendChild(D);const M=ft(D,t,void 0,l),y=setInterval(()=>{a.closed&&qe(o)},400);a.addEventListener("pagehide",()=>qe(o)),_.set(o,{id:o,ctrl:M,cardIndex:e,label:n,closePollId:y,stopThemeSync:r,close:()=>{a.closed||a.close()},setTitle:R=>{s.title=R,i.textContent=R,i.style.display="block"}})}else{const s=Io({title:n,onClose:()=>qe(o),anchor:u});s.contentWrap.style.flexDirection="column",en(s,n,t.wafer.metadata??void 0);const r=ft(s.contentWrap,t,void 0,l);_.set(o,{id:o,ctrl:r,cardIndex:e,label:n,closePollId:null,close:()=>s.close(),setTitle:i=>{const c=s.box.querySelector("[data-wmap-window-title]");c&&(c.textContent=i,c.title=i)}})}S[e]?.destroy(),S[e]=null,lo(e,a!==null),so(e)}function ft(e,t,n,o){const l=t.viewOptions?{...d,...t.viewOptions}:d,a=o?{...l,...o}:l,r={...n!==void 0?{...a,plotMode:"value",activeTest:n}:a,showLegend:!0,legendPosition:ye},i=bn(e,t,{viewOptions:r,showTooltip:!0,padding:I,statsSummary:t.statsSummary,onSaveImage:p.onSaveImage,onSaveText:p.onSaveText,onClick:t.onClick,onSelect:t.onSelect,showExpandButton:!1,showIdentity:!1});return i.setViewControlsVisible(!0),i.setSummaryVisible(!0),i.setExpandVisible(!1),i}function qe(e){const t=_.get(e);if(!t)return;_.delete(e),t.closePollId!=null&&clearInterval(t.closePollId),t.stopThemeSync?.();const n=t.ctrl.getOptions();if(t.ctrl.destroy(),t.cardIndex===null)return;const o=t.cardIndex,l=T[o];if(!l)return;const a={...l,viewOptions:n};T[o]=a;const{card:s,ctrl:r,canvasWrapper:i,expandBtn:c}=ct(a,o,T.length);se[o]?.parentElement?.replaceWith(s),S[o]=r,se[o]=i,fe[o]=c}function ro(e){const t=_.get(e);t&&(t.close(),qe(e))}function co(){for(const e of _.values())e.cardIndex!==null&&(e.cardIndex=null,e.setTitle(`${e.label} \u2014 unlinked from gallery`))}function fo(){const e=[...W.querySelectorAll("canvas")];if(!e.length)return;const t=e.length,n=Math.ceil(Math.sqrt(t)),o=Math.ceil(t/n),l=e[0].width,a=e[0].height,s=8,r=(u.ownerDocument.defaultView??window).devicePixelRatio||1,i=Math.round(26*r),c=Math.round(12*r),f=u.ownerDocument.createElement("canvas");f.width=n*l+(n-1)*s,f.height=o*(a+i)+(o-1)*s;const b=f.getContext("2d"),k=Do(W);b.fillStyle=k.background,b.fillRect(0,0,f.width,f.height),e.forEach((D,M)=>{const y=M%n,R=Math.floor(M/n),de=y*(l+s),U=R*(a+i+s),ut=D.closest(".wmap-gallery-card")?.querySelector("span")?.textContent??"";b.fillStyle=k.surface,b.fillRect(de,U,l,i),b.fillStyle=k.text,b.font=`700 ${c}px system-ui, sans-serif`,b.textAlign="left",b.textBaseline="middle",b.fillText(ut,de+Math.round(10*r),U+i/2,l-Math.round(20*r)),b.drawImage(D,de,U+i)}),f.toBlob(D=>{D&&Ko(D,A,p.onSaveImage)})}return{setItems(e){w=e.map(n=>typeof n=="function"?null:n);const t=d.plotMode;if(be.has(t))if(e.some(o=>typeof o=="function"))Y(e);else{const o=dt(t);d={...d,...o},Y(Te(t))}else Y(e)},setOptions(e){E(e,{fireCallback:!1})},getOptions(){return yn(d)},setFindingsNotice(e){kt=e,V()},setLotStatsSummary(e){j=e,Be(),h&&V(),le()},openUserGuide:Gt,destroy(){et++;for(const e of[..._.values()])e.closePollId!=null&&clearInterval(e.closePollId),e.close(),e.ctrl.destroy();_.clear();for(const e of S)e&&e.destroy();S=[],Ce()?.remove(),tn.disconnect(),he&&((u.ownerDocument.defaultView??window).cancelAnimationFrame(he),he=0),u.ownerDocument.removeEventListener("click",He,!0),window.removeEventListener("blur",Bt),ae(),cn(),Oe.remove(),q.remove(),h?.remove(),me?.destroy()}}}
1
+ import{getUniqueTestNumbers as So,resolveTestNumber as Mo,findTestDef as Do,collectMetadataValues as ko}from"../renderer/buildView.js";import{metadataCategoricalValue as Bo}from"../core/metadata.js";import{resolveBinColorsForMaps as Eo,mergeBinDefs as Xe,binColorWarning as Oo,mixedPassBinsWarning as To,binColorsEqual as Lo}from"../renderer/binColors.js";import{itemPassBins as Ro,INPUT_DEFAULT_PASS_BINS as $o}from"../core/passBins.js";import{NO_DATA_FILL as Wo}from"../renderer/colorMap.js";import{metadataValueColor as Io}from"../renderer/colorMap.js";import{resolveCanvasTheme as Ao}from"./canvasTheme.js";import{ICONS as $e}from"./icons.js";import{SHADOW as We,LEADING as Ze,TRACKING as Po,controlStyle as Fo,wireControlHover as vt,SPACE as w,EDGE_GUTTER as ie,MAP_CHROME_INSET as Ho,RADIUS as _,FONT as L,CLR as h,sevColor as mn,MODE_LABELS as jo,BIN_LEGEND_MODES as gn,STACKED_MODES as he,Z_ABOVE as hn,applyOverlayZ as Vo,getTooltip as zo,hideTooltip as bn,createToolbarHelpers as No,buildModeMenuEl as _o,openDetachWindow as Go,openFloatingWindow as Ko,openModal as qo,openReportModal as Uo,copyWmapThemeTokens as Xo,syncWmapPopupTheme as Zo,openUserGuideWindow as Yo,makePaletteBtn as Qo,makeLogScaleBtn as Jo,makeLegendStyleBtn as ei,makeOverlaysBtn as ti,makeOrientationBtn as ni,menuLayerFor as yn,saveImageBlob as wn,markMenuTrigger as Ie,wireMenuA11y as vn,wireExpandToggle as xn,wireTooltip as Cn,requestedPassFailDisplay as Sn,overlayMenuRows as oi,anyOverlayActive as ii,logWmapVersionOnce as li,buildDataModeEntries as ai,metadataKeyHasData as si,metadataModeEntry as ri}from"./toolbar.js";import{waferDisplayLabel as Ye,waferIdentityLabel as di}from"../core/waferLabel.js";import{metadataDisplayValue as ci}from"../core/metadata.js";import{withExportContext as ui,noticeDownloadFilenameChange as fi}from"./exportName.js";import{sortBinsForDisplay as pi}from"../stats/binPareto.js";import{diePassStatus as mi}from"../core/dies.js";import{aggregateValues as gi,aggregateBinCounts as Mn}from"../core/aggregates.js";import{renderWaferMap as hi,renderWaferMapCard as Dn,toPublicViewOptions as kn}from"./renderWaferMap.js";import{classifyChanged as bi,COLOR_KEYS as yi}from"./renderWaferMap.js";import{buildWaferMap as wi,getTestPassStatus as Bn,isParametricTest as xt,getDieTestValue as vi}from"../renderer/buildWaferMap.js";import{analyzeWaferMap as Qe}from"../stats/analyzeWaferMap.js";import{collectWarnings as xi,buildWarningsMenuEl as Ci,severityOf as Si}from"./warnings.js";import{compareNatural as Ct,arrayEqual as En}from"../core/utils.js";import{createSummaryPanelEl as St,buildMetadataStripRow as Mi,buildCompactMetadataRows as On,metadataEntries as Tn,renderLotSummaryContentSteps as Di,reportMapsFromItems as ki}from"./summaryPanel.js";import{renderLotReportHtml as Bi}from"../stats/renderSummaryReport.js";import{prettyKey as Ei}from"../stats/facets.js";import{mergeTestDefs as Oi}from"../stats/mergeTestDefs.js";import{getDieKey as Ti,hasPosition as Li}from"../core/dies.js";import{runChunked as Ri}from"./chunked.js";function Ln(f){if(typeof f!="number"||!Number.isFinite(f))return;const x=Math.round(f);return x>=1?x:void 0}const Mt=12;function $i(f,x,p){let A=x;if(x.length>Mt){const D=x.slice(0,Mt-1),$=x.slice(Mt-1),H=$.reduce((Q,j)=>Q+j.count,0);A=[...D,{color:h.label,count:H,label:`Other (${$.length} bins)`}]}const M=f.createElement("div");Object.assign(M.style,{display:"flex",columnGap:"1px",width:"100%",height:"8px",borderRadius:_.control,overflow:"hidden",border:`1px solid ${h.menuBorder}`,boxSizing:"border-box",marginTop:w.xs,flexShrink:"0"}),M.setAttribute("role","img"),M.setAttribute("aria-label",`Population split: ${A.map(D=>`${D.label} ${Je(D.count/p*100)}`).join(", ")}`);for(const D of A){const $=D.count/p*100,H=f.createElement("div");Object.assign(H.style,{flex:`${$} 1 0`,minWidth:$>0?"2px":"0",background:D.color}),Cn(H,`${D.label} \xB7 ${D.count.toLocaleString()} dies \xB7 ${Je($)}`),M.appendChild(H)}return M}function Je(f){return f>0&&f<.1?"<0.1%":`${f.toFixed(1)}%`}function Rn(f,x,p){const A=new Map;let M=0,D=0;for(const $ of f){const H=p?new Set(p($)):void 0;for(const Q of $.dies){if(Q.partial||Q.edgeExcluded)continue;const j=x(Q);j!==void 0&&(A.set(j,(A.get(j)??0)+1),M++,H&&mi(Q,H)&&D++)}}return{counts:A,total:M,pass:D}}function $n(f,x){const p=f.ownerDocument.createElement("div");Object.assign(p.style,{display:"flex",alignItems:"center",gap:"5px",cursor:"pointer",userSelect:"none",padding:`${w.xxs} ${w.xs}`,borderRadius:_.control});const A=f.ownerDocument.createElement("span");Object.assign(A.style,{display:"inline-block",width:"13px",height:"13px",flexShrink:"0",background:x.color,border:x.isActive?`2px solid ${h.iconActive}`:`1px solid ${h.menuBorder}`,borderRadius:_.control,boxSizing:"border-box"});const M=f.ownerDocument.createElement("span");if(M.textContent=x.label,Object.assign(M.style,{fontWeight:x.isActive?"700":"400",color:x.isActive?h.iconActive:h.text,whiteSpace:"nowrap"}),p.appendChild(A),p.appendChild(M),x.count!==void 0){const D=f.ownerDocument.createElement("span");if(D.textContent=x.count.toLocaleString(),Object.assign(D.style,{color:x.isActive?h.iconActive:h.text,fontWeight:x.isActive?"700":"400",whiteSpace:"nowrap"}),p.appendChild(D),x.percent!==void 0){const $=f.ownerDocument.createElement("span");$.textContent=Je(x.percent),Object.assign($.style,{color:h.label,whiteSpace:"nowrap"}),p.appendChild($)}}p.setAttribute("role","button"),p.setAttribute("aria-pressed",x.isActive?"true":"false"),p.tabIndex=0,p.addEventListener("mouseenter",()=>{p.style.background=h.bgHover}),p.addEventListener("mouseleave",()=>{p.style.background="transparent"}),p.addEventListener("focus",()=>{p.style.background=h.bgHover}),p.addEventListener("blur",()=>{p.style.background="transparent"}),p.addEventListener("click",x.onClick),p.addEventListener("keydown",D=>{(D.key==="Enter"||D.key===" ")&&(D.preventDefault(),x.onClick())}),f.appendChild(p)}export function renderWaferGallery(f,x,p={}){li();const A=6;let M=Ln(p.columns);const D=p.showHelpButton??!1,$=p.userGuideExtension,H=p.insights?.enabled??!1,Q=Vo(p.zIndex),j=p.summaryPanel,Wn=e=>[...Ro(e)],Dt=()=>v.flatMap(e=>e?[e.ringCount??4]:[]),et=()=>Dt()[0]??4;function In(e){const t=[...new Set(e)].sort((n,o)=>n-o);return t.length<2?null:{code:"ring-count-mixed",severity:"warning",message:`These wafers were built with different ring counts (${t.join(", ")}). Each card and each wafer's findings use their own; the lot's ring yield in the Summary panel, report and Insights uses ${e[0]} rings.`}}let V=p.lotStatsSummary,be=p.viewOptions?.legendPosition??"default",Ae=p.perCardLegend??!1;function ye(){const e=c.plotMode??"hardBin";return gn.has(e)?null:c.colorbarRangeMode==="data"?"Each map has its own value range, so it keeps its own colour bar":"These wafers have no shared colour bar for value mode, so each map keeps its own"}function kt(){const e=Ae||ye()!==null;for(const t of S)t&&t.setOptions({showLegend:e})}let c={plotMode:"hardBin",showDieLabels:!1,showRingBoundaries:!1,showQuadrantBoundaries:!1,rotation:0,flipX:!1,flipY:!1,...p.viewOptions},S=[],le=[],re=[],R=[];const Pe=ui(()=>{const e=R.filter(n=>n!=null);return{lots:[...new Set(e.map(n=>ci(n.wafer.metadata?.lot)).filter(n=>n!==void 0))].sort(Ct),...e.length===1?{wafer:di(e[0])}:{waferCount:e.length}}},p.onSaveImage,p.onSaveText);fi(p.downloadFilename);let v=x.map(e=>typeof e=="function"?null:e);function Bt(){return Oi(v)}function we(){if(v.some(e=>e?.testDefs?.length))return Bt().defs}let ve=0,Fe=0;const G=new Map;let An=0,P=null,xe=null,Et=new Set,b=null,Pn={};function Ot(e){return v[e]?.statsSummary??V?.perWafer.find(t=>t.waferIndex===e)?.summary}function tt(){return!!(v.some(e=>e?.statsSummary?.findings.length)||V?.perWafer.some(e=>e.summary.findings.length))}const Fn=zo(),de=No(Fn),{makeBtn:J,setActive:Ce,makeSep:K,makeMenuRow:Hn,makeMenuSection:jn,makeDropdown:Tt,closeOpenMenu:He,getOpenMenu:Se,setOpenMenu:Me}=de;f.ownerDocument.addEventListener("click",He,!0);const Lt=()=>bn();window.addEventListener("blur",Lt);let Rt=p.findingsNotice;function $t(e){Et=new Set(e);const t=[...I.querySelectorAll(".wmap-gallery-card")];let n;t.forEach((o,d)=>{const a=Et.has(d);o.style.outline=a?`3px solid ${h.findingHighlight}`:"",o.style.outlineOffset=a?"-3px":"",a&&n===void 0&&(n=o)}),n?.scrollIntoView?.({behavior:"smooth",block:"nearest"})}function Wt(){$t([])}function It(){for(const e of S)e&&e.clearSelection()}function nt(e,t){const n=new Set(e),o=t??S.map((d,a)=>a);for(const d of o){const a=R[d],l=S[d];if(!a||!l)continue;const r=a.dies.filter(i=>n.has(Ti(i)));l.setSelection(r)}}function Vn(){const e=[];for(let t=0;t<v.length;t++){const n=v[t];if(!n)continue;const o=Ot(t)?.findings??[];o.length&&e.push({index:t,item:n,unusualCount:o.filter(d=>d.severity==="unusual").length,notableCount:o.filter(d=>d.severity==="notable").length,totalCount:o.length})}return e}function zn(e){const t=R[e]??v[e];t&&Re(e,t)}function Nn(e){const t=Ot(e)?.findings??[];if(t.length)return{total:t.length,unusual:t.filter(n=>n.severity==="unusual").length,notable:t.filter(n=>n.severity==="notable").length}}function _n(){Uo(Bi(ki(v,$o,et())),{anchor:f})}function Gn(){if(!b)return;b.innerHTML="";const e=Vn();if(v.length>0){const n=f.ownerDocument.createElement("div"),o=e.length,d=v.filter(Boolean).length;n.textContent=o===0?`No findings on any of the ${d} wafers`:`${o} of ${d} wafer${d===1?"":"s"} with findings`,Object.assign(n.style,{fontSize:L.body,color:h.label,marginBottom:w.md}),b.appendChild(n)}if(e.length>0){const n=f.ownerDocument.createElement("button");n.type="button",n.textContent="Summary report",Object.assign(n.style,{...Fo("outlined"),marginBottom:w.lg,display:"block",width:"100%",textAlign:"left"}),vt(n),n.addEventListener("click",_n),b.appendChild(n)}if(e.length===0){const n=f.ownerDocument.createElement("div");Object.assign(n.style,{color:h.icon,fontSize:L.body,padding:"4px 0"}),n.textContent="No findings on any wafer.",b.appendChild(n)}else for(const{index:n,item:o,unusualCount:d,notableCount:a,totalCount:l}of e){const r=d?"unusual":a?"notable":"info",i=d+a||l,s=f.ownerDocument.createElement("button");s.type="button",Object.assign(s.style,{display:"flex",alignItems:"center",justifyContent:"space-between",width:"100%",padding:"5px 8px",marginBottom:w.xs,border:"none",borderLeft:`3px solid ${mn(r)}`,borderRadius:_.control,background:h.bgHover,cursor:"pointer",fontSize:L.body,textAlign:"left",boxSizing:"border-box"}),s.addEventListener("mouseover",()=>{s.style.background=h.bgActive}),s.addEventListener("mouseout",()=>{s.style.background=h.bgHover});const u=f.ownerDocument.createElement("span");u.textContent=Ye(o,n),Object.assign(u.style,{color:h.iconHover,overflow:"hidden",textOverflow:"ellipsis",whiteSpace:"nowrap"});const m=f.ownerDocument.createElement("span");m.textContent=String(i),s.setAttribute("aria-label",`${Ye(o,n)} \u2014 ${i} finding${i===1?"":"s"}${d?`, ${d} unusual`:""} \u2014 view wafer`),Object.assign(m.style,{marginLeft:w.sm,flexShrink:"0",background:mn(r),color:"#fff",borderRadius:_.container,padding:"1px 5px",fontSize:L.body,fontWeight:"600"}),s.appendChild(u),s.appendChild(m),s.addEventListener("click",()=>Re(n,o)),b.appendChild(s)}const t=f.ownerDocument.createElement("div");t.style.height="12px",b.appendChild(t)}let ce=null,De=null;function z(){if(b)if(ce?.cancel(),ce=null,V){const e=Xe(v.map(n=>n?.hbinDefs)),t=Xe(v.map(n=>n?.sbinDefs));ce=Ri(Di(b,{lotSummary:V,items:v,hbinDefs:e.length?e:void 0,sbinDefs:t.length?t:void 0,testDefs:we(),ringCount:et(),binColors:c.binColors,plotMode:c.plotMode??"hardBin",fallbackFormat:c.fallbackFormat,activeFindingId:xe,warnings:p.warnings?.display??!0?ee:[],findingsFilter:Pn,findingsNotice:Rt,onFindingsFilterChange:z,onSaveText:Pe.onSaveText,onFindingClick:(n,o)=>{xe===n.id?ot():qn(n,o),z()},onWaferClick:zn,findingsFor:Nn,dieListOptions:p.dieList}),{onDone:()=>{const n=De;De=null,n?.()}})}else Gn()}function Kn(e){const t=e.highlight;return t?.kind==="wafer"&&t.waferIndices?.length?t.waferIndices:null}function At(e){return[e.variable.kind,e.variable.index??"",e.variable.bin??"",e.comparison.family,e.comparison.left,e.effect.direction].join("|")}let je=[];function Pt(){if(je.length!==0){for(const e of je)S[e]?.setOptions({plotMode:c.plotMode,activeTest:c.activeTest});je=[]}}function ot(){xe=null,Wt(),It(),Pt(),T({highlightBin:void 0},{fireCallback:!1})}function qn(e,t){if(xe===e.id){ot();return}xe=e.id,t.style.background=h.bgActive,t.style.fontWeight="600",Pt();const{kind:n,index:o}=e.variable;if(n==="test"){const a=o??0,l=we();if(l===void 0||l.some(i=>i.testNumber===a))T({plotMode:"value",activeTest:a,highlightBin:void 0},{fireCallback:!1});else{T({highlightBin:void 0},{fireCallback:!1});const s=he.has(c.plotMode??"hardBin")?null:Kn(e);if(s){for(const u of s)S[u]?.setOptions({plotMode:"value",activeTest:a});je=[...s]}}}else n==="softBin"?T({plotMode:"softBin",highlightBin:void 0},{fireCallback:!1}):T({plotMode:"hardBin",highlightBin:void 0},{fireCallback:!1});Wt(),It();const d=e.highlight;if(d.kind==="wafer"){$t(d.waferIndices);const a=At(e);for(const l of d.waferIndices){const s=R[l]?.statsSummary?.findings.find(u=>At(u)===a)?.highlight?.dieKeys;s?.length&&nt(s,[l])}}else d.kind==="bin"?d.dieKeys?.length&&nt(d.dieKeys):(d.kind==="region"||d.kind==="dies")&&d.dieKeys?.length&&nt(d.dieKeys)}function ae(){if(!P)return;const e=!!b;P.style.display=(V||tt())&&e&&!Oe?"flex":"none";const t=b?b.style.display!=="none":!1;(V?.hasNotableFindings||v.some(o=>o?.statsSummary?.hasNotableFindings))&&!t?P.style.color=h.findingIndicator:P.dataset.active||(P.style.color=h.icon)}const W=f.ownerDocument.createElement("div");W.dataset.wmapToolbar="gallery",Object.assign(W.style,{flexShrink:"0",marginLeft:"auto",display:"inline-flex",flexDirection:"row",alignItems:"center",gap:"0",background:h.menuBg,border:`1px solid ${h.menuBorder}`,borderRadius:_.control,padding:"0",boxShadow:We.panel,flexWrap:"wrap",minWidth:"0",overflowX:"auto"});const Ft=()=>{const e=Se();e&&(e.remove(),Me(null)),Ie(Z,!1)},Z=J("mode","Plot mode",()=>{if(Se()){Ft();return}const e=v.flatMap(y=>y?.dies??[]),t=we(),{testEntries:n,binEntries:o,stackedEntries:d}=ai(e,t,{includeStacked:!0}),a=v.filter(Boolean).length,l=y=>{if(y===void 0)return a;let O=0;for(const N of v)N?.dies?.some(X=>Bn(X,y)!==void 0||X.testValues?.[y]!==void 0)&&O++;return O},r=a<2?n:n.map(y=>{const O=l(y.activeTest);return O===a?y:{...y,label:`${y.label} \u2014 ${O} of ${a} wafers`}}),i=c.plotMode??"hardBin",s=c.activeTest??0;function u(y){return y.plotMode!==i?!1:y.plotMode==="value"?s===(y.activeTest??0):y.plotMode==="metadata"?c.activeMetadataKey===y.activeMetadataKey:!0}function m(y,O){y.activeTest!==void 0?T({plotMode:"value",activeTest:y.activeTest,activeMetadataKey:void 0,logScale:y.logScale}):y.activeMetadataKey!==void 0?T({plotMode:y.plotMode,activeTest:void 0,activeMetadataKey:y.activeMetadataKey,passFailDisplay:"off"}):T({plotMode:y.plotMode,activeTest:void 0,activeMetadataKey:void 0,passFailDisplay:"off"}),O.remove(),Me(null),Ie(Z,!1)}const k=new Set,B=v.flatMap(y=>y?.metadataFields??[]).filter(y=>!k.has(y.key)&&k.add(y.key)).filter(y=>si(e,y.key)).map(ri),C=_o(Z.getBoundingClientRect(),r,o,d,u,m,{makeMenuRow:Hn,makeMenuSection:jn},i,Z.ownerDocument.defaultView??window,B);yn(Z).appendChild(C),Me(C),Ie(Z,!0),vn(C,Z,Ft)});Ie(Z,!1);const Un=()=>R.flatMap(e=>e?[...e.hbinDefs??[],...e.sbinDefs??[]]:[]).some(e=>e.color),{btn:Xn,sync:Ht}=Qo(de,()=>({...c,plotMode:c.plotMode??"hardBin"}),Un,e=>T(e));Ht();const Zn=x.some(e=>typeof e!="function"&&(e.reticles?.length??0)>0);function Ve(){const e=we(),{testNumber:t}=Mo(c.activeTest??0,e);return{testNumber:t,td:Do(e,t)}}function jt(){if((c.plotMode??"hardBin")!=="value")return!1;const{td:e}=Ve();return e!==void 0&&(e.limitLow!==void 0||e.limitHigh!==void 0)}function ke(){if((c.plotMode??"hardBin")!=="value")return!1;const{td:e}=Ve();return e!==void 0&&!xt(e)}function Yn(){if((c.plotMode??"hardBin")!=="value")return!1;const{testNumber:e,td:t}=Ve();return v.some(n=>n?.dies?.some(o=>Bn(o,e,t)!==void 0))}function Qn(e,t){if(!t||!xt(t))return;const n=t.limitLow!==void 0||t.limitHigh!==void 0;if((Sn(c)==="spec"&&n?"spec":c.colorbarRangeMode??"spec")==="spec"&&n)return;let a=1/0,l=-1/0;for(const r of v)if(r)for(const i of r.dies){const s=vi(i,e);s!==void 0&&(s<a&&(a=s),s>l&&(l=s))}if(isFinite(a))return a===l?[a,a+1]:[a,l]}function it(e,t,n){const o=c[e];if(o===t||o!=null&&t!=null&&n(o,t))return!1;c={...c,[e]:t};for(const d of S)d&&d.setOptions({[e]:t});return!0}function Vt(){if((c.plotMode??"hardBin")!=="metadata")return;const e=c.activeMetadataKey;if(!e)return;const t=new Set;for(const n of v)if(n)for(const o of ko(n.dies,e))t.add(o);return{key:e,values:[...t].sort(Ct)}}function lt(){it("metadataValueOrder",Vt(),(e,t)=>e.key===t.key&&En(e.values,t.values))}function at(){if((c.plotMode??"hardBin")!=="value")return;const{testNumber:e,td:t}=Ve(),n=Qn(e,t);it("valueRange",n?{test:e,range:n}:void 0,(d,a)=>{const l=s=>Array.isArray(s)?{test:void 0,range:s}:{test:s.test,range:s.range},r=l(d),i=l(a);return r.test===i.test&&En(r.range,i.range)})}let st=!1;function Jn(){if(st)return;st=!0,(f.ownerDocument.defaultView?.requestAnimationFrame??(t=>setTimeout(()=>t(0),0)))(()=>{st=!1,at(),lt(),Nt()})}let rt=!1;function eo(){if(rt)return;rt=!0,(f.ownerDocument.defaultView?.requestAnimationFrame??(t=>setTimeout(()=>t(0),0)))(()=>{rt=!1,Ke()})}let zt=[];function ue(){const{colors:e,mixedHardBins:t}=Eo(v.filter(n=>n!=null),{binColorScheme:c.binColorScheme,useDefinedBinColors:c.useDefinedBinColors});return zt=t,e}function Nt(){it("binColors",ue(),Lo),Be()}const to=ti(de,()=>oi(c,Zn,{functionalActive:ke(),hasLimits:jt()&&!ke(),hasRecorded:Yn()&&!ke(),binMode:(c.plotMode??"hardBin")==="hardBin"||(c.plotMode??"hardBin")==="softBin"},e=>T(e)),()=>ii(c)),{btn:no,sync:_t}=ei(de,()=>({plotMode:c.plotMode,legendPosition:be}),e=>{be=e;for(const t of S)t&&t.setOptions({legendPosition:be})},{get:()=>Ae,set:e=>{Ae=e,kt()},blockedReason:ye});_t();const oo=[{value:"mean",label:"Mean"},{value:"median",label:"Median"},{value:"stddev",label:"Std Dev"},{value:"min",label:"Min"},{value:"max",label:"Max"},{value:"count",label:"Count"}],Gt=Tt("aggr","Aggregation method",()=>oo,()=>c.aggregationMethod??"mean",e=>T({aggregationMethod:e}));function Kt(){const e=c.plotMode==="stackedValues";Gt.style.display=e?"":"none"}Kt();const{btn:io,sync:qt}=Jo(de,()=>({...c,functionalActive:ke()}),e=>T(e));qt();const ze=J("specRange","Colorbar range: spec limits",()=>{const e=c.colorbarRangeMode==="data"?"spec":"data";T({colorbarRangeMode:e})});function Ut(){const e=(c.plotMode??"hardBin")==="value"&&jt()&&Sn(c)==="off"&&!ke();ze.style.display=e?"":"none";const t=(c.colorbarRangeMode??"spec")==="spec";Ce(ze,t),ze.ariaLabel=t?"Colorbar range: spec limits (click for data range)":"Colorbar range: data range (click for spec limits)"}Ut();const lo=ni(de,()=>c,e=>T(e)),ao=J("downloadAll","Download gallery PNG",vo),so=Tt("columns","Columns",()=>[{value:"auto",label:"Auto"},{value:"1",label:"1 column"},{value:"2",label:"2 columns"},{value:"3",label:"3 columns"},{value:"4",label:"4 columns"},{value:"5",label:"5 columns"}],()=>M!=null?String(M):"auto",e=>ln(e==="auto"?void 0:Number(e))),E=f.ownerDocument.createElement("div");Object.assign(E.style,{display:"inline-flex",alignItems:"center",gap:"0"}),W.appendChild(E),E.appendChild(Z),E.appendChild(Xn),E.appendChild(Gt),E.appendChild(io),E.appendChild(ze),E.appendChild(K()),E.appendChild(to),E.appendChild(K()),E.appendChild(no),E.appendChild(K()),E.appendChild(lo),E.appendChild(K()),E.appendChild(so),E.appendChild(K()),E.appendChild(ao),(V||tt())&&(P=J("findings","Summary panel",()=>{if(!b)return;const e=b.style.display!=="none";e||z(),b.style.display=e?"none":"flex",Ce(P,!e),ae()}),W.appendChild(K()),W.appendChild(P));let q=null,Ne=null,ee=[],Xt=!1;function Be(){const e=xi({lotStatsSummary:V,result:{warnings:[...v.flatMap(n=>n?.warnings??[]),...Bt().warnings]},extra:[c.binColors&&Oo(c.binColors,c.plotMode),To(zt),In(Dt())].filter(n=>!!n)}),t=e.length!==ee.length||e.some((n,o)=>n.code!==ee[o]?.code||n.message!==ee[o]?.message);ee=e,(t||!Xt)&&(Xt=!0,p.warnings?.onWarning?.(e)),ro()}function ro(){if(!q||!Ne)return;const e=ee.length,t=e>0;if(q.style.display=t?"flex":"none",Ne.style.display=t?"":"none",!t)return;const n=Si(ee[0]),o=`${e} data ${e===1?"warning":"warnings"}`;q.style.color=n==="error"?h.errText:h.warnText,q.ariaLabel=n==="error"?`${o} \u2014 some wafers may be positionally wrong`:o}(p.warnings?.display??!0)&&(Ne=K(),q=J("warning","Data warnings",()=>{const e=Se();if(He(new MouseEvent("click")),e)return;const t=Ci(q.getBoundingClientRect(),ee,q.ownerDocument.defaultView??window);yn(f).appendChild(t),Me(t),vn(t,q,()=>He(new MouseEvent("click")))}),Ie(q,!1),W.appendChild(Ne),W.appendChild(q)),Be();let se=null;H&&(se=J("analysis","Insights",()=>{Qt(!Oe)}),se.dataset.wmapInsightsBtn="1",W.appendChild(K()),W.appendChild(se));function Zt(){import("./userGuideHtml.js").then(e=>Yo({buildWaferMap:wi,renderWaferMap:hi,renderWaferGallery,analyzeWaferMap:Qe},e.USER_GUIDE_HTML,$,f))}D&&(W.appendChild(K()),W.appendChild(J("help","User guide",()=>Zt())));const fe=f.ownerDocument.createElement("div");fe.dataset.wmapGalleryMeta="1",Object.assign(fe.style,{flex:"1",minWidth:"0",overflow:"hidden",display:"flex",alignItems:"center",padding:`${w.sm} ${w.lg}`,fontSize:L.sub,lineHeight:Ze.none,boxSizing:"border-box"});const _e=f.ownerDocument.createElement("div");Object.assign(_e.style,{background:h.canvasBg,display:"flex",alignItems:"stretch",gap:w.lg,paddingBottom:w.lg,minWidth:"0"});const te=f.ownerDocument.createElement("div");te.dataset.wmapGalleryLegend="1",Object.assign(te.style,{display:"flex",flexDirection:"column",gap:w.sm,background:h.menuBg,border:`1px solid ${h.menuBorder}`,borderRadius:_.container,padding:`${w.sm} ${w.lg}`,marginBottom:w.lg,boxShadow:We.panel,fontSize:L.body,lineHeight:Ze.none,boxSizing:"border-box",width:"100%",minWidth:"0"});const U=f.ownerDocument.createElement("div");Object.assign(U.style,{display:"flex",flexDirection:"row",alignItems:"flex-start"});let pe=null,Ee=null,Yt=null;function co(){return H?pe?Promise.resolve(pe):(Yt??=import("./insightsTab.js").then(({createInsightsTab:e})=>(pe=e({getItems:()=>v,getLotStats:()=>V,getBinColors:()=>c.binColors??ue(),getRingCount:et,defaultView:p.insights?.defaultView,onOpenGuide:void 0,showMetadataStrip:!1,onSaveImage:Pe.onSaveImage,onSaveText:Pe.onSaveText,ownerDocument:f.ownerDocument,openWafer:(t,n,o)=>{const d=v[t];if(!d)return;let a=null;const l=qo({title:n,onClose:()=>a?.destroy(),anchor:f});rn(l,n,d.wafer.metadata??void 0),a=gt(l.contentWrap,d,o)}}),Ee=pe.el,Ee.style.display="none",f.appendChild(Ee),pe)),Yt):Promise.resolve(null)}let Oe=!1;function Qt(e){H&&(Oe=e,Ee&&(Ee.style.display=e?"flex":"none"),U.style.display=e?"none":"flex",E.style.display=e?"none":"inline-flex",W.style.display="inline-flex",se&&(Ce(se,e),se.innerHTML=e?$e.wafer:$e.analysis,se.ariaLabel=e?"Back to gallery view":"Insights"),ae(),Ke(),e&&co().then(t=>{!t||!Oe||(t.el.style.display="flex",t.render())}))}H&&p.insights?.defaultOpen&&queueMicrotask(()=>Qt(!0));const uo=4,dt=240,Jt=480,fo=720,po=1.25;let me=Jt;function en(e){const t=A*2+110+2;let n=null;for(const o of e){if(o==null||!o.dies?.length)continue;const d=o.dies[0].width;if(!(d>0))continue;const a=o.wafer.radius*2*(uo/d),l=Math.ceil(a+t);(n==null||l>n)&&(n=l)}return n}function mo(e){const t=en(e);return t==null?dt:Math.min(me,Math.max(dt,t))}function go(e){const t=en(e);if(t==null)return!1;const n=Math.min(fo,Math.max(Jt,t));return n<=me?!1:(me=n,!0)}function ct(){return M!=null?"none":`${me}px`}function tn(){const e=ct();for(const t of Array.from(I.children))t.classList.contains("wmap-gallery-card")&&(t.style.maxWidth=e,t.style.maxHeight=e)}let Ge=dt,nn=0;function on(e){if(go(e)&&tn(),M==null){const n=mo(e);n>Ge&&(Ge=n)}ft()}function ln(e){M=Ln(e),tn(),ft()}function ut(e){return`repeat(${e}, minmax(0, ${M!=null?"1fr":`${me}px`}))`}function an(e){I.style.gridTemplateColumns!==e&&(I.style.gridTemplateColumns=e)}function ft(){if(M!=null){an(ut(M));return}const e=Math.max(1,nn),t=12,n=I.clientWidth||0,o=a=>(n-t*(a-1))/a;let d=Math.max(1,Math.ceil(Math.sqrt(e)));if(n>0){const a=Math.min(me,Ge*po),l=r=>{let i=0;for(;i<e&&o(i+1)>=r;)i++;return i};d=l(a)||l(Ge)||1}an(ut(d))}const I=f.ownerDocument.createElement("div");if(Object.assign(I.style,{flex:"1 1 0",minWidth:"0",display:"grid",gridTemplateColumns:ut(1),gap:w.xl,paddingLeft:ie,paddingRight:ie,justifyContent:"start",alignContent:"start",isolation:"isolate"}),j?.placement){const e=j.placement;b=St(e,ie,f.ownerDocument),b.style.maxHeight="calc(100vh - 80px)",b.style.position="sticky",b.style.top="8px",b.style.display="flex",b.style.flexDirection="column"}else if(V||tt()){const e=!!j?.defaultOpen;b=St("right",ie,f.ownerDocument),b.style.maxHeight="calc(100vh - 80px)",b.style.position="sticky",b.style.top="8px",b.style.flexDirection="column",b.style.display=e?"flex":"none",z()}ae(),b?.style.display!=="none"&&P&&Ce(P,!0),(j?.placement??"right")==="left"?(b&&U.appendChild(b),U.appendChild(I)):(U.appendChild(I),b&&U.appendChild(b));const Te=f.ownerDocument.createElement("div");Object.assign(Te.style,{position:"sticky",top:"0",display:"flex",flexDirection:"column",paddingLeft:ie,paddingRight:ie,paddingTop:ie,zIndex:"1",background:h.menuBg}),_e.appendChild(fe),_e.appendChild(W),Te.appendChild(_e),Te.appendChild(te),f.appendChild(Te),f.appendChild(U);function Ke(){te.innerHTML="";const e=c.plotMode??"hardBin",t=!Oe&&gn.has(e),n=R.filter(g=>g!=null),o=n.find(g=>g.isLotStack),d=Mi(n.map(g=>({metadata:g.wafer.metadata??void 0})),o?{lotSize:o.lotSize??n.length,aggrMethod:o.aggrMethod}:void 0),a=e==="metadata",l=c.activeMetadataKey,r=t&&!a?Rn(n,g=>(e==="softBin"?g.sbin:g.hbin)??void 0,Wn):{counts:new Map,total:0,pass:0},i=t&&!a?c.binColors??ue():void 0,s=i?e==="softBin"?i.pass.soft:i.pass.hard:new Set,u=pi(r.counts.entries(),s).map(([g])=>g),m=t&&a&&l?Rn(n,g=>Bo(g.metadata?.[l])):{counts:new Map,total:0,pass:0},k=a?Vt()?.values:void 0,B=t&&a?(k??[...m.counts.keys()].sort(Ct)).filter(g=>m.counts.has(g)):[];if(fe.innerHTML="",d&&fe.appendChild(d),fe.style.display=d?"flex":"none",!u.length&&!B.length){te.style.display="none";return}te.style.display="flex";const C=f.ownerDocument.createElement("div");Object.assign(C.style,{display:"flex",flexWrap:"wrap",alignItems:"center",gap:`${w.sm} ${w.xl}`,maxHeight:"86px",overflowY:"auto",overflowX:"hidden"}),te.appendChild(C);const y=c.highlightBin,O=R.flatMap(g=>g?.metadataFields??[]).find(g=>g.key===l),N=a?O?.label??(l?Ei(l):"Metadata"):jo[e],X=f.ownerDocument.createElement("span");if(X.textContent=N,Object.assign(X.style,{fontSize:L.body,fontWeight:"700",letterSpacing:Po,textTransform:"uppercase",color:h.label,flexShrink:"0"}),C.appendChild(X),!a&&r.total>0){const g=r.pass,F=g/r.total*100,ne=f.ownerDocument.createElement("span");ne.textContent=`Yield ${Je(F)}`,Object.assign(ne.style,{fontSize:L.body,fontWeight:"700",color:h.value,whiteSpace:"nowrap",flexShrink:"0"});const oe=f.ownerDocument.createElement("span");oe.textContent=`${g.toLocaleString()} / ${r.total.toLocaleString()} dies`,Object.assign(oe.style,{fontSize:L.body,color:h.label,whiteSpace:"nowrap",flexShrink:"0"}),C.appendChild(ne),C.appendChild(oe)}if(a){const g=c.highlightMetadataValue;B.forEach((F,ne)=>{const oe=g===F,Ue=O?.values?.find(wt=>wt.value===F),Co=Ue?.color??Io(ne),pn=m.counts.get(F)??0;$n(C,{color:Co,isActive:oe,label:Ue?.label??F,count:pn,percent:m.total>0?pn/m.total*100:void 0,onClick:()=>{const wt=c.highlightMetadataValue===F?void 0:F;T({highlightMetadataValue:wt})}})});return}const ht=e==="softBin"?R.flatMap(g=>g?.sbinDefs??[]):R.flatMap(g=>g?.hbinDefs??[]),cn=new Set,bt=[];for(const g of ht)cn.has(g.bin)||(cn.add(g.bin),bt.push(g));const yt=bt.length>0?new Map(bt.map(g=>[g.bin,g])):null,un=i??c.binColors??ue(),xo=e==="softBin"?un.soft:un.hard,fn=g=>xo.get(g)??Wo;for(const g of u){const F=y===g,ne=yt?.get(g),oe=r.counts.get(g)??0;$n(C,{color:fn(g),isActive:F,label:ne?.name?`${g} \xB7 ${ne.name}`:`Bin ${g}`,count:oe,percent:r.total>0?oe/r.total*100:void 0,onClick:()=>{const Ue=c.highlightBin===g?void 0:g;T({highlightBin:Ue})}})}r.total>0&&u.length>1&&te.appendChild($i(f.ownerDocument,[...r.counts.entries()].sort(([,g],[,F])=>F-g).map(([g,F])=>({color:fn(g),count:F,label:yt?.get(g)?.name?`${g} \xB7 ${yt.get(g).name}`:`Bin ${g}`})),r.total))}function Le(e){const t=v.filter(r=>r!==null);if(!t.length)return[];const n=t.map(r=>r.dies.filter(Li)),o=t[0].wafer,d=t.length,a=o.metadata?{...o,metadata:(({wafer:r,waferId:i,...s})=>s)(o.metadata)}:o;function l(r,i){return{...r,stats:{...r.stats,isLotStack:!0,aggregationMethod:i,lotSize:d}}}if(e==="stackedValues"){const r=we();let i=r?.filter(xt);if(r!==void 0&&i?.length===0)return[];if(r?.length||(i=So(t.flatMap(m=>m.dies)).map(m=>({testNumber:m,name:`Test ${m}`}))),!i?.length)return[];const s=c.aggregationMethod??"mean";return i.map(u=>{const m=gi(n,s,u.testNumber),k={testNumber:0,name:u.name,unit:u.unit};return{wafer:a,dies:m,testDefs:[k],label:`${u.name} \xB7 ${s}`,isLotStack:!0,aggrMethod:s,lotSize:d,statsSummary:l(Qe({wafer:a,dies:m,testDefs:[k]},{testNumbers:[0]}),s)}})}if(e==="stackedBins"){let r=Xe(t.map(i=>i?.hbinDefs));return(!r||r.length===0)&&(r=[...new Set(t.flatMap(s=>s.dies.map(u=>u.hbin).filter(u=>u!=null)))].sort((s,u)=>s-u).map(s=>({bin:s,name:`Bin ${s}`}))),r.map(i=>{const s=Mn(n,i.bin,"hard"),u=[{bin:i.bin,name:i.name}];return{wafer:a,dies:s,hbinDefs:u,label:`${i.bin} \xB7 ${i.name}`,isLotStack:!0,aggrMethod:"countBin",lotSize:d,statsSummary:l(Qe({wafer:a,dies:s,hbinDefs:u}),"countBin")}})}if(e==="stackedSoftBins"){let r=Xe(t.map(i=>i?.sbinDefs));return(!r||r.length===0)&&(r=[...new Set(t.flatMap(s=>s.dies.map(u=>u.sbin).filter(u=>u!=null)))].sort((s,u)=>s-u).map(s=>({bin:s,name:`Bin ${s}`}))),r.map(i=>{const s=Mn(n,i.bin,"soft"),u=[{bin:i.bin,name:i.name}];return{wafer:a,dies:s,sbinDefs:u,label:`${i.bin} \xB7 ${i.name}`,isLotStack:!0,aggrMethod:"countBin",lotSize:d,statsSummary:l(Qe({wafer:a,dies:s,sbinDefs:u}),"countBin")}})}return t}function pt(e){const t=v.length;return e==="stackedBins"||e==="stackedSoftBins"?{valueRange:[0,t],lotSize:t}:e==="stackedValues"?{aggregationMethod:c.aggregationMethod??"mean",valueRange:void 0,lotSize:void 0}:{}}function T(e,{fireCallback:t=!0}={}){const n=Object.keys(e),o=c.plotMode,d=ye()!==null;c={...c,...e};const a=c.plotMode,l=he.has(a),r=o!==void 0&&he.has(o),i=Fe>0;if("binColorScheme"in e||"useDefinedBinColors"in e){const u=ue();c={...c,binColors:u},e={...e,binColors:u}}if(e.plotMode!==void 0)if(l){const u=pt(a);if(c={...c,...u},i)for(const m of S)m&&m.setOptions(e);else Y(Le(a))}else if(r){const{valueRange:u,lotSize:m,aggregationMethod:k,...B}=c;if(c=B,i)for(const C of S)C&&C.setOptions(e);else Y(v.filter(C=>C!==null))}else for(const u of S)u&&u.setOptions(e);else if(e.aggregationMethod!==void 0&&a==="stackedValues")if(i)for(const u of S)u&&u.setOptions(e);else Y(Le("stackedValues"));else for(const u of S)u&&u.setOptions(e);ye()!==null!==d&&kt(),Ke(),Kt(),_t(),Ht();const s=e.plotMode!==void 0&&e.plotMode!==o;(yi.some(u=>u in e)||s)&&z(),Be(),qt(),Ut(),at(),lt(),t&&p.onViewOptionsChange?.(kn(c),n,bi(n))}function sn(e,t,n){const o=e.createElement("div");Object.assign(o.style,{display:"flex",alignItems:"center",gap:w.xs,flex:"1",minWidth:"0"});const d=e.createElement("span");d.textContent=t,Object.assign(d.style,{fontWeight:"700",fontSize:L.sub,overflow:"hidden",textOverflow:"ellipsis",whiteSpace:"nowrap"}),o.appendChild(d);const a=Tn(n??{});let l=null;if(a.length>0){const r=e.createElement("span");Object.assign(r.style,{fontSize:L.body,lineHeight:Ze.none,color:h.label,flexShrink:"0"}),r.textContent="\u25BE",o.appendChild(r),Object.assign(o.style,{cursor:"pointer",borderRadius:_.control}),o.addEventListener("mouseenter",()=>{o.style.background=h.bgHover}),o.addEventListener("mouseleave",()=>{o.style.background="none"}),l=e.createElement("div"),l.dataset.wmapCardMetaPanel="1",Object.assign(l.style,{position:"absolute",top:"0",left:"0",right:"0",zIndex:hn,background:h.menuBg,borderBottom:`1px solid ${h.menuBorder}`,boxShadow:We.menu,padding:`${w.md} ${w.lg}`,fontSize:L.body,display:"none"});const i=On(n??{});i&&l.appendChild(i),o.setAttribute("aria-expanded","false"),o.setAttribute("aria-label",`Wafer info for ${t||"this card"}. Click to expand.`),xn(o,s=>{r.textContent=s?"\u25B4":"\u25BE",o.setAttribute("aria-expanded",String(s)),l&&(l.style.display=s?"block":"none")})}return{wrap:o,metaPanel:l}}function rn(e,t,n){const o=e.box.querySelector("[data-wmap-window-title]"),d=Tn(n??{});if(!o||d.length===0)return;const a=o.parentElement,l=f.ownerDocument.createElement("div");Object.assign(l.style,{display:"flex",alignItems:"center",gap:w.xs,flex:"1",minWidth:"0",cursor:"pointer"}),vt(l,"bare"),a?.insertBefore(l,o),l.appendChild(o);const r=f.ownerDocument.createElement("span");Object.assign(r.style,{fontSize:L.body,lineHeight:Ze.none,color:h.label,flexShrink:"0"}),r.textContent="\u25BE",l.appendChild(r);const i=f.ownerDocument.createElement("div");i.dataset.wmapCardMetaPanel="1",Object.assign(i.style,{position:"absolute",top:"0",left:"0",right:"0",zIndex:hn,background:h.menuBg,borderBottom:`1px solid ${h.menuBorder}`,boxShadow:We.menu,padding:`${w.md} ${w.lg}`,fontSize:L.body,display:"none"});const s=On(n??{});s&&i.appendChild(s),e.contentWrap.style.position="relative",e.contentWrap.appendChild(i),l.setAttribute("aria-expanded","false"),l.setAttribute("aria-label",`Wafer info for ${t}. Click to expand.`),xn(l,u=>{r.textContent=u?"\u25B4":"\u25BE",l.setAttribute("aria-expanded",String(u)),i.style.display=u?"block":"none"})}function mt(e,t,n){const o=f.ownerDocument.createElement("div");o.className="wmap-gallery-card",Object.assign(o.style,{background:h.menuBg,border:`1px solid ${h.menuBorder}`,borderRadius:_.container,boxShadow:We.panel,overflow:"hidden",display:"flex",flexDirection:"column",position:"relative",aspectRatio:"1",maxWidth:ct(),maxHeight:ct()});const d=f.ownerDocument.createElement("div");Object.assign(d.style,{display:"flex",alignItems:"center",padding:`${w.md} ${w.lg} ${w.sm}`,borderBottom:`1px solid ${h.menuBorder}`,flexShrink:"0",gap:w.sm});const{wrap:a,metaPanel:l}=sn(document,Ye(e,t),e.wafer.metadata??void 0);d.appendChild(a);const r=f.ownerDocument.createElement("button");r.dataset.wmapExpandBtn="1",r.setAttribute("aria-label","Open full view"),Cn(r),r.innerHTML=$e.expand,Object.assign(r.style,{display:"flex",alignItems:"center",justifyContent:"center",border:`1px solid ${h.menuBorder}`,borderRadius:_.control,background:h.panelBg,color:h.label,padding:w.xxs,cursor:"pointer",flexShrink:"0",width:"22px",height:"22px"}),vt(r),d.appendChild(r),o.appendChild(d);const i=f.ownerDocument.createElement("div");Object.assign(i.style,{position:"relative",flex:"1",minHeight:"0",overflow:"hidden",display:"flex",flexDirection:"column"}),o.appendChild(i),l&&i.appendChild(l),I.appendChild(o);const s=Ae||ye()!==null,u=e.viewOptions?{...c,...e.viewOptions}:c,m=Dn(i,e,{viewOptions:{...u,showLegend:s,legendPosition:be},showTooltip:!0,padding:A,statsSummary:e.statsSummary,onSaveImage:p.onSaveImage,onSaveText:p.onSaveText,onClick:e.onClick,onSelect:e.onSelect,onExpand:()=>Re(t,e),showIdentity:!1,chromeInset:Ho});return m.setViewControlsVisible(!1),m.setSummaryVisible(!1),r.onclick=()=>Re(t,e),{card:o,ctrl:m,canvasWrapper:i,expandBtn:r}}function ho(e){const t=re[e];if(!t)return;const n=[...G.values()].find(o=>o.cardIndex===e);n?(t.innerHTML=$e.minimize,t.setAttribute("aria-label","Reattach to gallery"),t.onclick=()=>yo(n.id)):(t.innerHTML=$e.expand,t.setAttribute("aria-label","Open full view"),t.onclick=()=>Re(e,R[e]))}function Y(e){wo(),Se()?.remove(),Me(null),ot(),R=[];for(const i of S)i&&i.destroy();S=[],le=[],re=[],Fe=0,I.innerHTML="",nn=e.length,on(e.map(i=>typeof i=="function"?null:i));const t=[];for(let i=0;i<e.length;i++){const s=e[i];if(typeof s=="function"){const u=f.ownerDocument.createElement("div");u.className="wmap-gallery-card",Object.assign(u.style,{background:h.menuBg,border:`1px solid ${h.menuBorder}`,borderRadius:_.container,aspectRatio:"1",display:"flex",alignItems:"center",justifyContent:"center"});const m=f.ownerDocument.createElement("span");m.textContent="\u2026",Object.assign(m.style,{color:h.label,fontSize:"18px"}),u.appendChild(m),I.appendChild(u),R.push(null),S.push(null),le.push(null),re.push(null),t.push({index:i,factory:s,placeholder:u}),Fe++}else{const{ctrl:u,canvasWrapper:m,expandBtn:k}=mt(s,i,e.length);R.push(s),S.push(u),le.push(m),re.push(k)}}Nt(),Ke(),at(),lt();const n=++ve,o=()=>{setTimeout(()=>{n===ve&&p.onItemsResolved?.()},0)},d=i=>{setTimeout(()=>{n===ve&&p.onItemResolved?.(i,e.length)},0)};let a=e.length-t.length;a>0&&d(a);let l=0;function r(){if(n!==ve)return;if(l>=t.length){if(he.has(c.plotMode)){const O=c.plotMode;Y(Le(O));return}De=o,z(),ce||(De=null,o());return}const{index:i,factory:s,placeholder:u}=t[l++],m=s();R[i]=m,v[i]=m,on([m]);const{card:k,ctrl:B,canvasWrapper:C,expandBtn:y}=mt(m,i,e.length);S[i]=B,le[i]=C,re[i]=y,Fe--,u.replaceWith(k),d(++a),eo(),Jn(),!b&&!j?.placement&&m.statsSummary?.findings.length&&(b=St("right",ie,f.ownerDocument),b.style.maxHeight="calc(100vh - 80px)",b.style.position="sticky",b.style.top="8px",b.style.flexDirection="column",b.style.display="none",(j?.placement??"right")==="left"?U.insertBefore(b,I):U.appendChild(b),z(),P||(P=J("findings","Summary panel",()=>{if(!b)return;const N=b.style.display!=="none";N||z(),b.style.display=N?"none":"flex",Ce(P,!N),ae()}),W.appendChild(K()),W.appendChild(P)),ae()),ae(),setTimeout(r,0)}t.length>0?setTimeout(r,0):o()}if(he.has(c.plotMode)&&v.length>0){const e=pt(c.plotMode);c={...c,...e},Y(Le(c.plotMode))}else Y(x);Be();let ge=0;const dn=new(f.ownerDocument.defaultView??window).ResizeObserver(()=>{if(ge)return;ge=(f.ownerDocument.defaultView??window).requestAnimationFrame(()=>{ge=0,M==null&&ft()})});dn.observe(I),b&&z();function bo(e,t){const n=le[e];if(!n)return;n.innerHTML="",Object.assign(n.style,{alignItems:"center",justifyContent:"center"});const o=f.ownerDocument.createElement("span");o.textContent=t?"Opened in its own window":"Opened in the wafer viewer",Object.assign(o.style,{color:h.label,fontSize:L.body,textAlign:"center",padding:"0 12px"}),n.appendChild(o)}function Re(e,t){for(const l of G.values())if(l.cardIndex===e)return;const n=Ye(t,e),o=An++,d=S[e]?.getOptions(),a=Go(n);if(a){const l=a.document;l.title=n,Object.assign(l.documentElement.style,{height:"100%"}),Object.assign(l.body.style,{margin:"0",height:"100vh",overflow:"hidden",display:"flex",flexDirection:"column",fontFamily:L.family}),Xo(f,l.documentElement);const r=Zo(f,l.documentElement,()=>C.setOptions({})),i=l.createElement("div");i.dataset.wmapWindowBanner="1",i.textContent=n,Object.assign(i.style,{display:"none",padding:`${w.sm} ${w.xl}`,fontSize:L.body,fontWeight:"700",background:h.warnBg,color:h.warnText,borderBottom:`1px solid ${h.warnBorder}`,flexShrink:"0"}),l.body.appendChild(i);const s=l.createElement("div");Object.assign(s.style,{flex:"1",minHeight:"0",display:"flex",flexDirection:"column"}),l.body.appendChild(s);const{wrap:u,metaPanel:m}=sn(l,n,t.wafer.metadata??void 0),k=l.createElement("div");Object.assign(k.style,{display:"flex",alignItems:"center",padding:`${w.md} ${w.lg} ${w.sm}`,borderBottom:`1px solid ${h.menuBorder}`,flexShrink:"0",gap:w.sm}),k.appendChild(u),s.appendChild(k);const B=l.createElement("div");Object.assign(B.style,{position:"relative",flex:"1",minHeight:"0",overflow:"hidden",display:"flex",flexDirection:"column"}),m&&B.appendChild(m),s.appendChild(B);const C=gt(B,t,void 0,d),y=setInterval(()=>{a.closed&&qe(o)},400);a.addEventListener("pagehide",()=>qe(o)),G.set(o,{id:o,ctrl:C,cardIndex:e,label:n,closePollId:y,stopThemeSync:r,close:()=>{a.closed||a.close()},setTitle:O=>{l.title=O,i.textContent=O,i.style.display="block"}})}else{const l=Ko({title:n,onClose:()=>qe(o),anchor:f});l.contentWrap.style.flexDirection="column",rn(l,n,t.wafer.metadata??void 0);const r=gt(l.contentWrap,t,void 0,d);G.set(o,{id:o,ctrl:r,cardIndex:e,label:n,closePollId:null,close:()=>l.close(),setTitle:i=>{const s=l.box.querySelector("[data-wmap-window-title]");s&&(s.textContent=i,s.title=i)}})}S[e]?.destroy(),S[e]=null,bo(e,a!==null),ho(e)}function gt(e,t,n,o){const d=t.viewOptions?{...c,...t.viewOptions}:c,a=o?{...d,...o}:d,r={...n!==void 0?{...a,plotMode:"value",activeTest:n}:a,showLegend:!0,legendPosition:be},i=Dn(e,t,{viewOptions:r,showTooltip:!0,padding:A,statsSummary:t.statsSummary,onSaveImage:p.onSaveImage,onSaveText:p.onSaveText,onClick:t.onClick,onSelect:t.onSelect,showExpandButton:!1,showIdentity:!1});return i.setViewControlsVisible(!0),i.setSummaryVisible(!0),i.setExpandVisible(!1),i}function qe(e){const t=G.get(e);if(!t||(G.delete(e),t.closePollId!=null&&clearInterval(t.closePollId),t.stopThemeSync?.(),t.ctrl.destroy(),t.cardIndex===null))return;const n=t.cardIndex,o=R[n];if(!o)return;const{card:d,ctrl:a,canvasWrapper:l,expandBtn:r}=mt(o,n,R.length);le[n]?.parentElement?.replaceWith(d),S[n]=a,le[n]=l,re[n]=r}function yo(e){const t=G.get(e);t&&(t.close(),qe(e))}function wo(){for(const e of G.values())e.cardIndex!==null&&(e.cardIndex=null,e.setTitle(`${e.label} \u2014 unlinked from gallery`))}function vo(){const e=[...I.querySelectorAll("canvas")];if(!e.length)return;const t=e.length,n=Math.ceil(Math.sqrt(t)),o=Math.ceil(t/n),d=e[0].width,a=e[0].height,l=8,r=(f.ownerDocument.defaultView??window).devicePixelRatio||1,i=Math.round(26*r),s=Math.round(12*r),u=f.ownerDocument.createElement("canvas");u.width=n*d+(n-1)*l,u.height=o*(a+i)+(o-1)*l;const m=u.getContext("2d"),k=Ao(I);m.fillStyle=k.background,m.fillRect(0,0,u.width,u.height),e.forEach((B,C)=>{const y=C%n,O=Math.floor(C/n),N=y*(d+l),X=O*(a+i+l),ht=B.closest(".wmap-gallery-card")?.querySelector("span")?.textContent??"";m.fillStyle=k.surface,m.fillRect(N,X,d,i),m.fillStyle=k.text,m.font=`700 ${s}px system-ui, sans-serif`,m.textAlign="left",m.textBaseline="middle",m.fillText(ht,N+Math.round(10*r),X+i/2,d-Math.round(20*r)),m.drawImage(B,N,X+i)}),u.toBlob(B=>{if(!B)return;if(p.downloadFilename!=null){wn(B,p.downloadFilename,p.onSaveImage);return}const C=S.find(y=>y!=null)?.getExportTitle();wn(B,["gallery",C].filter(Boolean).join(" "),Pe.onSaveImage)})}return{setItems(e){v=e.map(n=>typeof n=="function"?null:n);const t=c.plotMode;if(he.has(t))if(e.some(o=>typeof o=="function"))Y(e);else{const o=pt(t);c={...c,...o},Y(Le(t))}else Y(e)},setOptions(e){T(e,{fireCallback:!1})},getOptions(){return kn(c)},setFindingsNotice(e){Rt=e,z()},setColumns(e){ln(e)},getBinColors(){return c.binColors??ue()},setLotStatsSummary(e){V=e,Be(),b&&z(),ae()},openUserGuide:Zt,destroy(){ve++,ce?.cancel(),ce=null,De=null;for(const e of[...G.values()])e.closePollId!=null&&clearInterval(e.closePollId),e.close(),e.ctrl.destroy();G.clear();for(const e of S)e&&e.destroy();S=[],Se()?.remove(),dn.disconnect(),ge&&((f.ownerDocument.defaultView??window).cancelAnimationFrame(ge),ge=0),f.ownerDocument.removeEventListener("click",He,!0),window.removeEventListener("blur",Lt),Q(),bn(),Te.remove(),U.remove(),b?.remove(),pe?.destroy()}}}
@@ -217,14 +217,21 @@ export interface RenderOptions extends ForwardedDrawOptions {
217
217
  showIdentity?: boolean;
218
218
  /** Optional precomputed wafer-level stats summary. Enables the summary panel toggle button in the toolbar. */
219
219
  statsSummary?: StatsSummary;
220
- /** Filename for the PNG download (without extension). Default `'wafermap'`. */
220
+ /**
221
+ * Name for the map's PNG download, without extension. When omitted, the PNG
222
+ * is named for its lot, wafer and map title (`LOT123_W05_hard-bin.png`); CSV
223
+ * exports are always named that way. See docs/api.md §5.4.5.
224
+ *
225
+ * **Changes in 0.31.0:** this becomes a prefix for every file the map saves,
226
+ * with the lot, wafer and content appended. Passing it logs a one-time notice.
227
+ */
221
228
  downloadFilename?: string;
222
229
  /**
223
230
  * Host hook for persisting the rendered PNG. When provided, the toolbar's save
224
231
  * action calls `onSaveImage(blob, suggestedName)` instead of triggering a
225
232
  * browser `<a download>` — letting embedded hosts (Tauri, Electron, WebView2)
226
233
  * route the image through a native save dialog. `suggestedName` includes the
227
- * `.png` extension and is derived from `downloadFilename`. When omitted, the
234
+ * `.png` extension and is the generated name (see `downloadFilename`). When omitted, the
228
235
  * default browser download behaviour is unchanged.
229
236
  */
230
237
  onSaveImage?: SaveImageHandler;
@@ -350,6 +357,19 @@ export interface WaferMapController {
350
357
  resetZoom(): void;
351
358
  /** Replace the current stats summary used by the built-in Summary panel. */
352
359
  setStatsSummary(summary: StatsSummary | undefined): void;
360
+ /**
361
+ * Close the Summary panel if it is open; no-op if it is closed or the map has
362
+ * none. For a host that needs the map's full width back — loading a new file,
363
+ * opening its own dialog, moving to a narrower layout. The user reopens it from
364
+ * the toolbar's Summary button.
365
+ */
366
+ closeSummaryPanel(): void;
367
+ /**
368
+ * The bin colours this map draws, with the palette currently chosen — for a
369
+ * host surface of its own (a table swatch, an export). Inside a gallery these
370
+ * are the gallery-wide colours. Hard and soft bins are separate maps.
371
+ */
372
+ getBinColors(): BinColors;
353
373
  /**
354
374
  * Opens the built-in end-user guide window — the same action the help
355
375
  * toolbar button performs, but callable directly. Works regardless of
@@ -439,6 +459,8 @@ export interface CardController extends WaferMapController {
439
459
  setViewControlsVisible(visible: boolean): void;
440
460
  /** Show or hide the expand toolbar button. */
441
461
  setExpandVisible(visible: boolean): void;
462
+ /** The map's title as a file-name content part — the gallery names its composite PNG by it. */
463
+ getExportTitle(): string;
442
464
  }
443
465
  /** @internal The public view of a card's options: everything but the gallery's shared state. */
444
466
  export declare function toPublicViewOptions(opts: CardViewOptions): WaferViewOptions;