@wafertools/wafermap 0.29.0 → 0.30.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (88) hide show
  1. package/AGENTS.md +26 -17
  2. package/CHANGELOG.md +410 -0
  3. package/README.md +6 -6
  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/correlation.js +2 -2
  9. package/dist/packages/canvas-adapter/charts/groupedBarPlot.d.ts +1 -0
  10. package/dist/packages/canvas-adapter/charts/histogram.js +1 -1
  11. package/dist/packages/canvas-adapter/charts/testPassRate.js +1 -1
  12. package/dist/packages/canvas-adapter/charts/trend.js +1 -1
  13. package/dist/packages/canvas-adapter/deprecated.d.ts +7 -0
  14. package/dist/packages/canvas-adapter/deprecated.js +1 -0
  15. package/dist/packages/canvas-adapter/dieList.d.ts +1 -3
  16. package/dist/packages/canvas-adapter/dieList.js +7 -7
  17. package/dist/packages/canvas-adapter/exportName.d.ts +68 -0
  18. package/dist/packages/canvas-adapter/exportName.js +1 -0
  19. package/dist/packages/canvas-adapter/icons.js +2 -2
  20. package/dist/packages/canvas-adapter/index.d.ts +2 -3
  21. package/dist/packages/canvas-adapter/index.js +1 -1
  22. package/dist/packages/canvas-adapter/insightsTab.d.ts +0 -1
  23. package/dist/packages/canvas-adapter/insightsTab.js +1 -1
  24. package/dist/packages/canvas-adapter/maplessSummary.js +1 -1
  25. package/dist/packages/canvas-adapter/renderWaferGallery.d.ts +23 -40
  26. package/dist/packages/canvas-adapter/renderWaferGallery.js +1 -1
  27. package/dist/packages/canvas-adapter/renderWaferMap.d.ts +96 -104
  28. package/dist/packages/canvas-adapter/renderWaferMap.js +1 -1
  29. package/dist/packages/canvas-adapter/summaryPanel.d.ts +31 -13
  30. package/dist/packages/canvas-adapter/summaryPanel.js +3 -3
  31. package/dist/packages/canvas-adapter/toCanvas.d.ts +20 -12
  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 +26 -15
  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/deprecated.d.ts +60 -0
  41. package/dist/packages/core/deprecated.js +1 -0
  42. package/dist/packages/core/index.d.ts +1 -3
  43. package/dist/packages/core/index.js +1 -1
  44. package/dist/packages/core/passBins.d.ts +33 -0
  45. package/dist/packages/core/passBins.js +1 -0
  46. package/dist/packages/core/utils.d.ts +12 -0
  47. package/dist/packages/core/utils.js +1 -1
  48. package/dist/packages/core/waferLabel.d.ts +35 -0
  49. package/dist/packages/core/waferLabel.js +1 -0
  50. package/dist/packages/renderer/binColors.d.ts +105 -13
  51. package/dist/packages/renderer/binColors.js +1 -1
  52. package/dist/packages/renderer/buildView.d.ts +8 -9
  53. package/dist/packages/renderer/buildView.js +1 -1
  54. package/dist/packages/renderer/buildWaferMap.d.ts +76 -17
  55. package/dist/packages/renderer/buildWaferMap.js +1 -1
  56. package/dist/packages/renderer/colorMap.d.ts +4 -3
  57. package/dist/packages/renderer/colorSchemes.d.ts +8 -6
  58. package/dist/packages/renderer/deprecate.d.ts +31 -0
  59. package/dist/packages/renderer/deprecate.js +1 -0
  60. package/dist/packages/renderer/deprecated.d.ts +45 -0
  61. package/dist/packages/renderer/deprecated.js +1 -0
  62. package/dist/packages/renderer/index.d.ts +4 -4
  63. package/dist/packages/renderer/index.js +1 -1
  64. package/dist/packages/stats/analyzeWaferLot.js +1 -1
  65. package/dist/packages/stats/analyzeWaferMap.js +1 -1
  66. package/dist/packages/stats/binPareto.d.ts +6 -1
  67. package/dist/packages/stats/capability.d.ts +16 -1
  68. package/dist/packages/stats/capability.js +1 -1
  69. package/dist/packages/stats/clusterDetection.d.ts +0 -2
  70. package/dist/packages/stats/deprecated.d.ts +82 -0
  71. package/dist/packages/stats/deprecated.js +1 -0
  72. package/dist/packages/stats/index.d.ts +5 -14
  73. package/dist/packages/stats/index.js +1 -1
  74. package/dist/packages/stats/normalizeInput.d.ts +10 -0
  75. package/dist/packages/stats/normalizeInput.js +1 -0
  76. package/dist/packages/stats/regions.d.ts +9 -2
  77. package/dist/packages/stats/regions.js +1 -1
  78. package/dist/packages/stats/renderFindingsReport.js +1 -1
  79. package/dist/packages/stats/renderSummaryReport.d.ts +42 -1
  80. package/dist/packages/stats/renderSummaryReport.js +34 -34
  81. package/dist/packages/stats/reportHtml.d.ts +0 -1
  82. package/dist/packages/stats/reportHtml.js +10 -10
  83. package/dist/packages/stats/summaryFigures.d.ts +47 -0
  84. package/dist/packages/stats/summaryFigures.js +1 -0
  85. package/dist/packages/stats/types.d.ts +118 -43
  86. package/dist/packages/stats/yield.d.ts +12 -6
  87. package/dist/packages/stats/yield.js +1 -1
  88. package/package.json +1 -1
@@ -72,7 +72,7 @@ export const USER_GUIDE_HTML=`<div class="wmap-guide">
72
72
  .wmap-guide-toc{display:none!important}
73
73
  }
74
74
  </style>
75
- <h1 id="wafer-map-user-guide">Wafer Map \u2014 User Guide<span class="wmap-guide-version" title="Built 2026-09-12T08:32:38.570Z">v0.29.0</span></h1>
75
+ <h1 id="wafer-map-user-guide">Wafer Map \u2014 User Guide<span class="wmap-guide-version" title="Built 2026-09-17T07:11:09.912Z">v0.30.1</span></h1>
76
76
  <p class="wmap-guide-online-link">This is a quick reference. <a href="https://wafertools.github.io/wafermap/user-guide/" target="_blank" rel="noopener">View the full illustrated guide online \u2197</a></p>
77
77
  <p>This guide describes the display and analysis features of the wafer map viewer.
78
78
  It is written for users who may be semiconductor test engineers, device engineers, and yield engineers
@@ -129,7 +129,9 @@ fail; it simply has no result recorded. Edge-excluded dies are shown dimmed and
129
129
  not counted in yield calculations.</p>
130
130
  <h3 id="14-legend-and-colorbar">1.4 Legend and colorbar</h3>
131
131
  <p><strong>Bin modes</strong> show a discrete colour legend: one swatch per bin, with bin number
132
- and name if the application has supplied bin names.</p>
132
+ and name if the application has supplied bin names. Passing bins are listed first,
133
+ then failing bins from the most dies to the fewest \u2014 the same order as the Summary
134
+ panel and reports.</p>
133
135
  <p><strong>Value mode</strong> shows a continuous colorbar: the colour scale runs from the minimum
134
136
  to maximum value, with units when available. The colorbar is informational only \u2014
135
137
  clicking it has no effect. To switch to a pass/fail view, use the <strong>Spec pass/fail</strong>
@@ -358,20 +360,23 @@ shows die tooltips \u2014 a separate thing from the toolbar, which is always pre
358
360
  </tbody></table>
359
361
  <p><strong>This guide&#39;s own window</strong> (and a gallery card detached into its own window,
360
362
  see <a href="#gallery" onclick="(function(e){e.preventDefault();var g=e.target.closest('.wmap-guide');var el=g&&g.querySelector('[id=\\'gallery\\']');if(el)el.scrollIntoView({behavior:'smooth'});})(event)">Section 3 \u2014 Gallery</a> below) is a floating window, not a modal \u2014
361
- its header shows minimize, maximize, and close buttons, and it can be dragged
362
- and resized by its corner grip. Minimizing collapses it to a small title strip
363
- without closing it; click it again to restore.</p>
363
+ its header shows collapse, maximize, and close buttons, and it can be dragged
364
+ and resized by its corner grip. Collapse shrinks it to a small title strip
365
+ without closing it; click the same button (now <strong>Show contents</strong>) to restore it.
366
+ While the window is maximized only restore and close are shown.</p>
364
367
  <p><strong>Highlight bin</strong> \u2014 in bin modes, click any bin swatch in the legend to highlight
365
368
  that bin and dim all others. Click again to clear. Useful for isolating a specific
366
369
  failure category across the wafer.</p>
367
370
  <div data-wmap-demo="bin-highlight" class="wmap-demo"></div><p><em>Bin 2 (Fail) highlighted \u2014 all other bins are dimmed.</em></p>
368
371
  <p><strong>Bin colours</strong> \u2014 a bin&#39;s colour tells you whether it passed before you read the legend:
369
372
  passing bins are always shades of green and failing bins never are, whichever bin numbers
370
- your test program uses. The bins with the most dies get the most distinct colours. In a
371
- gallery the colours are worked out across every wafer it shows, so a bin is the same
372
- colour on every card. If there are more bins than the colour scheme has distinct colours, some bins
373
- have to share one \u2014 a <strong>Data warnings</strong> entry names them, and you can tell them apart with
374
- the legend, the tooltip, or by highlighting one bin at a time.</p>
373
+ your test program uses. Each bin number keeps its colour: bin 7 is the same colour in every
374
+ lot, every gallery and every screenshot, however many dies it holds, so you can learn your
375
+ program&#39;s colours. Hard and soft bins are coloured separately, so hard bin 3 and soft bin 3
376
+ are different colours. A colour scheme has a limited number of colours, so bins far enough
377
+ apart in number repeat one \u2014 when two of them are on screen together a <strong>Data warnings</strong>
378
+ entry names them, and you can tell them apart with the legend, the tooltip, or by
379
+ highlighting one bin at a time.</p>
375
380
  <h3 id="overlays">Overlays</h3>
376
381
  <p>Use the <strong>Overlays</strong> menu to toggle optional display layers on and off:</p>
377
382
  <ul>
@@ -462,7 +467,7 @@ the legend, the tooltip, or by highlighting one bin at a time.</p>
462
467
  <tr>
463
468
  <td><img src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCIgd2lkdGg9IjE2IiBoZWlnaHQ9IjE2IiBmaWxsPSJub25lIiBzdHJva2U9IiMzNzQxNTEiIHN0cm9rZS13aWR0aD0iMS44IiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiPgogIDxyZWN0IHg9IjMiIHk9IjMiIHdpZHRoPSI0IiBoZWlnaHQ9IjE4IiByeD0iMSIvPgogIDxyZWN0IHg9IjEwIiB5PSIzIiB3aWR0aD0iNCIgaGVpZ2h0PSIxOCIgcng9IjEiLz4KICA8cmVjdCB4PSIxNyIgeT0iMyIgd2lkdGg9IjQiIGhlaWdodD0iMTgiIHJ4PSIxIi8+Cjwvc3ZnPgo=" width="20" height="20"></td>
464
469
  <td>Columns</td>
465
- <td>Sets the number of columns in the card grid.</td>
470
+ <td>Sets the number of columns. <strong>Auto</strong> fits as many cards as stay readable; a number divides the full width between that many columns.</td>
466
471
  </tr>
467
472
  <tr>
468
473
  <td><img src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCIgd2lkdGg9IjE2IiBoZWlnaHQ9IjE2IiBmaWxsPSJub25lIiBzdHJva2U9IiMzNzQxNTEiIHN0cm9rZS13aWR0aD0iMS44IiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiPjxwYXRoIGQ9Ik0xMy45OTcgNGEyIDIgMCAwIDEgMS43NiAxLjA1bC40ODYuOUEyIDIgMCAwIDAgMTguMDAzIDdIMjBhMiAyIDAgMCAxIDIgMnY5YTIgMiAwIDAgMS0yIDJINGEyIDIgMCAwIDEtMi0yVjlhMiAyIDAgMCAxIDItMmgxLjk5N2EyIDIgMCAwIDAgMS43NTktMS4wNDhsLjQ4OS0uOTA0QTIgMiAwIDAgMSAxMC4wMDQgNHoiLz48Y2lyY2xlIGN4PSIxMiIgY3k9IjEzIiByPSIzIi8+PGNpcmNsZSBjeD0iNS41IiBjeT0iNC41IiByPSIwLjgiIGZpbGw9IiMzNzQxNTEiIHN0cm9rZT0ibm9uZSIvPjxjaXJjbGUgY3g9IjgiIGN5PSI0LjUiIHI9IjAuOCIgZmlsbD0iIzM3NDE1MSIgc3Ryb2tlPSJub25lIi8+PGNpcmNsZSBjeD0iNS41IiBjeT0iNyIgcj0iMC44IiBmaWxsPSIjMzc0MTUxIiBzdHJva2U9Im5vbmUiLz48Y2lyY2xlIGN4PSI4IiBjeT0iNyIgcj0iMC44IiBmaWxsPSIjMzc0MTUxIiBzdHJva2U9Im5vbmUiLz48L3N2Zz4K" width="20" height="20"></td>
@@ -482,7 +487,7 @@ the legend, the tooltip, or by highlighting one bin at a time.</p>
482
487
  <tr>
483
488
  <td><img src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCIgd2lkdGg9IjE2IiBoZWlnaHQ9IjE2IiBmaWxsPSJub25lIiBzdHJva2U9IiMzNzQxNTEiIHN0cm9rZS13aWR0aD0iMS44IiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiPjxyZWN0IHdpZHRoPSI3IiBoZWlnaHQ9IjciIHg9IjMiIHk9IjMiIHJ4PSIxIi8+PHJlY3Qgd2lkdGg9IjciIGhlaWdodD0iNyIgeD0iMyIgeT0iMTQiIHJ4PSIxIi8+PHBhdGggZD0iTTE0IDRoNyIvPjxwYXRoIGQ9Ik0xNCA5aDciLz48cGF0aCBkPSJNMTQgMTVoNyIvPjxwYXRoIGQ9Ik0xNCAyMGg3Ii8+PC9zdmc+Cg==" width="20" height="20"></td>
484
489
  <td>Legend style</td>
485
- <td>Bin modes only. Sets where the bin legend sits relative to each card.</td>
490
+ <td><strong>Legend on each map</strong> adds a legend to every card as well as the shared one above the grid (off by default). While it is on, bin modes also let you choose where that legend sits on each card.</td>
486
491
  </tr>
487
492
  <tr>
488
493
  <td><img src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCIgd2lkdGg9IjE2IiBoZWlnaHQ9IjE2IiBmaWxsPSJub25lIiBzdHJva2U9IiMzNzQxNTEiIHN0cm9rZS13aWR0aD0iMS44IiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiPjxwYXRoIGQ9Ik01IDQgTDUgMjAiLz48cGF0aCBkPSJNNSA0IEw4IDQiLz48cGF0aCBkPSJNNSAyMCBMOCAyMCIvPjxwYXRoIGQ9Ik0xOSA0IEwxOSAyMCIvPjxwYXRoIGQ9Ik0xOSA0IEwxNiA0Ii8+PHBhdGggZD0iTTE5IDIwIEwxNiAyMCIvPjxwYXRoIGQ9Ik01IDEyIEwxOSAxMiIgc3Ryb2tlLWRhc2hhcnJheT0iMyAyIi8+PC9zdmc+Cg==" width="20" height="20"></td>
@@ -829,9 +834,15 @@ onto that pair.</em></p>
829
834
  <p>Every chart panel has a <strong>camera</strong> button that saves the current view as a PNG at the displayed
830
835
  resolution. To get a clean full-resolution render, use the panel&#39;s expand (corner-arrows)
831
836
  button first to open it in the fullscreen modal, then click the camera button.</p>
832
- <p>Each exported PNG includes a header strip above the chart with the panel title, source
833
- filename, wafer and die counts, the active test name (where applicable), and the time of
834
- export. The live card is unchanged \u2014 the header appears only in the saved file.</p>
837
+ <p>The saved image is the chart as shown, on a white background. It carries no title or
838
+ context of its own, but its file name identifies the lot, wafers and chart (see below).</p>
839
+ <h3 id="names-of-saved-files">Names of saved files</h3>
840
+ <p>Every image and CSV you save is named for what it shows: the lot, then the wafer (or how many
841
+ wafers), then the content. For example, <code>LOT123_W05_hard-bin.png</code>, <code>LOT123_W05_die-list.csv</code> or
842
+ <code>LOT123_25-wafers_yield-by-wafer.png</code>. Saving the same export from different wafers gives
843
+ different names, so files don&#39;t overwrite each other or need renaming. A part is left out when
844
+ the data doesn&#39;t have it, for example a wafer with no ID. Your application may choose its own
845
+ name for map images instead.</p>
835
846
  <hr>
836
847
  <h2 id="9-reticle-overlay">9. Reticle overlay</h2>
837
848
  <p>When reticle (stepper field) geometry is configured, the <strong>Reticle grid</strong> overlay
@@ -1,3 +1,3 @@
1
- export declare const WMAP_VERSION = "0.29.0";
2
- export declare const WMAP_BUILD_TIME = "2026-09-12T08:42:54.616Z";
1
+ export declare const WMAP_VERSION = "0.30.1";
2
+ export declare const WMAP_BUILD_TIME = "2026-09-17T07:12:45.499Z";
3
3
  //# sourceMappingURL=version.d.ts.map
@@ -1 +1 @@
1
- export const WMAP_VERSION="0.29.0",WMAP_BUILD_TIME="2026-09-12T08:42:54.616Z";
1
+ export const WMAP_VERSION="0.30.1",WMAP_BUILD_TIME="2026-09-17T07:12:45.499Z";
@@ -1 +1 @@
1
- import{SHADOW as w,TRACKING as O,SPACE as s,RADIUS as E,FONT as y,CLR as d,Z_BASE as A}from"./toolbar.js";const $={error:0,warning:1,info:2};export function severityOf(t){return t.severity??"warning"}export function collectWarnings(t){const o=[],i=new Set,n=e=>{if(!e?.message)return;const r=`${e.code}\0${e.message}`;i.has(r)||(i.add(r),o.push(e))};for(const e of t.result?.warnings??[])n(e);for(const e of t.statsSummary?.stats.warnings??[])n(e);for(const e of t.lotStatsSummary?.perWafer??[])for(const r of e.summary.stats.warnings??[])n(r);for(const e of t.extra??[])n(e);return o.sort((e,r)=>$[severityOf(e)]-$[severityOf(r)])}function v(t){return t==="error"?{bg:d.errBg,border:d.errBorder,text:d.errText}:{bg:d.warnBg,border:d.warnBorder,text:d.warnText}}function C(t){return t==="error"?"\u26D4":t==="info"?"\u2139":"\u26A0"}export const SHORT_LABEL={"partial-coverage":"Partial wafer coverage","geometry-conflict":"Geometry conflict","non-standard-diameter":"Non-standard wafer diameter","diameter-exceeds-die-extent":"Diameter exceeds die extent","edge-exclusion-exceeds-radius":"Edge exclusion exceeds radius","test-count-capped":"Test analysis skipped","test-def-collision":"Test definitions disagree","analysis-option-corrected":"Analysis option corrected"};function S(t){const o=t.code?SHORT_LABEL[t.code]:void 0;if(o)return o;const i=t.message.split(/(?<=\.)\s/)[0]??t.message;return i.length>60?`${i.slice(0,57)}\u2026`:i}export function buildWarningsBanner(t,o=document){const i=o.createElement("div");Object.assign(i.style,{marginBottom:s.lg});for(const n of t){const e=severityOf(n),r=v(e),m=o.createElement("div");Object.assign(m.style,{background:r.bg,border:`1px solid ${r.border}`,borderRadius:E.control,marginBottom:s.sm,fontSize:y.body,color:r.text,lineHeight:"1.5"}),m.setAttribute("role","status");const a=o.createElement("button");a.type="button",Object.assign(a.style,{display:"flex",alignItems:"center",gap:s.sm,width:"100%",background:"none",border:"none",font:"inherit",color:"inherit",textAlign:"left",padding:`${s.sm} ${s.lg}`,cursor:"pointer"});const g=o.createElement("span");Object.assign(g.style,{flex:"1",minWidth:"0"}),g.textContent=`${C(e)} ${S(n)}`;const l=o.createElement("span");Object.assign(l.style,{flexShrink:"0"}),a.append(g,l);const p=o.createElement("div");Object.assign(p.style,{padding:`0 ${s.lg} ${s.sm}`}),p.textContent=n.message;let c=!1;const f=()=>{l.textContent=c?"\u25B4":"\u25BE",p.style.display=c?"block":"none",a.setAttribute("aria-expanded",String(c)),a.setAttribute("aria-label",`${S(n)}. ${c?"Click to collapse.":"Click to expand for detail."}`)};f(),a.addEventListener("click",()=>{c=!c,f()}),a.addEventListener("mouseenter",()=>{l.style.opacity="0.65"}),a.addEventListener("mouseleave",()=>{l.style.opacity="1"}),m.append(a,p),i.appendChild(m)}return i}export function buildWarningsMenuEl(t,o,i=window){const n=i.document,e=n.createElement("div"),r=280,a=t.left+r<=(i.innerWidth??1/0)?t.left:Math.max(4,t.right-r);Object.assign(e.style,{position:"fixed",top:`${t.bottom+4}px`,left:`${a}px`,background:d.menuBg,border:`1px solid ${d.menuBorder}`,borderRadius:E.control,boxShadow:w.menu,zIndex:A,width:`${r}px`,maxHeight:"320px",overflowY:"auto",padding:"4px 0",pointerEvents:"auto"}),e.setAttribute("role","dialog"),e.setAttribute("aria-label","Data warnings");const g=n.createElement("div");Object.assign(g.style,{padding:`${s.sm} ${s.xl} ${s.xs}`,fontSize:y.body,fontWeight:"700",letterSpacing:O,textTransform:"uppercase",color:d.label}),g.textContent=o.length===1?"1 warning":`${o.length} warnings`,e.appendChild(g);for(const l of o){const p=severityOf(l),c=v(p),f=n.createElement("div");Object.assign(f.style,{display:"flex",gap:s.md,padding:`${s.md} ${s.xl}`,fontSize:y.body,lineHeight:"1.5",color:d.text,borderTop:`1px solid ${d.menuBorder}`,alignItems:"flex-start"});const x=n.createElement("span");Object.assign(x.style,{color:c.text,flexShrink:"0"}),x.textContent=C(p),f.appendChild(x);const u=n.createElement("div"),b=n.createElement("div");Object.assign(b.style,{fontWeight:"700",color:c.text,marginBottom:s.xxs}),b.textContent=l.code,u.appendChild(b);const h=n.createElement("div");h.textContent=l.message,u.appendChild(h),f.appendChild(u),e.appendChild(f)}return e}
1
+ import{SHADOW as S,TRACKING as O,SPACE as s,RADIUS as v,FONT as y,CLR as d,Z_BASE as B}from"./toolbar.js";const E={error:0,warning:1,info:2};export function severityOf(t){return t.severity??"warning"}export function collectWarnings(t){const o=[],i=new Set,n=e=>{if(!e?.message)return;const r=`${e.code}\0${e.message}`;i.has(r)||(i.add(r),o.push(e))};for(const e of t.result?.warnings??[])n(e);for(const e of t.statsSummary?.stats.warnings??[])n(e);for(const e of t.lotStatsSummary?.perWafer??[])for(const r of e.summary.stats.warnings??[])n(r);for(const e of t.extra??[])n(e);return o.sort((e,r)=>E[severityOf(e)]-E[severityOf(r)])}function $(t){return t==="error"?{bg:d.errBg,border:d.errBorder,text:d.errText}:{bg:d.warnBg,border:d.warnBorder,text:d.warnText}}function w(t){return t==="error"?"\u26D4":t==="info"?"\u2139":"\u26A0"}export const SHORT_LABEL={"partial-coverage":"Partial wafer coverage","geometry-conflict":"Geometry conflict","non-standard-diameter":"Non-standard wafer diameter","diameter-exceeds-die-extent":"Diameter exceeds die extent","edge-exclusion-exceeds-radius":"Edge exclusion exceeds radius","test-count-capped":"Test analysis skipped","test-def-collision":"Test definitions disagree","analysis-option-corrected":"Analysis option corrected","bin-colors-shared":"Bins share a colour","pass-bins-mixed":"Pass bins differ by wafer","ring-count-mixed":"Ring counts differ by wafer","input-field-removed":"Removed input name ignored","input-values-not-numbers":"Bins or test values are text"};function C(t){const o=t.code?SHORT_LABEL[t.code]:void 0;if(o)return o;const i=t.message.split(/(?<=\.)\s/)[0]??t.message;return i.length>60?`${i.slice(0,57)}\u2026`:i}export function buildWarningsBanner(t,o=document){const i=o.createElement("div");Object.assign(i.style,{marginBottom:s.lg});for(const n of t){const e=severityOf(n),r=$(e),m=o.createElement("div");Object.assign(m.style,{background:r.bg,border:`1px solid ${r.border}`,borderRadius:v.control,marginBottom:s.sm,fontSize:y.body,color:r.text,lineHeight:"1.5"}),m.setAttribute("role","status");const a=o.createElement("button");a.type="button",Object.assign(a.style,{display:"flex",alignItems:"center",gap:s.sm,width:"100%",background:"none",border:"none",font:"inherit",color:"inherit",textAlign:"left",padding:`${s.sm} ${s.lg}`,cursor:"pointer"});const g=o.createElement("span");Object.assign(g.style,{flex:"1",minWidth:"0"}),g.textContent=`${w(e)} ${C(n)}`;const l=o.createElement("span");Object.assign(l.style,{flexShrink:"0"}),a.append(g,l);const p=o.createElement("div");Object.assign(p.style,{padding:`0 ${s.lg} ${s.sm}`}),p.textContent=n.message;let c=!1;const f=()=>{l.textContent=c?"\u25B4":"\u25BE",p.style.display=c?"block":"none",a.setAttribute("aria-expanded",String(c)),a.setAttribute("aria-label",`${C(n)}. ${c?"Click to collapse.":"Click to expand for detail."}`)};f(),a.addEventListener("click",()=>{c=!c,f()}),a.addEventListener("mouseenter",()=>{l.style.opacity="0.65"}),a.addEventListener("mouseleave",()=>{l.style.opacity="1"}),m.append(a,p),i.appendChild(m)}return i}export function buildWarningsMenuEl(t,o,i=window){const n=i.document,e=n.createElement("div"),r=280,a=t.left+r<=(i.innerWidth??1/0)?t.left:Math.max(4,t.right-r);Object.assign(e.style,{position:"fixed",top:`${t.bottom+4}px`,left:`${a}px`,background:d.menuBg,border:`1px solid ${d.menuBorder}`,borderRadius:v.control,boxShadow:S.menu,zIndex:B,width:`${r}px`,maxHeight:"320px",overflowY:"auto",padding:"4px 0",pointerEvents:"auto"}),e.setAttribute("role","dialog"),e.setAttribute("aria-label","Data warnings");const g=n.createElement("div");Object.assign(g.style,{padding:`${s.sm} ${s.xl} ${s.xs}`,fontSize:y.body,fontWeight:"700",letterSpacing:O,textTransform:"uppercase",color:d.label}),g.textContent=o.length===1?"1 warning":`${o.length} warnings`,e.appendChild(g);for(const l of o){const p=severityOf(l),c=$(p),f=n.createElement("div");Object.assign(f.style,{display:"flex",gap:s.md,padding:`${s.md} ${s.xl}`,fontSize:y.body,lineHeight:"1.5",color:d.text,borderTop:`1px solid ${d.menuBorder}`,alignItems:"flex-start"});const x=n.createElement("span");Object.assign(x.style,{color:c.text,flexShrink:"0"}),x.textContent=w(p),f.appendChild(x);const u=n.createElement("div"),b=n.createElement("div");Object.assign(b.style,{fontWeight:"700",color:c.text,marginBottom:s.xxs}),b.textContent=l.code,u.appendChild(b);const h=n.createElement("div");h.textContent=l.message,u.appendChild(h),f.appendChild(u),e.appendChild(f)}return e}
@@ -0,0 +1,60 @@
1
+ import { createWafer as createWaferImpl } from './wafer.js';
2
+ import { generateDies as generateDiesImpl, isPositionedDie as isPositionedDieImpl } from './dies.js';
3
+ import { clipDiesToWafer as clipDiesToWaferImpl, applyOrientation as applyOrientationImpl, transformDies as transformDiesImpl, mapDataToDies as mapDataToDiesImpl, isInsideWafer as isInsideWaferImpl, affineIdentity as affineIdentityImpl, affineRotation as affineRotationImpl, affineMirror as affineMirrorImpl, affineCompose as affineComposeImpl, affineInvert as affineInvertImpl, affinePoint as affinePointImpl, affineVector as affineVectorImpl, affineSwapsAxes as affineSwapsAxesImpl } from './transforms.js';
4
+ import { applyProbeSequence as applyProbeSequenceImpl } from './probe.js';
5
+ import { generateReticleGrid as generateReticleGridImpl } from './reticle.js';
6
+ import { classifyDie as classifyDieImpl, getRingLabel as getRingLabelImpl } from './classify.js';
7
+ import { aggregateValues as aggregateValuesImpl, aggregateBinCounts as aggregateBinCountsImpl, getUniqueBins as getUniqueBinsImpl } from './aggregates.js';
8
+ import { metadataCategoricalValue as metadataCategoricalValueImpl } from './metadata.js';
9
+ import { resolveGridPitch as resolveGridPitchImpl } from './inference/pitch.js';
10
+ /** @deprecated Removed in 0.31.0. For a die layout with no test data use `buildWaferMap({ layout: true, waferConfig, dieConfig })`; otherwise build with `buildWaferMap` and draw with `renderWaferMap` or `renderWaferGallery`. */
11
+ export declare const createWafer: typeof createWaferImpl;
12
+ /** @deprecated Removed in 0.31.0. For a die layout with no test data use `buildWaferMap({ layout: true, waferConfig, dieConfig })`, which keeps every site fully on the wafer. */
13
+ export declare const generateDies: typeof generateDiesImpl;
14
+ /** @deprecated Removed in 0.31.0. Use `hasPosition` to test whether a die has a position. */
15
+ export declare const isPositionedDie: typeof isPositionedDieImpl;
16
+ /** @deprecated Removed in 0.31.0. For a die layout with no test data use `buildWaferMap({ layout: true, waferConfig, dieConfig })`, which keeps every site fully on the wafer. */
17
+ export declare const clipDiesToWafer: typeof clipDiesToWaferImpl;
18
+ /** @deprecated Removed in 0.31.0. It belongs to the low-level drawing pipeline, which is being withdrawn: build with `buildWaferMap` and draw with `renderWaferMap` or `renderWaferGallery`. */
19
+ export declare const applyOrientation: typeof applyOrientationImpl;
20
+ /** @deprecated Removed in 0.31.0. It belongs to the low-level drawing pipeline, which is being withdrawn: build with `buildWaferMap` and draw with `renderWaferMap` or `renderWaferGallery`. */
21
+ export declare const transformDies: typeof transformDiesImpl;
22
+ /** @deprecated Removed in 0.31.0. `buildWaferMap` maps results onto dies. */
23
+ export declare const mapDataToDies: typeof mapDataToDiesImpl;
24
+ /** @deprecated Removed in 0.31.0. It belongs to the low-level drawing pipeline, which is being withdrawn: build with `buildWaferMap` and draw with `renderWaferMap` or `renderWaferGallery`. */
25
+ export declare const isInsideWafer: typeof isInsideWaferImpl;
26
+ /** @deprecated Removed in 0.31.0. It belongs to the low-level drawing pipeline, which is being withdrawn: build with `buildWaferMap` and draw with `renderWaferMap` or `renderWaferGallery`. */
27
+ export declare const affineIdentity: typeof affineIdentityImpl;
28
+ /** @deprecated Removed in 0.31.0. It belongs to the low-level drawing pipeline, which is being withdrawn: build with `buildWaferMap` and draw with `renderWaferMap` or `renderWaferGallery`. */
29
+ export declare const affineRotation: typeof affineRotationImpl;
30
+ /** @deprecated Removed in 0.31.0. It belongs to the low-level drawing pipeline, which is being withdrawn: build with `buildWaferMap` and draw with `renderWaferMap` or `renderWaferGallery`. */
31
+ export declare const affineMirror: typeof affineMirrorImpl;
32
+ /** @deprecated Removed in 0.31.0. It belongs to the low-level drawing pipeline, which is being withdrawn: build with `buildWaferMap` and draw with `renderWaferMap` or `renderWaferGallery`. */
33
+ export declare const affineCompose: typeof affineComposeImpl;
34
+ /** @deprecated Removed in 0.31.0. It belongs to the low-level drawing pipeline, which is being withdrawn: build with `buildWaferMap` and draw with `renderWaferMap` or `renderWaferGallery`. */
35
+ export declare const affineInvert: typeof affineInvertImpl;
36
+ /** @deprecated Removed in 0.31.0. It belongs to the low-level drawing pipeline, which is being withdrawn: build with `buildWaferMap` and draw with `renderWaferMap` or `renderWaferGallery`. */
37
+ export declare const affinePoint: typeof affinePointImpl;
38
+ /** @deprecated Removed in 0.31.0. It belongs to the low-level drawing pipeline, which is being withdrawn: build with `buildWaferMap` and draw with `renderWaferMap` or `renderWaferGallery`. */
39
+ export declare const affineVector: typeof affineVectorImpl;
40
+ /** @deprecated Removed in 0.31.0. It belongs to the low-level drawing pipeline, which is being withdrawn: build with `buildWaferMap` and draw with `renderWaferMap` or `renderWaferGallery`. */
41
+ export declare const affineSwapsAxes: typeof affineSwapsAxesImpl;
42
+ /** @deprecated Removed in 0.31.0. It belongs to the low-level drawing pipeline, which is being withdrawn: build with `buildWaferMap` and draw with `renderWaferMap` or `renderWaferGallery`. */
43
+ export declare const applyProbeSequence: typeof applyProbeSequenceImpl;
44
+ /** @deprecated Removed in 0.31.0. It belongs to the low-level drawing pipeline, which is being withdrawn: build with `buildWaferMap` and draw with `renderWaferMap` or `renderWaferGallery`. */
45
+ export declare const generateReticleGrid: typeof generateReticleGridImpl;
46
+ /** @deprecated Removed in 0.31.0. A die's ring and quadrant are in the die list's CSV export (Ring and Quadrant columns); ring and quadrant yield is `stats.regionYield.` */
47
+ export declare const classifyDie: typeof classifyDieImpl;
48
+ /** @deprecated Removed in 0.31.0. Ring labels come with ring yield (`stats.regionYield.ring[].label`) and ring findings (`comparison.left`). */
49
+ export declare const getRingLabel: typeof getRingLabelImpl;
50
+ /** @deprecated Removed in 0.31.0. Stack wafers with `buildWaferMap`'s `lotStack` option. */
51
+ export declare const aggregateValues: typeof aggregateValuesImpl;
52
+ /** @deprecated Removed in 0.31.0. Stack wafers with `buildWaferMap`'s `lotStack` option. */
53
+ export declare const aggregateBinCounts: typeof aggregateBinCountsImpl;
54
+ /** @deprecated Removed in 0.31.0. Stack wafers with `buildWaferMap`'s `lotStack` option. */
55
+ export declare const getUniqueBins: typeof getUniqueBinsImpl;
56
+ /** @deprecated Removed in 0.31.0. Use `metadataDisplayValue` for a metadata value's text. */
57
+ export declare const metadataCategoricalValue: typeof metadataCategoricalValueImpl;
58
+ /** @deprecated Removed in 0.31.0. `buildWaferMap` resolves the die pitch itself; each built die's width and height carry it. */
59
+ export declare const resolveGridPitch: typeof resolveGridPitchImpl;
60
+ //# sourceMappingURL=deprecated.d.ts.map
@@ -0,0 +1 @@
1
+ import{deprecated as e}from"../renderer/deprecate.js";import{createWafer as o}from"./wafer.js";import{generateDies as r,isPositionedDie as n}from"./dies.js";import{clipDiesToWafer as s,applyOrientation as f,transformDies as p,mapDataToDies as l,isInsideWafer as d,affineIdentity as c,affineRotation as g,affineMirror as m,affineCompose as u,affineInvert as I,affinePoint as $,affineVector as h,affineSwapsAxes as D}from"./transforms.js";import{applyProbeSequence as y}from"./probe.js";import{generateReticleGrid as w}from"./reticle.js";import{classifyDie as x,getRingLabel as C}from"./classify.js";import{aggregateValues as b,aggregateBinCounts as W,getUniqueBins as P}from"./aggregates.js";import{metadataCategoricalValue as V}from"./metadata.js";import{resolveGridPitch as S}from"./inference/pitch.js";const a="If you depend on it, say so at https://github.com/wafertools/wafermap/issues.",t="It belongs to the low-level drawing pipeline, which is being withdrawn: build with buildWaferMap and draw with renderWaferMap or renderWaferGallery.",A="Use hasPosition to test whether a die has a position.",M="buildWaferMap maps results onto dies.",i="Stack wafers with buildWaferMap's lotStack option.",R="buildWaferMap resolves the die pitch itself; each built die's width and height carry it.";export const createWafer=e(o,"createWafer",`For a die layout with no test data use buildWaferMap({ layout: true, waferConfig, dieConfig }); otherwise build with buildWaferMap and draw with renderWaferMap or renderWaferGallery. ${a}`),generateDies=e(r,"generateDies",`For a die layout with no test data use buildWaferMap({ layout: true, waferConfig, dieConfig }), which keeps every site fully on the wafer. ${a}`),isPositionedDie=e(n,"isPositionedDie",`${A} ${a}`),clipDiesToWafer=e(s,"clipDiesToWafer",`For a die layout with no test data use buildWaferMap({ layout: true, waferConfig, dieConfig }), which keeps every site fully on the wafer. ${a}`),applyOrientation=e(f,"applyOrientation",`${t} ${a}`),transformDies=e(p,"transformDies",`${t} ${a}`),mapDataToDies=e(l,"mapDataToDies",`${M} ${a}`),isInsideWafer=e(d,"isInsideWafer",`${t} ${a}`),affineIdentity=e(c,"affineIdentity",`${t} ${a}`),affineRotation=e(g,"affineRotation",`${t} ${a}`),affineMirror=e(m,"affineMirror",`${t} ${a}`),affineCompose=e(u,"affineCompose",`${t} ${a}`),affineInvert=e(I,"affineInvert",`${t} ${a}`),affinePoint=e($,"affinePoint",`${t} ${a}`),affineVector=e(h,"affineVector",`${t} ${a}`),affineSwapsAxes=e(D,"affineSwapsAxes",`${t} ${a}`),applyProbeSequence=e(y,"applyProbeSequence",`${t} ${a}`),generateReticleGrid=e(w,"generateReticleGrid",`${t} ${a}`),classifyDie=e(x,"classifyDie",`A die's ring and quadrant are in the die list's CSV export (Ring and Quadrant columns); ring and quadrant yield is stats.regionYield. ${a}`),getRingLabel=e(C,"getRingLabel",`Ring labels come with ring yield (stats.regionYield.ring[].label) and ring findings (comparison.left). ${a}`),aggregateValues=e(b,"aggregateValues",`${i} ${a}`),aggregateBinCounts=e(W,"aggregateBinCounts",`${i} ${a}`),getUniqueBins=e(P,"getUniqueBins",`${i} ${a}`),metadataCategoricalValue=e(V,"metadataCategoricalValue",`Use metadataDisplayValue for a metadata value's text. ${a}`),resolveGridPitch=e(S,"resolveGridPitch",`${R} ${a}`);
@@ -5,9 +5,7 @@ export * from './metadata.js';
5
5
  export * from './probe.js';
6
6
  export * from './reticle.js';
7
7
  export type { DataRow, MapOptions, TransformOptions, Affine, CoordFrame } from './transforms.js';
8
- export { isInsideWafer, clipDiesToWafer, applyOrientation, transformDies, mapDataToDies } from './transforms.js';
9
- export { affineIdentity, affineRotation, affineMirror, affineCompose, affineInvert, affinePoint, affineVector, affineSwapsAxes, } from './transforms.js';
10
8
  export * from './wafer.js';
11
9
  export type { PitchResult } from './inference/pitch.js';
12
- export { resolveGridPitch } from './inference/pitch.js';
10
+ export { createWafer, generateDies, isPositionedDie, clipDiesToWafer, applyOrientation, transformDies, mapDataToDies, isInsideWafer, affineIdentity, affineRotation, affineMirror, affineCompose, affineInvert, affinePoint, affineVector, affineSwapsAxes, applyProbeSequence, generateReticleGrid, classifyDie, getRingLabel, aggregateValues, aggregateBinCounts, getUniqueBins, metadataCategoricalValue, resolveGridPitch, } from './deprecated.js';
13
11
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- export*from"./aggregates.js";export*from"./classify.js";export*from"./dies.js";export*from"./metadata.js";export*from"./probe.js";export*from"./reticle.js";export{isInsideWafer,clipDiesToWafer,applyOrientation,transformDies,mapDataToDies}from"./transforms.js";export{affineIdentity,affineRotation,affineMirror,affineCompose,affineInvert,affinePoint,affineVector,affineSwapsAxes}from"./transforms.js";export*from"./wafer.js";export{resolveGridPitch}from"./inference/pitch.js";
1
+ export*from"./aggregates.js";export*from"./classify.js";export*from"./dies.js";export*from"./metadata.js";export*from"./probe.js";export*from"./reticle.js";export*from"./wafer.js";export{createWafer,generateDies,isPositionedDie,clipDiesToWafer,applyOrientation,transformDies,mapDataToDies,isInsideWafer,affineIdentity,affineRotation,affineMirror,affineCompose,affineInvert,affinePoint,affineVector,affineSwapsAxes,applyProbeSequence,generateReticleGrid,classifyDie,getRingLabel,aggregateValues,aggregateBinCounts,getUniqueBins,metadataCategoricalValue,resolveGridPitch}from"./deprecated.js";
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Which bins pass, per wafer — and how to name a set of them.
3
+ *
4
+ * The rule this file exists for: `[1]` is a default only where data ENTERS the
5
+ * library (`buildWaferMap`'s input). From there the value travels on the result
6
+ * (`WaferMapResult.passBins`), and every display, analysis and report reads it —
7
+ * per wafer, because a gallery can hold wafers from programs with different pass
8
+ * bins. Every downstream surface used to default `passBins` to `[1]` on its own
9
+ * unless the caller repeated it, so a host that told `buildWaferMap` bins 1 and 2
10
+ * pass saw bin 2 drawn, sorted and totalled as a failure everywhere else — with
11
+ * `result.yield`, the one figure that did use them, disagreeing beside it.
12
+ *
13
+ * Deliberately internal (not re-exported from `core/index.ts`).
14
+ */
15
+ /** The industry convention, applied ONLY to input that states no pass bins. */
16
+ export declare const INPUT_DEFAULT_PASS_BINS: readonly number[];
17
+ /**
18
+ * One wafer's pass bins: its own (carried from `buildWaferMap`), else the
19
+ * caller's default for items built without any, else the input convention.
20
+ */
21
+ export declare function itemPassBins(item: {
22
+ passBins?: readonly number[];
23
+ } | null | undefined, fallback?: readonly number[]): readonly number[];
24
+ /** Same bins, in any order. */
25
+ export declare function sameBins(a: readonly number[], b: readonly number[]): boolean;
26
+ /**
27
+ * Names the pass bins in use, for a yield label: "bin 1", "bins 1, 2", or —
28
+ * when the wafers shown disagree — "per wafer: bin 1 · bins 1, 2". A label that
29
+ * named one wafer's set while the figure pooled wafers judged differently would
30
+ * be the exact mislabelled population the library must never show.
31
+ */
32
+ export declare function passBinsLabel(sets: Iterable<readonly number[]>): string;
33
+ //# sourceMappingURL=passBins.d.ts.map
@@ -0,0 +1 @@
1
+ export const INPUT_DEFAULT_PASS_BINS=[1];export function itemPassBins(s,e=INPUT_DEFAULT_PASS_BINS){return s?.passBins??e}export function sameBins(s,e){const t=new Set(s),n=new Set(e);return t.size===n.size&&[...t].every(i=>n.has(i))}export function passBinsLabel(s){const e=[];for(const n of s)e.some(i=>sameBins(i,n))||e.push(n);const t=n=>n.length===0?"no bins":n.length===1?`bin ${n[0]}`:`bins ${n.join(", ")}`;return e.length<=1?t(e[0]??INPUT_DEFAULT_PASS_BINS):`per wafer: ${e.map(t).join(" \xB7 ")}`}
@@ -68,4 +68,16 @@ export declare function modeOf(values: number[]): number | null;
68
68
  * obvious. Every CSV this library writes must go through it.
69
69
  */
70
70
  export declare function csvField(value: string): string;
71
+ /**
72
+ * Escape text for insertion into HTML — the one copy for the whole library.
73
+ *
74
+ * Every string the library puts into `innerHTML` that did not come from its own
75
+ * literals must go through this: test names, units, bin names, wafer labels and
76
+ * metadata values all arrive from input files (STDF/ATDF/CSV headers, MES
77
+ * fields, operator free text), and a name like `<img src=x onerror=…>` would
78
+ * otherwise run as script when a tooltip showed it — in a Tauri or Electron
79
+ * host, inside the app's own webview. It used to live in stats/reportHtml.ts,
80
+ * where only the reports used it, while every tooltip interpolated raw names.
81
+ */
82
+ export declare function escHtml(value: string): string;
71
83
  //# sourceMappingURL=utils.d.ts.map
@@ -1 +1 @@
1
- export function compareNatural(t,n){return t.localeCompare(n,"en",{numeric:!0})}export function prettyKey(t){return t.replace(/([A-Z])/g," $1").replace(/_/g," ").trim().replace(/^./,n=>n.toUpperCase())}export function clamp01(t){return Math.max(0,Math.min(1,t))}export function mean(t){if(t.length===0)return NaN;let n=0;for(const r of t)n+=r;return n/t.length}export function medianOfSorted(t){const n=t.length;if(n===0)return 0;const r=Math.floor(n/2);return n%2===1?t[r]:(t[r-1]+t[r])/2}export function median(t){return medianOfSorted([...t].sort((n,r)=>n-r))}export function percentile98(t){if(t.length===0)return 0;const n=[...t].sort((o,e)=>o-e),r=Math.floor(n.length*.98);return n[Math.min(r,n.length-1)]}export function modeOf(t){if(!t.length)return null;const n=new Map;for(const e of t)n.set(e,(n.get(e)??0)+1);let r=0,o=t[0];for(const[e,c]of n)c>r&&(r=c,o=e);return o}const i=/^[=+\-@]/;export function csvField(t){const n=i.test(t)&&Number.isNaN(Number(t))?`'${t}`:t;return/[",\n\r]/.test(n)?`"${n.replace(/"/g,'""')}"`:n}
1
+ export function compareNatural(t,e){return t.localeCompare(e,"en",{numeric:!0})}export function prettyKey(t){return t.replace(/([A-Z])/g," $1").replace(/_/g," ").trim().replace(/^./,e=>e.toUpperCase())}export function clamp01(t){return Math.max(0,Math.min(1,t))}export function mean(t){if(t.length===0)return NaN;let e=0;for(const n of t)e+=n;return e/t.length}export function medianOfSorted(t){const e=t.length;if(e===0)return 0;const n=Math.floor(e/2);return e%2===1?t[n]:(t[n-1]+t[n])/2}export function median(t){return medianOfSorted([...t].sort((e,n)=>e-n))}export function percentile98(t){if(t.length===0)return 0;const e=[...t].sort((o,r)=>o-r),n=Math.floor(e.length*.98);return e[Math.min(n,e.length-1)]}export function modeOf(t){if(!t.length)return null;const e=new Map;for(const r of t)e.set(r,(e.get(r)??0)+1);let n=0,o=t[0];for(const[r,c]of e)c>n&&(n=c,o=r);return o}const p=/^[=+\-@]/;export function csvField(t){const e=p.test(t)&&Number.isNaN(Number(t))?`'${t}`:t;return/[",\n\r]/.test(e)?`"${e.replace(/"/g,'""')}"`:e}export function escHtml(t){return t.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;").replace(/'/g,"&#39;")}
@@ -0,0 +1,35 @@
1
+ import { type WaferMetadata } from './metadata.js';
2
+ /**
3
+ * The name to show for one wafer among several — a gallery card's header, a
4
+ * findings or yield list row, a report's Wafer column, a die list's Wafer
5
+ * column, a detached window's title.
6
+ *
7
+ * Deliberately internal (not re-exported from `core/index.ts`): it is the one
8
+ * rule for every surface that names a wafer, not an API a host needs.
9
+ *
10
+ * Every one of those surfaces used to supply its own fallback for an item with
11
+ * no `label`, and they disagreed: the gallery card header was blank, the
12
+ * findings list and reports said `W3` (a position, which reads as a wafer ID
13
+ * and need not match one), a detached window said "Wafer map", and the
14
+ * Insights tab used the wafer ID. The wafer ID was on the item the whole time.
15
+ *
16
+ * Order: the caller's `label` (a blank one counts as absent), then the wafer's
17
+ * own `metadata.waferId`, then a position that says plainly it is not an ID.
18
+ */
19
+ export declare function waferDisplayLabel(item: WaferLabelSource, index: number): string;
20
+ type WaferLabelSource = {
21
+ label?: string;
22
+ wafer?: {
23
+ metadata?: WaferMetadata | null;
24
+ } | null;
25
+ } | null | undefined;
26
+ /**
27
+ * The wafer's real identity — the caller's `label`, else `metadata.waferId` —
28
+ * or `undefined` when it has none. `waferDisplayLabel` without its positional
29
+ * fallback, for places where a position must not stand in for an identity: a
30
+ * saved file's name (see canvas-adapter/exportName.ts), where "Wafer 3 (no ID)"
31
+ * would be written into every export of a lot and read back later as an ID.
32
+ */
33
+ export declare function waferIdentityLabel(item: WaferLabelSource): string | undefined;
34
+ export {};
35
+ //# sourceMappingURL=waferLabel.d.ts.map
@@ -0,0 +1 @@
1
+ import{metadataDisplayValue as r}from"./metadata.js";export function waferDisplayLabel(a,e){return waferIdentityLabel(a)??`Wafer ${e+1} (no ID)`}export function waferIdentityLabel(a){return(a?.label?.trim()||void 0)??r(a?.wafer?.metadata?.waferId)}
@@ -12,15 +12,26 @@ export interface BinColors {
12
12
  hard: Map<number, string>;
13
13
  soft: Map<number, string>;
14
14
  /**
15
- * Bins drawn in a colour that another bin of the same type also has — the
16
- * population has more pass or fail bins than the palette has colours, or a
17
- * defined colour duplicates one. Empty when every bin is distinguishable by
18
- * colour alone. Ascending bin number.
15
+ * Bins drawn in a colour that another bin of the same type also has — two
16
+ * bins present whose numbers land on the same palette slot (they differ by a
17
+ * multiple of the palette's length), or a defined colour that duplicates one.
18
+ * Empty when every bin is distinguishable by colour alone. Ascending bin number.
19
19
  */
20
20
  shared: {
21
21
  hard: number[];
22
22
  soft: number[];
23
23
  };
24
+ /**
25
+ * Bins that pass, per type — the verdict the colours were chosen with. Hard:
26
+ * the bins in `passBins`. Soft: bins whose every die passes. Read this, never
27
+ * `passBins`, to order, total or judge SOFT bins: `passBins` holds hard-bin
28
+ * numbers, so testing a soft bin against it treats soft bin 1 as a pass and
29
+ * soft bin 100 as a fail whatever their dies did.
30
+ */
31
+ pass: {
32
+ hard: Set<number>;
33
+ soft: Set<number>;
34
+ };
24
35
  }
25
36
  export interface BinColorOptions {
26
37
  /** Which bins pass. Default `[1]`. Must match the `passBins` behind the yield figure. */
@@ -40,26 +51,107 @@ export interface BinColorOptions {
40
51
  * Assign every bin present in `dies` a colour — the ONE rule for bin colour,
41
52
  * shared by the map, its legends, the summary panels and the Insights charts.
42
53
  *
43
- * Why not a function of the bin number, as it used to be:
44
54
  * - **Pass/fail comes from `passBins`, never the number.** Passing bins take
45
55
  * the palette's pass colours (greens) and failing bins its fail colours, so
46
- * with `passBins: [1, 3]` bin 3 is green and a failing bin 12 is not. Keying
47
- * colour on the number made bin 1 green even when it failed.
48
- * - **Rank, not hash.** Bins are ordered by die count (then bin number) and
49
- * take palette slots in that order, so the bins that dominate the map get
50
- * the most distinct colours and no two bins collide until the palette is
51
- * exhausted. Hashing collided from the start: two of the first sixteen soft
52
- * bins shared a colour exactly.
56
+ * with `passBins: [1, 3]` bin 3 is green and a failing bin 1 is not.
57
+ * - **Which colour is keyed by bin number, never by die count.** A pass bin
58
+ * takes `pass[(bin − 1) mod n]` and a fail bin `fail[(bin − 2) mod n]`: bin 1
59
+ * (the conventional pass bin) and bin 2 (the conventional first fail bin)
60
+ * land on the front, most distinct colour of each list, and the low bin
61
+ * numbers most programs use get the clearest colours. So bin 7 is the same
62
+ * colour in every lot, gallery subset and screenshot of a program, which is
63
+ * how engineers learn to read a bin map. This used to rank bins by die count,
64
+ * which gave "the biggest fail bin" a colour rather than any bin — two lots of
65
+ * one program drew the same bin in different colours. Before that it hashed
66
+ * the number, which collided two of the first sixteen soft bins; a modular
67
+ * index collides only at a palette's length apart, and `shared` names those.
68
+ * - **The slot counts from the bin number alone**, not its position among the
69
+ * pass or fail bins, so changing `passBins` recolours only the bins whose
70
+ * verdict changed.
53
71
  * - **Soft bins are pass-aware too.** A soft bin passes when every die
54
72
  * carrying it passes (by the same per-die rule yield uses); otherwise, or
55
73
  * when no die says, it takes a fail colour. The library has the data to
56
74
  * decide, so the caller never supplies it.
75
+ * - **Soft bins read the palette shifted by half its length**, so hard bin n
76
+ * and soft bin n are different colours by default. They are separate number
77
+ * spaces and are never on screen together, but a shared colour invites
78
+ * reading one as the other.
57
79
  *
58
80
  * A gallery resolves ONCE over every wafer it shows and hands the result to every
59
- * card (`ViewOptions.binColors`), so a bin is the same colour on every wafer.
81
+ * card (`ViewOptions.binColors`): a `BinDef.color` supplied by one item then
82
+ * colours that bin on every card, and `shared` describes the whole gallery.
60
83
  * A lone map resolves over its own dies.
61
84
  */
62
85
  export declare function resolveBinColors(dies: Iterable<Die>, options?: BinColorOptions): BinColors;
86
+ /** The inputs a built map carries for its bin colours. A `WaferMapResult` is one. */
87
+ export interface BinColorSource {
88
+ dies: Iterable<Die>;
89
+ /** The pass bins the map was built with (`WaferMapResult.passBins`). */
90
+ passBins?: readonly number[];
91
+ hbinDefs?: readonly BinDef[];
92
+ sbinDefs?: readonly BinDef[];
93
+ }
94
+ /** Display choices for `binColorsForMaps` — the palette and whether defined colours win. */
95
+ export interface MapBinColorOptions {
96
+ /** Registered bin palette name (`registerBinColorScheme`). Default `'default'`. */
97
+ binColorScheme?: string;
98
+ /** Honour `BinDef.color`. Default true. */
99
+ useDefinedBinColors?: boolean;
100
+ }
101
+ /**
102
+ * The bin colours for one or more built maps — exactly the colours
103
+ * `renderWaferMap` and `renderWaferGallery` draw them in, for a host surface of
104
+ * its own: a table swatch, a PDF, a chart in another library.
105
+ *
106
+ * Takes the maps rather than dies and a `passBins` list, so pass/fail cannot be
107
+ * judged by the wrong bins: each map's dies are judged by that map's own
108
+ * `passBins`, and bin definitions (with any `BinDef.color`) are merged across the
109
+ * maps, first definition of a bin winning — the gallery's own rule. For a live
110
+ * map with the user's palette choice, read `WaferMapController.getBinColors()` or
111
+ * `GalleryController.getBinColors()` instead.
112
+ */
113
+ export declare function binColorsForMaps(maps: BinColorSource | readonly BinColorSource[], options?: MapBinColorOptions): BinColors;
114
+ /** @internal `binColorsForMaps` plus the hard bins that pass on some maps and fail on others. */
115
+ export declare function resolveBinColorsForMaps(maps: readonly BinColorSource[], options?: MapBinColorOptions): {
116
+ colors: BinColors;
117
+ mixedHardBins: number[];
118
+ };
119
+ /**
120
+ * @internal Bin definitions merged across maps: one per bin, the first map's
121
+ * definition winning. The one rule for a multi-map legend, report or colouring.
122
+ */
123
+ export declare function mergeBinDefs(lists: Iterable<readonly BinDef[] | undefined>): BinDef[];
124
+ /** One wafer's dies and the pass bins that wafer was built with. */
125
+ export interface BinPassGroup {
126
+ dies: Iterable<Die>;
127
+ /** Omitted ⇒ the input convention `[1]` — pass `WaferMapResult.passBins`. */
128
+ passBins?: readonly number[];
129
+ }
130
+ /**
131
+ * `resolveBinColors` over several wafers that may carry DIFFERENT pass bins — a
132
+ * gallery of wafers from more than one test program. Each die is judged by its
133
+ * own wafer's `passBins`, and a bin passes only when every die carrying it
134
+ * passes (for one wafer that is exactly "the bin is in `passBins`").
135
+ *
136
+ * `mixedHardBins` names hard bins that pass on some wafers and fail on others.
137
+ * One colour and one legend row cannot be right for both, so the gallery states
138
+ * it (`mixedPassBinsWarning`) rather than letting either wafer's verdict
139
+ * silently stand for all of them.
140
+ */
141
+ export declare function resolveBinColorsByWafer(groups: Iterable<BinPassGroup>, options?: Omit<BinColorOptions, 'passBins'>): {
142
+ colors: BinColors;
143
+ mixedHardBins: number[];
144
+ };
145
+ /**
146
+ * Which hard and soft bins pass — `BinColors.pass` without the colours, for a
147
+ * caller that has dies but no resolved assignment (the summary report). The same
148
+ * tally `resolveBinColors` uses, so the two cannot disagree about a verdict.
149
+ */
150
+ export declare function binPassSets(dies: Iterable<Die>, passBins?: readonly number[]): BinColors['pass'];
151
+ /** `binPassSets` over wafers that each carry their own pass bins. */
152
+ export declare function binPassSetsByWafer(groups: Iterable<BinPassGroup>): BinColors['pass'];
153
+ /** The `pass-bins-mixed` advisory for `resolveBinColorsByWafer`'s `mixedHardBins`, or null. */
154
+ export declare function mixedPassBinsWarning(bins: readonly number[]): WaferWarning | null;
63
155
  /**
64
156
  * True when `colors` has an entry for every bin on these dies — i.e. it was
65
157
  * resolved over a population that includes them. `buildView` checks this
@@ -1 +1 @@
1
- import{diePassStatus as p}from"../core/dies.js";import{getBinColorScheme as m}from"./colorSchemes.js";export function resolveBinColors(r,o={}){const s=new Set(o.passBins??[1]),i=m(o.binColorScheme),l=o.useDefinedBinColors??!0,a=new Map,f=new Map,c=new Map;for(const e of r)if(e.hbin!=null&&a.set(e.hbin,(a.get(e.hbin)??0)+1),e.sbin!=null){f.set(e.sbin,(f.get(e.sbin)??0)+1);const n=p(e,s)===!0;c.set(e.sbin,(c.get(e.sbin)??!0)&&n)}const d=b(a,e=>s.has(e),i,l?o.hbinDefs:void 0),h=b(f,e=>c.get(e)===!0,i,l?o.sbinDefs:void 0);return{hard:d.colors,soft:h.colors,shared:{hard:d.shared,soft:h.shared}}}export function binColorsCover(r,o){for(const s of o)if(s.hbin!=null&&!r.hard.has(s.hbin)||s.sbin!=null&&!r.soft.has(s.sbin))return!1;return!0}export function binColorWarning(r,o){const s=o==="softBin"?"soft":o==="hardBin"?"hard":null;if(!s)return null;const i=r.shared[s];if(!i.length)return null;const l=i.length>8?`${i.slice(0,8).join(", ")} and ${i.length-8} more`:i.join(", ");return{code:"bin-colors-shared",severity:"warning",message:`${s==="hard"?"Hard":"Soft"} bins ${l} are drawn in a colour another bin also has, so colour alone cannot tell them apart. There are more bins than the colour scheme has distinct colours (or a bin definition repeats one). Use the legend, the tooltip, or highlight one bin at a time.`}}function b(r,o,s,i){const l=new Map;for(const n of i??[])n.color&&l.set(n.bin,n.color);const a=new Map,f=[],c=[];for(const n of r.keys()){const t=l.get(n);t!==void 0?a.set(n,t):(o(n)?f:c).push(n)}const d=(n,t)=>r.get(t)-r.get(n)||n-t;for(const[n,t]of[[f,s.pass],[c,s.fail]])n.sort(d),n.forEach((u,g)=>a.set(u,t[g%t.length]));const h=new Map;for(const[n,t]of a){const u=t.toLowerCase();h.set(u,[...h.get(u)??[],n])}const e=[...h.values()].filter(n=>n.length>1).flat().sort((n,t)=>n-t);return{colors:a,shared:e}}
1
+ import{diePassStatus as g}from"../core/dies.js";import{INPUT_DEFAULT_PASS_BINS as B,itemPassBins as m}from"../core/passBins.js";import{getBinColorScheme as w}from"./colorSchemes.js";export function resolveBinColors(s,n={}){return resolveBinColorsByWafer([{dies:s,passBins:n.passBins}],n).colors}export function binColorsForMaps(s,n={}){return resolveBinColorsForMaps(Array.isArray(s)?s:[s],n).colors}export function resolveBinColorsForMaps(s,n={}){return resolveBinColorsByWafer(s.map(e=>({dies:e.dies,passBins:m(e)})),{...n,hbinDefs:mergeBinDefs(s.map(e=>e.hbinDefs)),sbinDefs:mergeBinDefs(s.map(e=>e.sbinDefs))})}export function mergeBinDefs(s){const n=new Set,e=[];for(const a of s)for(const o of a??[])n.has(o.bin)||(n.add(o.bin),e.push(o));return e}export function resolveBinColorsByWafer(s,n={}){const e=w(n.binColorScheme),a=n.useDefinedBinColors??!0,o=h(s),f=b(o.hardBins,i=>o.pass.hard.has(i),e,a?n.hbinDefs:void 0,!1),t=b(o.softBins,i=>o.pass.soft.has(i),e,a?n.sbinDefs:void 0,!0);return{colors:{hard:f.colors,soft:t.colors,shared:{hard:f.shared,soft:t.shared},pass:o.pass},mixedHardBins:o.mixedHardBins}}export function binPassSets(s,n){return h([{dies:s,passBins:n}]).pass}export function binPassSetsByWafer(s){return h(s).pass}function h(s){const n=new Map,e=new Map,a=new Set;for(const t of s){const i=new Set(t.passBins??B);for(const l of t.dies){const r=g(l,i)===!0;l.hbin!=null&&(n.set(l.hbin,(n.get(l.hbin)??!0)&&r),r&&a.add(l.hbin)),l.sbin!=null&&e.set(l.sbin,(e.get(l.sbin)??!0)&&r)}}const o=t=>new Set([...t].filter(([,i])=>i).map(([i])=>i)),f={hard:o(n),soft:o(e)};return{hardBins:[...n.keys()],softBins:[...e.keys()],pass:f,mixedHardBins:[...a].filter(t=>!f.hard.has(t)).sort((t,i)=>t-i)}}function u(s){return s.length>8?`${s.slice(0,8).join(", ")} and ${s.length-8} more`:s.join(", ")}export function mixedPassBinsWarning(s){if(!s.length)return null;const n=s.length===1;return{code:"pass-bins-mixed",severity:"warning",message:`Hard ${n?"bin":"bins"} ${u(s)} ${n?"passes":"pass"} on some wafers and ${n?"fails":"fail"} on others: these wafers were built with different pass bins. Each wafer's own yield and die verdicts are correct, but a bin has one colour and one legend row, so ${n?"it is":"they are"} shown as failing there.`}}export function binColorsCover(s,n){if(!s.pass)return!1;for(const e of n)if(e.hbin!=null&&!s.hard.has(e.hbin)||e.sbin!=null&&!s.soft.has(e.sbin))return!1;return!0}export function binColorWarning(s,n){const e=n==="softBin"?"soft":n==="hardBin"?"hard":null;if(!e)return null;const a=s.shared[e];if(!a.length)return null;const o=u(a);return{code:"bin-colors-shared",severity:"warning",message:`${e==="hard"?"Hard":"Soft"} bins ${o} are drawn in a colour another bin also has, so colour alone cannot tell them apart. There are more bins than the colour scheme has distinct colours (or a bin definition repeats one). Use the legend, the tooltip, or highlight one bin at a time.`}}const S=1,x=2;function p(s,n,e,a){const o=s.length,f=a?Math.floor(o/2):0;return s[((n-e+f)%o+o)%o]}function b(s,n,e,a,o){const f=new Map;for(const r of a??[])r.color&&f.set(r.bin,r.color);const t=new Map;for(const r of s)t.set(r,f.get(r)??(n(r)?p(e.pass,r,S,o):p(e.fail,r,x,o)));const i=new Map;for(const[r,c]of t){const d=c.toLowerCase();i.set(d,[...i.get(d)??[],r])}const l=[...i.values()].filter(r=>r.length>1).flat().sort((r,c)=>r-c);return{colors:t,shared:l}}
@@ -155,7 +155,7 @@ export interface View {
155
155
  allIntegerValues?: boolean;
156
156
  /** Named test definitions — populated when `testDefs` is passed to `buildWaferMap`. */
157
157
  testDefs?: TestDef[];
158
- /** Which `values[]` index is being displayed (for `value` plot mode). Default 0. */
158
+ /** The test number being displayed in `value` plot mode — a `die.testValues` key, not a position. */
159
159
  activeTest: number;
160
160
  /** True when log₁₀ scale is both requested and valid (vMin > 0). */
161
161
  logScale: boolean;
@@ -217,8 +217,6 @@ export interface View {
217
217
  * - `'off'` — continuous value gradient (or a non-value plot mode).
218
218
  */
219
219
  passFailDisplay: 'off' | 'spec' | 'test';
220
- /** True when `passFailDisplay === 'spec'` — kept for back-compat with existing consumers. */
221
- colorBySpec: boolean;
222
220
  /** Whether the colorbar range is anchored to spec limits ('spec') or data extents ('data'). */
223
221
  colorbarRangeMode: 'spec' | 'data';
224
222
  /** Wafer centre in scene/display mm coordinates (the rotation pivot). */
@@ -284,7 +282,6 @@ export interface ViewOptions {
284
282
  showRingBoundaries?: boolean;
285
283
  showQuadrantBoundaries?: boolean;
286
284
  showXYIndicator?: boolean;
287
- dieGap?: number;
288
285
  /** Bin palette for `hardBin`/`softBin` — any name registered via `registerBinColorScheme()`. Default `'default'`. */
289
286
  binColorScheme?: string;
290
287
  /** Value gradient for `value` and the stacked modes — any name registered via `registerValueColorScheme()`. Default `'default'`. */
@@ -364,9 +361,10 @@ export interface ViewOptions {
364
361
  * Which bins count as passing. Default `[1]`.
365
362
  *
366
363
  * Sets `ViewRect.binFail` and decides which bins take the palette's pass
367
- * colours (see `resolveBinColors`) — never the bin number. Must match the
368
- * `passBins` given to `buildWaferMap` / `analyzeWaferMap`, or the map will
369
- * disagree with the yield figure beside it.
364
+ * colours (see `resolveBinColors`) — never the bin number. `buildWaferMap` and
365
+ * the renderers set this from `WaferMapResult.passBins`, so a map agrees with
366
+ * its own yield figure; only a hand-rolled pipeline calling `buildView`
367
+ * directly supplies it.
370
368
  */
371
369
  passBins?: number[];
372
370
  /**
@@ -374,10 +372,11 @@ export interface ViewOptions {
374
372
  * Generated by `generateReticleGrid` or the `reticle` option on `buildWaferMap`.
375
373
  */
376
374
  reticles?: Reticle[];
377
- /** Named test definitions — one per `values[]` entry. */
375
+ /** Named test definitions — one per test number in `die.testValues`. */
378
376
  testDefs?: TestDef[];
379
377
  /**
380
- * Which `values[]` index to display in `value` plot mode. Default `0`.
378
+ * The test number to display in `value` plot mode — a `die.testValues` key
379
+ * (`TestDef.testNumber`), not a position. Default `0`.
381
380
  * When `testDefs` is provided, the toolbar mode dropdown offers one item per test.
382
381
  */
383
382
  activeTest?: number;