@wafertools/wafermap 0.23.1 → 0.24.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -22,6 +22,84 @@ under `### Breaking`.
22
22
 
23
23
  ---
24
24
 
25
+ ## [0.24.0] — 2026-08-24
26
+
27
+ ### Breaking
28
+
29
+ - **`buildDieListSection`/`RenderOptions.dieList` now caps rendered rows at `maxRows` (default
30
+ `50_000`)**, where the table previously rendered every die unconditionally. This table has
31
+ no virtualisation, so an uncapped multi-hundred-thousand-die lot was already a genuinely
32
+ heavy DOM build (~1.3M elements at 266k dies) before this release added metadata columns —
33
+ without the cap, adding columns would have made that worst case materially worse rather than
34
+ strictly better. The **CSV export is never capped**; it always contains every die, and a
35
+ footer states the truncation explicitly whenever it applies (`"Showing the first 50,000 of
36
+ 266,412 dies. The CSV export contains all 266,412."`). A host that needs every row on screen
37
+ regardless of scale can set `maxRows` to a larger value or `Infinity`.
38
+
39
+ ### Added
40
+
41
+ - **Metadata now reaches the die-list table and its CSV export**, and the two per-test CSVs
42
+ (`test-values.csv`, `functional-tests.csv`) gain wafer identity. Previously no export surface
43
+ in the library emitted `die.metadata` at all — only the hover tooltip and the `'metadata'`
44
+ plot mode did — and none of the three CSVs emitted wafer metadata either, despite the HTML
45
+ reports already doing so.
46
+ - **Die metadata** (`DieMetadata`) is on by default in `buildDieListSection`
47
+ (`DieListOptions.metadataColumns`, default `'auto'`) — every key found on any die,
48
+ deterministically ordered (`metadataFields` declaration order first, then natural sort).
49
+ Unlike `metadataFields` gating the `'metadata'` plot mode (a legend has a cardinality
50
+ limit), a table column has none, so auto-discovery here does not conflict with that rule —
51
+ it follows the same intent. Pass an explicit `string[]` to pin the set, or `'none'` to omit.
52
+ - **Wafer metadata** (`WaferMetadata`) is CSV-only by default
53
+ (`DieListOptions.waferMetadataColumns`, default `'csv'`) — constant down every row, so it's
54
+ noise on screen next to the always-visible metadata badge, but it's what makes a detached
55
+ CSV self-describing enough to concatenate several exports and still know which wafer each
56
+ row came from. Set `'both'` to also show it in the table, or `'none'` to omit it entirely.
57
+ - A key present on both a wafer and a die produces exactly **one** column, scope `die`,
58
+ carrying the die's value — the same shadowing rule the hover tooltip already applied.
59
+ - Column labels resolve `metadataFields[].label` → `prettyKey(key)` → the raw key, matching
60
+ every other column's human-readable header. A key colliding with a built-in column name
61
+ (e.g. a metadata key literally called `Site`) is never dropped — it becomes
62
+ `"Site (metadata)"`, with further deterministic fallbacks for the rare case that also
63
+ collides.
64
+ - `RenderOptions.dieList?: DieListDisplayOptions` reaches the built-in die-list view (the
65
+ coordinate-less map replacement, and the "+N dies without position" footer), which
66
+ previously had no host hook at all. The wafer metadata and `metadataFields` it needs are
67
+ always supplied by the library from the current build result, never from this option, so a
68
+ host mistake can't substitute the wrong identity data into an export.
69
+ - `buildTestSection`/`buildFunctionalTestSection`/`buildLotTestSection`/
70
+ `buildLotFunctionalSection` (internal to the Summary/Insights panels) gained a trailing
71
+ `CsvExportContext` — the two lot variants self-derive it from the `perWaferSummaries` they
72
+ already receive, so `renderWaferGallery`'s Insights tab and the lot Summary panel needed no
73
+ call-site changes. A mixed lot (no metadata field common to every wafer) emits no false
74
+ identity column at all, rather than guessing.
75
+ - **"View die list" — every die as a table, reachable from the Summary panel**, on both
76
+ `renderWaferMap` (this wafer's own dies) and `renderWaferGallery` (every wafer in the lot,
77
+ pooled, with a leading `Wafer` column). Deliberately **not** a new toolbar button — reached
78
+ as a link inside the already-open Summary panel, the same way "Summary report" opens the
79
+ HTML report without one either, since the toolbar already carries eleven buttons and this
80
+ reuses an existing entry point rather than adding a twelfth. **On by default** whenever a
81
+ Summary panel is reachable at all (`RenderOptions.dieList`/`GalleryOptions.dieList`, set
82
+ `{ enabled: false }` to hide it); opens wmap's own resizable modal, mounting
83
+ `buildDieListSection` with the same metadata columns and CSV export the coordinate-less
84
+ die-list view already has. The lot-pooled CSV's wafer-metadata columns use `commonMetadata`,
85
+ so a mixed lot emits no false shared identity, same as the pooled per-test CSVs above.
86
+ - **`prettyKey`-labelled hover tooltip metadata.** `buildHoverText` now resolves a metadata
87
+ key's label the same way the new table/CSV columns do (`metadataFields[].label` →
88
+ `prettyKey(key)`), instead of printing the raw key. Brings the tooltip in line with every
89
+ other metadata surface in the library rather than shipping the inconsistency for even one
90
+ release. `HoverTextOptions` gained `metadataFields?: MetadataFieldDef[]`.
91
+ - New `@wafertools/wafermap/core` exports: `metadataDisplayValue(raw)` / `metadataCategoricalValue(raw)` —
92
+ the single stringifier for a metadata value (display/export vs. categorical/colour-swatch
93
+ use), replacing four near-identical inline implementations that had drifted (`buildView.ts`'s
94
+ tooltip and `'metadata'`-mode read path, `summaryPanel.ts`'s `metadataEntries`, `facets.ts`'s
95
+ `facetValueOf`). A `String(v)`-on-an-object bug in two of those four collapsed distinct object
96
+ values into the indistinguishable `"[object Object]"`; all four now emit `JSON.stringify(v)`
97
+ instead, so genuinely different values facet and export as genuinely different values.
98
+ - New `@wafertools/wafermap/stats` exports: `resolveMetadataColumns(options)` and
99
+ `discoverDieMetadataKeys(dies, metadataFields?, limit?)` — the column resolver backing the
100
+ above, public because a host building its own table/export over `die.metadata` shouldn't have
101
+ to reimplement discovery, ordering, shadowing, and collision resolution from scratch.
102
+
25
103
  ## [0.23.1] — 2026-08-18
26
104
 
27
105
  ### Added
@@ -49,6 +127,8 @@ under `### Breaking`.
49
127
  subset of dies happen to carry positions — so the constraint is now skipped on a degenerate axis (dropping
50
128
  confidence to 0.3), leaving the aspect ratio at the safer 1:1 default instead of guessing.
51
129
 
130
+ ## [0.23.0] — 2026-08-16
131
+
52
132
  ### Breaking
53
133
 
54
134
  - **`Die.x`/`y`/`physX`/`physY` are now optional**, not `number`. Existing code
package/README.md CHANGED
@@ -74,7 +74,7 @@ and stats layers.
74
74
 
75
75
  ## Built with wafermap
76
76
 
77
- - **[tsmap](https://github.com/wafertools/tsmap)** — cross-platform desktop app for loading STDF, ATDF, CSV, and JSON wafer data
77
+ - **[tsmap](https://github.com/wafertools/tsmap)** — desktop and browser app for loading STDF, ATDF, CSV, JSON, and Parquet wafer data
78
78
 
79
79
  ## Local preview
80
80
 
@@ -1,24 +1,67 @@
1
1
  import type { Die } from '../core/dies.js';
2
- import { type TestDef } from '../renderer/buildWaferMap.js';
2
+ import { type MetadataFieldDef, type TestDef } from '../renderer/buildWaferMap.js';
3
+ import type { WaferMetadata } from '../core/metadata.js';
4
+ import { type MetadataKeySelection } from '../stats/metadataColumns.js';
3
5
  import { type SaveTextHandler } from './toolbar.js';
4
- export interface DieListOptions {
5
- /** Heading text. Default: `Die list (N)`. */
6
- title?: string;
7
- /** Optional note shown under the heading, e.g. explaining why this replaced the map. */
8
- note?: string;
9
- /** CSV filename, including extension. Default: `'dies.csv'`. */
10
- csvFilename?: string;
11
- /** Host hook for "Export CSV" — see `saveTextFile` (toolbar.ts). */
12
- onSaveText?: SaveTextHandler;
6
+ /** Display preferences for the built-in die-list table. Everything here is a
7
+ * choice about what to show; the *data* it acts on (wafer metadata, metadata
8
+ * field definitions) is supplied by the library from its own build result
9
+ * when reached through `renderWaferMap`'s `RenderOptions.dieList` — never by
10
+ * the host substituting its own, so an export can't be handed the wrong
11
+ * identity data by mistake. */
12
+ export interface DieListDisplayOptions {
13
13
  /**
14
- * Extra leading column, e.g. a wafer-id label for a lot-level combined
15
- * list spanning multiple wafers. `get(die)` returning `undefined` renders
16
- * an empty cell for that row.
14
+ * Show a "View die list" link inside the Summary panel (`renderWaferMap`)
15
+ * / lot Summary panel (`renderWaferGallery`) that opens this wafer's — or,
16
+ * in the gallery, the whole lot's — dies as a table in a modal, with the
17
+ * same "Export CSV" button `buildDieListSection` always has. **Default
18
+ * `true`** — set `enabled: false` to hide it. Requires a Summary panel to
19
+ * be reachable at all (`RenderOptions.summaryPanel` / `GalleryOptions.summaryPanel`),
20
+ * since that panel is this link's only home; irrelevant otherwise.
21
+ *
22
+ * Has no effect on the coordinate-less map replacement or the "+N dies
23
+ * without position" footer, both of which show a die list unconditionally
24
+ * regardless of this flag — those exist because there is literally nothing
25
+ * else to show, not as an opt-in extra. The rest of this options object
26
+ * (column selection, `maxRows`, …) is shared by both surfaces.
17
27
  */
18
- extraColumn?: {
19
- label: string;
20
- get: (die: Die) => string | undefined;
21
- };
28
+ enabled?: boolean;
29
+ /**
30
+ * `die.metadata` keys to show as columns. Default `'auto'` — every key
31
+ * present on any die, deterministically ordered. `'none'` omits them; an
32
+ * explicit array pins both the set and the order.
33
+ *
34
+ * Unlike `metadataFields` (which gates the `'metadata'` PLOT mode, where a
35
+ * key must be categorical enough for a legend), a table column has no
36
+ * cardinality limit — one cell per die, whatever the value — so this
37
+ * defaults to on. An export that silently drops host data is worse than a
38
+ * wide one.
39
+ */
40
+ metadataColumns?: MetadataKeySelection;
41
+ /**
42
+ * Where wafer-level metadata (lot, wafer id, product, …) appears.
43
+ * - `'csv'` (default): CSV only. It is constant down every row, so on
44
+ * screen it is pure noise beside the metadata badge that already shows
45
+ * it — but a CSV travels alone, and per-row identity is what lets
46
+ * several exports be concatenated and pivoted later.
47
+ * - `'both'`: also as table columns.
48
+ * - `'none'`: omitted entirely.
49
+ */
50
+ waferMetadataColumns?: 'csv' | 'both' | 'none';
51
+ /**
52
+ * Cap the number of rows **rendered** in the DOM. Default `50_000`. The CSV
53
+ * export is never capped — it always contains every die — and a footer
54
+ * states the truncation explicitly when it applies, since a silently
55
+ * truncated view is exactly the kind of unlabelled filtered population
56
+ * this library's own display rules forbid.
57
+ *
58
+ * Exists because this table has no virtualisation: each row is real DOM.
59
+ * At lot scale (a quarter-million dies is a realistic STDF batch) an
60
+ * unbounded table is tens of seconds and several hundred MB before any
61
+ * metadata column is added. Set `0` to skip the table entirely and offer
62
+ * only the CSV export.
63
+ */
64
+ maxRows?: number;
22
65
  /**
23
66
  * Cap the scrolling table at this CSS length instead of letting it fill.
24
67
  *
@@ -32,6 +75,29 @@ export interface DieListOptions {
32
75
  * directly into a long document).
33
76
  */
34
77
  maxHeight?: string;
78
+ /** CSV filename, including extension. Default: `'dies.csv'`. */
79
+ csvFilename?: string;
80
+ }
81
+ export interface DieListOptions extends DieListDisplayOptions {
82
+ /** Heading text. Default: `Die list (N)`. */
83
+ title?: string;
84
+ /** Optional note shown under the heading, e.g. explaining why this replaced the map. */
85
+ note?: string;
86
+ /** Host hook for "Export CSV" — see `saveTextFile` (toolbar.ts). */
87
+ onSaveText?: SaveTextHandler;
88
+ /**
89
+ * Extra leading column, e.g. a wafer-id label for a lot-level combined
90
+ * list spanning multiple wafers. `get(die)` returning `undefined` renders
91
+ * an empty cell for that row.
92
+ */
93
+ extraColumn?: {
94
+ label: string;
95
+ get: (die: Die) => string | undefined;
96
+ };
97
+ /** Wafer-level metadata for this population, e.g. `WaferMapResult.metadata`. */
98
+ waferMetadata?: WaferMetadata;
99
+ /** Label/order hints for die metadata columns, e.g. `WaferMapResult.metadataFields`. */
100
+ metadataFields?: MetadataFieldDef[];
35
101
  }
36
102
  /**
37
103
  * Build a scrollable die-list table + "Export CSV" button. Returns `null`
@@ -1,2 +1,13 @@
1
- import{hasPosition as S}from"../core/dies.js";import{isParametricTest as v}from"../renderer/buildWaferMap.js";import{CLR as r,saveTextFile as w}from"./toolbar.js";import{csvField as C}from"./summaryPanel.js";import{fmt as y}from"../renderer/fmt.js";function o(n,d,t){const l=document.createElement(n);return d&&Object.assign(l.style,d),t!==void 0&&(l.textContent=t),l}function B(n){return S(n)?`(${n.x}, ${n.y})`:"\u2014"}function $(n,d){if(d?.length)return d.filter(v);const t=new Map;for(const l of n)for(const a of Object.keys(l.testValues??{})){const s=Number(a);t.has(s)||t.set(s,{testNumber:s,name:`Test ${s}`})}return[...t.values()].sort((l,a)=>l.testNumber-a.testNumber)}export function buildDieListSection(n,d,t={}){if(!n.length)return null;const l=$(n,d),a=o("div",{display:"flex",flexDirection:"column",gap:"8px",flex:"1",minHeight:"0"}),s=o("div",{display:"flex",alignItems:"center",justifyContent:"space-between",flexShrink:"0"});s.appendChild(o("div",{fontSize:"10px",fontWeight:"700",letterSpacing:"0.06em",textTransform:"uppercase",color:r.label},t.title??`Die list (${n.length})`));const f=o("button",{fontSize:"11px",padding:"3px 8px",borderRadius:"4px",border:`1px solid ${r.menuBorder}`,background:r.menuBg,color:r.text,cursor:"pointer"},"Export CSV");f.type="button",s.appendChild(f),a.appendChild(s),t.note&&a.appendChild(o("div",{fontSize:"11px",color:r.label,lineHeight:"1.4",flexShrink:"0"},t.note));const m=o("div",{overflow:"auto",flex:"1",minHeight:"0",border:`1px solid ${r.menuBorder}`,borderRadius:"4px",...t.maxHeight?{maxHeight:t.maxHeight}:{}}),c=o("table",{width:"100%",borderCollapse:"collapse",fontSize:"11px"});c.setAttribute("aria-label",t.title??`Die list (${n.length} dies)`);const u=[...t.extraColumn?[{label:t.extraColumn.label,get:e=>t.extraColumn.get(e)??""}]:[],{label:"Position",get:B},{label:"Site",get:e=>e.siteNum!==void 0?String(e.siteNum):""},{label:"Hard bin",get:e=>e.hbin!==void 0?String(e.hbin):""},{label:"Soft bin",get:e=>e.sbin!==void 0?String(e.sbin):""},...l.map(e=>({label:e.name,get:i=>{const p=i.testValues?.[e.testNumber];if(p!==void 0)return y(p,e.unit);const g=i.testPass?.[e.testNumber];return g===void 0?"":g?"PASS":"FAIL"}}))],b=o("thead"),x=o("tr");for(const e of u){const i=o("th",{textAlign:"left",padding:"4px 8px",position:"sticky",top:"0",background:r.panelBg,color:r.label,fontWeight:"600",borderBottom:`1px solid ${r.menuBorder}`,whiteSpace:"nowrap"},e.label);i.scope="col",x.appendChild(i)}b.appendChild(x),c.appendChild(b);const h=o("tbody");for(const e of n){const i=o("tr");for(const p of u)i.appendChild(o("td",{padding:"3px 8px",color:r.text,borderBottom:`1px solid ${r.menuBorder}`,whiteSpace:"nowrap"},p.get(e)));h.appendChild(i)}return c.appendChild(h),m.appendChild(c),a.appendChild(m),f.addEventListener("click",()=>{const e=[u.map(i=>C(i.label)).join(",")];for(const i of n)e.push(u.map(p=>C(p.get(i))).join(","));w(e.join(`
2
- `),t.csvFilename??"dies.csv","text/csv",t.onSaveText)}),a}
1
+ import{hasPosition as k}from"../core/dies.js";import{isParametricTest as H}from"../renderer/buildWaferMap.js";import{resolveMetadataColumns as R}from"../stats/metadataColumns.js";import{CLR as a,saveTextFile as E}from"./toolbar.js";import{csvField as y}from"./summaryPanel.js";import{fmt as M}from"../renderer/fmt.js";const T=5e4;let $=!1;function B(){if($)return;$=!0;const n=document.createElement("style");n.textContent=`
2
+ .wmap-dielist-table { width: 100%; border-collapse: collapse; font-size: 11px; }
3
+ .wmap-dielist-th {
4
+ text-align: left; padding: 4px 8px; position: sticky; top: 0;
5
+ background: ${a.panelBg}; color: ${a.label}; font-weight: 600;
6
+ border-bottom: 1px solid ${a.menuBorder}; white-space: nowrap;
7
+ }
8
+ .wmap-dielist-td {
9
+ padding: 3px 8px; color: ${a.text};
10
+ border-bottom: 1px solid ${a.menuBorder}; white-space: nowrap;
11
+ }
12
+ `,document.head.appendChild(n)}function o(n,s,t){const i=document.createElement(n);return s&&Object.assign(i.style,s),t!==void 0&&(i.textContent=t),i}function F(n){return k(n)?`(${n.x}, ${n.y})`:"\u2014"}function P(n,s){if(s?.length)return s.filter(H);const t=new Map;for(const i of n)for(const d of Object.keys(i.testValues??{})){const r=Number(d);t.has(r)||t.set(r,{testNumber:r,name:`Test ${r}`})}return[...t.values()].sort((i,d)=>i.testNumber-d.testNumber)}export function buildDieListSection(n,s,t={}){if(!n.length)return null;B();const i=P(n,s),d=t.maxRows??T,r=Math.max(0,n.length-d),N=r>0?n.slice(0,d):n,u=o("div",{display:"flex",flexDirection:"column",gap:"8px",flex:"1",minHeight:"0"}),h=o("div",{display:"flex",alignItems:"center",justifyContent:"space-between",flexShrink:"0"});h.appendChild(o("div",{fontSize:"10px",fontWeight:"700",letterSpacing:"0.06em",textTransform:"uppercase",color:a.label},t.title??`Die list (${n.length})`));const b=o("button",{fontSize:"11px",padding:"3px 8px",borderRadius:"4px",border:`1px solid ${a.menuBorder}`,background:a.menuBg,color:a.text,cursor:"pointer"},"Export CSV");b.type="button",h.appendChild(b),u.appendChild(h),t.note&&u.appendChild(o("div",{fontSize:"11px",color:a.label,lineHeight:"1.4",flexShrink:"0"},t.note));const L=[...t.extraColumn?[t.extraColumn.label]:[],"Position","Site","Hard bin","Soft bin",...i.map(e=>e.name)],{columns:j,truncatedKeys:f}=R({dies:n,waferMetadata:t.waferMetadata,metadataFields:t.metadataFields,dieKeys:t.metadataColumns,waferKeys:t.waferMetadataColumns==="none"?"none":void 0,waferPlacement:t.waferMetadataColumns??"csv",reservedLabels:L}),x=[...t.extraColumn?[{label:t.extraColumn.label,get:e=>t.extraColumn.get(e)??""}]:[],{label:"Position",get:F},{label:"Site",get:e=>e.siteNum!==void 0?String(e.siteNum):""},{label:"Hard bin",get:e=>e.hbin!==void 0?String(e.hbin):""},{label:"Soft bin",get:e=>e.sbin!==void 0?String(e.sbin):""},...i.map(e=>({label:e.name,get:l=>{const c=l.testValues?.[e.testNumber];if(c!==void 0)return M(c,e.unit);const m=l.testPass?.[e.testNumber];return m===void 0?"":m?"PASS":"FAIL"}})),...j.map(e=>({label:e.label,get:e.get,csvOnly:e.csvOnly}))],g=x.filter(e=>!e.csvOnly),C=o("div",{overflow:"auto",flex:"1",minHeight:"0",border:`1px solid ${a.menuBorder}`,borderRadius:"4px",...t.maxHeight?{maxHeight:t.maxHeight}:{}}),p=document.createElement("table");p.className="wmap-dielist-table",p.setAttribute("aria-label",t.title??`Die list (${n.length} dies)`);const w=o("thead"),S=o("tr");for(const e of g){const l=document.createElement("th");l.className="wmap-dielist-th",l.scope="col",l.textContent=e.label,S.appendChild(l)}w.appendChild(S),p.appendChild(w);const v=o("tbody");for(const e of N){const l=o("tr");for(const c of g){const m=document.createElement("td");m.className="wmap-dielist-td",m.textContent=c.get(e),l.appendChild(m)}v.appendChild(l)}if(p.appendChild(v),C.appendChild(p),u.appendChild(C),r>0||f.length>0){const e=[];r>0&&e.push(`Showing the first ${d.toLocaleString()} of ${n.length.toLocaleString()} dies. The CSV export contains all ${n.length.toLocaleString()}.`),f.length>0&&e.push(`${f.length} further metadata field${f.length===1?"":"s"} not shown.`),u.appendChild(o("div",{fontSize:"11px",color:a.label,lineHeight:"1.4",flexShrink:"0"},e.join(" ")))}return b.addEventListener("click",()=>{const e=[x.map(l=>y(l.label)).join(",")];for(const l of n)e.push(x.map(c=>y(c.get(l))).join(","));E(e.join(`
13
+ `),t.csvFilename??"dies.csv","text/csv",t.onSaveText)}),u}
@@ -6,7 +6,7 @@ export { renderWaferGallery } from './renderWaferGallery.js';
6
6
  export type { WaferMapDisplayItem, WaferMapDisplayItemFactory, GalleryOptions, GalleryController } from './renderWaferGallery.js';
7
7
  export type { SummaryPanelOptions } from './summaryPanel.js';
8
8
  export { buildDieListSection } from './dieList.js';
9
- export type { DieListOptions } from './dieList.js';
9
+ export type { DieListOptions, DieListDisplayOptions } from './dieList.js';
10
10
  export { collectWarnings, severityOf } from './warnings.js';
11
11
  export type { WarningsOptions } from './warnings.js';
12
12
  export type { InsightsOptions, InsightsView } from './insightsTab.js';
@@ -6,6 +6,7 @@ import type { LotStatsSummary } from '../stats/types.js';
6
6
  import { type WarningsOptions } from './warnings.js';
7
7
  import type { SummaryPanelOptions } from './summaryPanel.js';
8
8
  import { type InsightsOptions } from './insightsTab.js';
9
+ import type { DieListDisplayOptions } from './dieList.js';
9
10
  /**
10
11
  * The data and display overrides for a single gallery card.
11
12
  *
@@ -87,6 +88,15 @@ export interface GalleryOptions {
87
88
  * map for a finding to highlight against.
88
89
  */
89
90
  summaryPanel?: SummaryPanelOptions;
91
+ /**
92
+ * Display preferences for the lot-wide "View die list" link inside the
93
+ * Summary panel — every die across every wafer, pooled, with a Wafer
94
+ * column and CSV export. **On by default** whenever `summaryPanel` is
95
+ * reachable at all, since that panel is the link's only home; pass
96
+ * `{ enabled: false }` to hide it. See `DieListDisplayOptions` and wmap's
97
+ * own coordinate-less die-list table, which this reuses.
98
+ */
99
+ dieList?: DieListDisplayOptions;
90
100
  /**
91
101
  * Bin numbers treated as pass for yield calculation in the summary panel.
92
102
  * Defaults to `[1]`. Must match the `passBins` passed to `analyzeWaferLot` / `buildWaferMap`
@@ -1,4 +1,4 @@
1
- import{getUniqueTestNumbers as yn,resolveTestNumber as xn,findTestDef as wn}from"../renderer/buildView.js";import{getColorScheme as vn}from"../renderer/colorSchemes.js";import{metadataValueColor as Cn}from"../renderer/colorMap.js";import{resolveCanvasTheme as Sn}from"./canvasTheme.js";import{ICONS as pe}from"./icons.js";import{CLR as m,sevColor as Mt,BIN_LEGEND_MODES as Mn,STACKED_MODES as ue,Z_ABOVE as kt,applyOverlayZ as kn,getTooltip as Bn,hideTooltip as Bt,createToolbarHelpers as En,buildModeMenuEl as On,openDetachWindow as Dn,openFloatingWindow as Tn,openModal as Wn,copyWmapThemeTokens as Ln,syncWmapPopupTheme as In,openUserGuideWindow as Rn,makePaletteBtn as Pn,makeLogScaleBtn as An,makeLegendStyleBtn as Fn,makeOverlaysBtn as Hn,makeOrientationBtn as $n,overlayRootFor as Et,saveImageBlob as jn,markMenuTrigger as me,wireMenuA11y as Ot,wireExpandToggle as Dt,passFailMenuRows as zn,requestedPassFailDisplay as Ge,logWmapVersionOnce as Nn,buildDataModeEntries as Vn,metadataKeyHasData as _n,metadataModeEntry as Gn}from"./toolbar.js";import{aggregateValues as Kn,aggregateBinCounts as Tt}from"../core/aggregates.js";import{renderWaferMap as Ke}from"./renderWaferMap.js";import{classifyChanged as qn}from"./renderWaferMap.js";import{buildWaferMap as Xn,getTestPassStatus as Un,isParametricTest as Wt}from"../renderer/buildWaferMap.js";import{analyzeWaferMap as Ee}from"../stats/analyzeWaferMap.js";import{collectWarnings as Yn,buildWarningsMenuEl as Qn,severityOf as Zn}from"./warnings.js";import{compareNatural as Jn}from"../core/utils.js";import{createSummaryPanelEl as qe,buildMetadataStripRow as ei,buildCompactMetadataRows as Lt,metadataEntries as It,renderLotSummaryContent as ti}from"./summaryPanel.js";import"../stats/facets.js";import{openHtmlReport as ni}from"../stats/renderFindingsReport.js";import{escHtml as ge,renderSection as ii,renderSeverityBadge as ai,reportStyles as oi}from"../stats/reportHtml.js";import{createInsightsTab as li}from"./insightsTab.js";import{getDieKey as si,hasPosition as ri}from"../core/dies.js";function Oe(S){const B=new Set;return S.filter(u=>B.has(u.bin)?!1:(B.add(u.bin),!0))}function Rt(S,B){const u=document.createElement("div");Object.assign(u.style,{display:"flex",alignItems:"center",gap:"5px",cursor:"pointer",userSelect:"none",padding:"2px 4px",borderRadius:"3px"});const Y=document.createElement("span");Object.assign(Y.style,{display:"inline-block",width:"13px",height:"13px",flexShrink:"0",background:B.color,border:B.isActive?`2px solid ${m.iconActive}`:`1px solid ${m.menuBorder}`,borderRadius:"2px",boxSizing:"border-box"});const te=document.createElement("span");te.textContent=B.label,Object.assign(te.style,{fontWeight:B.isActive?"700":"400",color:B.isActive?m.iconActive:m.text,whiteSpace:"nowrap"}),u.appendChild(Y),u.appendChild(te),u.setAttribute("role","button"),u.setAttribute("aria-pressed",B.isActive?"true":"false"),u.tabIndex=0,u.addEventListener("mouseenter",()=>{u.style.background=m.bgHover}),u.addEventListener("mouseleave",()=>{u.style.background="transparent"}),u.addEventListener("focus",()=>{u.style.background=m.bgHover}),u.addEventListener("blur",()=>{u.style.background="transparent"}),u.addEventListener("click",B.onClick),u.addEventListener("keydown",L=>{(L.key==="Enter"||L.key===" ")&&(L.preventDefault(),B.onClick())}),S.appendChild(u)}export function renderWaferGallery(S,B,u={}){Nn();const Y=u.cardPadding??6,te=u.downloadFilename??"wafer-gallery";let L=u.columns;const Pt=u.showPlotModeSelector??!0,At=u.showHelpButton??!1,Ft=u.userGuideExtension,Xe=u.insights?.enabled??!1,Ht=kn(u.zIndex),Q=u.summaryPanel,Ue=u.passBins??[1];let he=u.fallbackFormat,D=u.lotStatsSummary,ne=u.legendPosition??"default",d={plotMode:"hardBin",colorScheme:"default",showDieLabels:!1,showRingBoundaries:!1,showQuadrantBoundaries:!1,ringCount:4,rotation:0,flipX:!1,flipY:!1,...u.viewOptions},w=[],q=[],Z=[],M=[],y=B.map(e=>typeof e=="function"?null:e),De=0,be=0;const R=new Map;let $t=0,T=null,ie=null,Ye=new Set,p=null,ae="lot",jt={};function Qe(e){return y[e]?.statsSummary??D?.perWafer.find(t=>t.waferIndex===e)?.summary}function ye(){return!!(y.some(e=>e?.statsSummary?.findings.length)||D?.perWafer.some(e=>e.summary.findings.length))}const zt=Bn(),J=En(zt),{makeBtn:V,setActive:oe,makeSep:P,makeMenuRow:Nt,makeMenuSection:Vt,makeDropdown:Ze,makeCheckMenuBtn:di,closeOpenMenu:xe,getOpenMenu:le,setOpenMenu:se}=J;S.ownerDocument.addEventListener("click",xe,!0);const Je=()=>Bt();window.addEventListener("blur",Je);function Te(e){Ye=new Set(e);const t=[...O.querySelectorAll(".wmap-gallery-card")];let n;t.forEach((i,s)=>{const c=Ye.has(s);i.style.outline=c?`3px solid ${m.findingHighlight}`:"",i.style.outlineOffset=c?"-3px":"",c&&n===void 0&&(n=i)}),n?.scrollIntoView?.({behavior:"smooth",block:"nearest"})}function We(){Te([])}function Le(){for(const e of w)e&&e.clearSelection()}function Ie(e,t){const n=new Set(e),i=t??w.map((s,c)=>c);for(const s of i){const c=M[s],a=w[s];if(!c||!a)continue;const r=c.dies.filter(o=>n.has(si(o)));a.setSelection(r)}}function et(){const e=document.createElement("div");Object.assign(e.style,{display:"flex",gap:"4px",marginBottom:"10px",borderBottom:`1px solid ${m.menuBorder}`,paddingBottom:"8px"});for(const t of["lot","wafers"]){const n=document.createElement("button");n.type="button",n.textContent=t==="lot"?"Lot":"Wafers";const i=ae===t;Object.assign(n.style,{border:"none",borderRadius:"4px",padding:"2px 8px",fontSize:"11px",cursor:"pointer",fontWeight:i?"600":"400",background:i?m.bgActive:"transparent",color:i?m.iconHover:m.icon}),n.addEventListener("click",()=>{ae=t,We(),Le(),I()}),e.appendChild(n)}return e}function _t(){if(!p)return;p.innerHTML="",D&&p.appendChild(et());const e=[];for(let n=0;n<y.length;n++){const i=y[n];if(!i)continue;const s=Qe(n)?.findings??[];if(!s.length)continue;const c=s.filter(r=>r.severity==="unusual").length,a=s.filter(r=>r.severity==="notable").length;e.push({index:n,item:i,unusualCount:c,notableCount:a,totalCount:s.length})}if(e.length>0){const n=document.createElement("button");n.type="button",n.textContent="Findings report",Object.assign(n.style,{border:`1px solid ${m.menuBorder}`,borderRadius:"4px",padding:"3px 8px",marginBottom:"10px",fontSize:"10px",color:m.iconHover,background:"none",cursor:"pointer",display:"block",width:"100%",textAlign:"left"}),n.addEventListener("click",()=>{const i=`Findings report \u2014 ${e.length} wafer${e.length>1?"s":""}`,s=new Date().toLocaleString(),c=e.map(({item:r,index:o})=>{const l=Qe(o),f=r.label??`W${o+1}`,h=l.findings.map(g=>`<li>${ai(g.severity)} ${ge(g.summary)}</li>`).join("");return ii(ge(f),`<ul style="margin:0;padding-left:18px;list-style:none">${h}</ul>`)}).join(`
1
+ import{getUniqueTestNumbers as yn,resolveTestNumber as xn,findTestDef as wn}from"../renderer/buildView.js";import{getColorScheme as vn}from"../renderer/colorSchemes.js";import{metadataValueColor as Cn}from"../renderer/colorMap.js";import{resolveCanvasTheme as Sn}from"./canvasTheme.js";import{ICONS as pe}from"./icons.js";import{CLR as m,sevColor as Mt,BIN_LEGEND_MODES as Mn,STACKED_MODES as ue,Z_ABOVE as kt,applyOverlayZ as kn,getTooltip as Bn,hideTooltip as Bt,createToolbarHelpers as En,buildModeMenuEl as On,openDetachWindow as Dn,openFloatingWindow as Tn,openModal as Wn,copyWmapThemeTokens as Ln,syncWmapPopupTheme as In,openUserGuideWindow as Rn,makePaletteBtn as Pn,makeLogScaleBtn as An,makeLegendStyleBtn as Fn,makeOverlaysBtn as Hn,makeOrientationBtn as $n,overlayRootFor as Et,saveImageBlob as jn,markMenuTrigger as me,wireMenuA11y as Ot,wireExpandToggle as Dt,passFailMenuRows as zn,requestedPassFailDisplay as Ge,logWmapVersionOnce as Nn,buildDataModeEntries as Vn,metadataKeyHasData as _n,metadataModeEntry as Gn}from"./toolbar.js";import{aggregateValues as Kn,aggregateBinCounts as Tt}from"../core/aggregates.js";import{renderWaferMap as Ke}from"./renderWaferMap.js";import{classifyChanged as qn}from"./renderWaferMap.js";import{buildWaferMap as Xn,getTestPassStatus as Un,isParametricTest as Wt}from"../renderer/buildWaferMap.js";import{analyzeWaferMap as Ee}from"../stats/analyzeWaferMap.js";import{collectWarnings as Yn,buildWarningsMenuEl as Qn,severityOf as Zn}from"./warnings.js";import{compareNatural as Jn}from"../core/utils.js";import{createSummaryPanelEl as qe,buildMetadataStripRow as ei,buildCompactMetadataRows as Lt,metadataEntries as It,renderLotSummaryContent as ti}from"./summaryPanel.js";import"../stats/facets.js";import{openHtmlReport as ni}from"../stats/renderFindingsReport.js";import{escHtml as ge,renderSection as ii,renderSeverityBadge as ai,reportStyles as oi}from"../stats/reportHtml.js";import{createInsightsTab as li}from"./insightsTab.js";import{getDieKey as si,hasPosition as ri}from"../core/dies.js";function Oe(S){const B=new Set;return S.filter(p=>B.has(p.bin)?!1:(B.add(p.bin),!0))}function Rt(S,B){const p=document.createElement("div");Object.assign(p.style,{display:"flex",alignItems:"center",gap:"5px",cursor:"pointer",userSelect:"none",padding:"2px 4px",borderRadius:"3px"});const Y=document.createElement("span");Object.assign(Y.style,{display:"inline-block",width:"13px",height:"13px",flexShrink:"0",background:B.color,border:B.isActive?`2px solid ${m.iconActive}`:`1px solid ${m.menuBorder}`,borderRadius:"2px",boxSizing:"border-box"});const te=document.createElement("span");te.textContent=B.label,Object.assign(te.style,{fontWeight:B.isActive?"700":"400",color:B.isActive?m.iconActive:m.text,whiteSpace:"nowrap"}),p.appendChild(Y),p.appendChild(te),p.setAttribute("role","button"),p.setAttribute("aria-pressed",B.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",B.onClick),p.addEventListener("keydown",L=>{(L.key==="Enter"||L.key===" ")&&(L.preventDefault(),B.onClick())}),S.appendChild(p)}export function renderWaferGallery(S,B,p={}){Nn();const Y=p.cardPadding??6,te=p.downloadFilename??"wafer-gallery";let L=p.columns;const Pt=p.showPlotModeSelector??!0,At=p.showHelpButton??!1,Ft=p.userGuideExtension,Xe=p.insights?.enabled??!1,Ht=kn(p.zIndex),Q=p.summaryPanel,Ue=p.passBins??[1];let he=p.fallbackFormat,D=p.lotStatsSummary,ne=p.legendPosition??"default",d={plotMode:"hardBin",colorScheme:"default",showDieLabels:!1,showRingBoundaries:!1,showQuadrantBoundaries:!1,ringCount:4,rotation:0,flipX:!1,flipY:!1,...p.viewOptions},w=[],q=[],Z=[],M=[],y=B.map(e=>typeof e=="function"?null:e),De=0,be=0;const R=new Map;let $t=0,T=null,ie=null,Ye=new Set,u=null,ae="lot",jt={};function Qe(e){return y[e]?.statsSummary??D?.perWafer.find(t=>t.waferIndex===e)?.summary}function ye(){return!!(y.some(e=>e?.statsSummary?.findings.length)||D?.perWafer.some(e=>e.summary.findings.length))}const zt=Bn(),J=En(zt),{makeBtn:V,setActive:oe,makeSep:P,makeMenuRow:Nt,makeMenuSection:Vt,makeDropdown:Ze,makeCheckMenuBtn:di,closeOpenMenu:xe,getOpenMenu:le,setOpenMenu:se}=J;S.ownerDocument.addEventListener("click",xe,!0);const Je=()=>Bt();window.addEventListener("blur",Je);function Te(e){Ye=new Set(e);const t=[...O.querySelectorAll(".wmap-gallery-card")];let n;t.forEach((i,s)=>{const c=Ye.has(s);i.style.outline=c?`3px solid ${m.findingHighlight}`:"",i.style.outlineOffset=c?"-3px":"",c&&n===void 0&&(n=i)}),n?.scrollIntoView?.({behavior:"smooth",block:"nearest"})}function We(){Te([])}function Le(){for(const e of w)e&&e.clearSelection()}function Ie(e,t){const n=new Set(e),i=t??w.map((s,c)=>c);for(const s of i){const c=M[s],a=w[s];if(!c||!a)continue;const r=c.dies.filter(o=>n.has(si(o)));a.setSelection(r)}}function et(){const e=document.createElement("div");Object.assign(e.style,{display:"flex",gap:"4px",marginBottom:"10px",borderBottom:`1px solid ${m.menuBorder}`,paddingBottom:"8px"});for(const t of["lot","wafers"]){const n=document.createElement("button");n.type="button",n.textContent=t==="lot"?"Lot":"Wafers";const i=ae===t;Object.assign(n.style,{border:"none",borderRadius:"4px",padding:"2px 8px",fontSize:"11px",cursor:"pointer",fontWeight:i?"600":"400",background:i?m.bgActive:"transparent",color:i?m.iconHover:m.icon}),n.addEventListener("click",()=>{ae=t,We(),Le(),I()}),e.appendChild(n)}return e}function _t(){if(!u)return;u.innerHTML="",D&&u.appendChild(et());const e=[];for(let n=0;n<y.length;n++){const i=y[n];if(!i)continue;const s=Qe(n)?.findings??[];if(!s.length)continue;const c=s.filter(r=>r.severity==="unusual").length,a=s.filter(r=>r.severity==="notable").length;e.push({index:n,item:i,unusualCount:c,notableCount:a,totalCount:s.length})}if(e.length>0){const n=document.createElement("button");n.type="button",n.textContent="Findings report",Object.assign(n.style,{border:`1px solid ${m.menuBorder}`,borderRadius:"4px",padding:"3px 8px",marginBottom:"10px",fontSize:"10px",color:m.iconHover,background:"none",cursor:"pointer",display:"block",width:"100%",textAlign:"left"}),n.addEventListener("click",()=>{const i=`Findings report \u2014 ${e.length} wafer${e.length>1?"s":""}`,s=new Date().toLocaleString(),c=e.map(({item:r,index:o})=>{const l=Qe(o),f=r.label??`W${o+1}`,h=l.findings.map(g=>`<li>${ai(g.severity)} ${ge(g.summary)}</li>`).join("");return ii(ge(f),`<ul style="margin:0;padding-left:18px;list-style:none">${h}</ul>`)}).join(`
2
2
  `),a=`<!DOCTYPE html>
3
3
  <html lang="en">
4
4
  <head>
@@ -17,4 +17,4 @@ ${oi()}
17
17
  ${c}
18
18
  </main>
19
19
  </body>
20
- </html>`;ni(a)}),p.appendChild(n)}if(e.length===0){const n=document.createElement("div");Object.assign(n.style,{color:m.icon,fontSize:"11px",padding:"4px 0"}),n.textContent="No findings on any wafer.",p.appendChild(n)}else for(const{index:n,item:i,unusualCount:s,notableCount:c,totalCount:a}of e){const r=s?"unusual":c?"notable":"info",o=s+c||a,l=document.createElement("button");l.type="button",Object.assign(l.style,{display:"flex",alignItems:"center",justifyContent:"space-between",width:"100%",padding:"5px 8px",marginBottom:"4px",border:"none",borderLeft:`3px solid ${Mt(r)}`,borderRadius:"3px",background:m.bgHover,cursor:"pointer",fontSize:"11px",textAlign:"left",boxSizing:"border-box"}),l.addEventListener("mouseover",()=>{l.style.background=m.bgActive}),l.addEventListener("mouseout",()=>{l.style.background=m.bgHover});const f=document.createElement("span");f.textContent=i.label??`W${n+1}`,Object.assign(f.style,{color:m.iconHover,overflow:"hidden",textOverflow:"ellipsis",whiteSpace:"nowrap"});const h=document.createElement("span");h.textContent=String(o),Object.assign(h.style,{marginLeft:"6px",flexShrink:"0",background:Mt(r),color:"#fff",borderRadius:"8px",padding:"1px 5px",fontSize:"10px",fontWeight:"600"}),l.appendChild(f),l.appendChild(h),l.addEventListener("click",()=>ke(n,i)),p.appendChild(l)}const t=document.createElement("div");t.style.height="12px",p.appendChild(t)}function I(){if(p)if(ae==="lot"&&D){const e=Oe(y.flatMap(n=>n?.hbinDefs??[])),t=Oe(y.flatMap(n=>n?.sbinDefs??[]));ti(p,{lotSummary:D,items:y,hbinDefs:e.length?e:void 0,sbinDefs:t.length?t:void 0,testDefs:y.find(n=>n?.testDefs?.length)?.testDefs,passBins:Ue,ringCount:d.ringCount,colorScheme:d.colorScheme,fallbackFormat:he,activeFindingId:ie,warnings:u.warnings?.display??!0?_:[],findingsFilter:jt,onFindingsFilterChange:I,onSaveText:u.onSaveText,onFindingClick:(n,i)=>{ie===n.id?Re():Gt(n,i),I()},onWaferClick:n=>{Te([n])}}),ye()&&p.insertBefore(et(),p.firstChild)}else _t()}function tt(e){return[e.variable.kind,e.variable.index??"",e.variable.bin??"",e.comparison.family,e.comparison.left,e.effect.direction].join("|")}function Re(){ie=null,We(),Le(),v({highlightBin:void 0},{fireCallback:!1})}function Gt(e,t){if(ie===e.id){Re();return}ie=e.id,t.style.background=m.bgActive,t.style.fontWeight="600";const{kind:n,index:i}=e.variable;n==="test"?v({plotMode:"value",activeTest:i??0,highlightBin:void 0},{fireCallback:!1}):n==="softBin"?v({plotMode:"softBin",highlightBin:void 0},{fireCallback:!1}):v({plotMode:"hardBin",highlightBin:void 0},{fireCallback:!1}),We(),Le();const s=e.highlight;if(s.kind==="wafer"){Te(s.waferIndices);const c=tt(e);for(const a of s.waferIndices){const l=M[a]?.statsSummary?.findings.find(f=>tt(f)===c)?.highlight?.dieKeys;l?.length&&Ie(l,[a])}}else s.kind==="bin"?s.dieKeys?.length&&Ie(s.dieKeys):(s.kind==="region"||s.kind==="dies")&&s.dieKeys?.length&&Ie(s.dieKeys)}function X(){if(!T)return;const e=!!p;T.style.display=(D||ye())&&e&&!Ae?"flex":"none";const t=p?p.style.display!=="none":!1;(D?.hasNotableFindings||y.some(i=>i?.statsSummary?.hasNotableFindings))&&!t?T.style.color=m.findingIndicator:T.dataset.active||(T.style.color=m.icon)}const E=document.createElement("div");E.dataset.wmapToolbar="gallery",Object.assign(E.style,{display:"inline-flex",flexDirection:"row",alignItems:"center",gap:"0",background:m.menuBg,border:`1px solid ${m.menuBorder}`,borderRadius:"6px",padding:"3px 4px",marginBottom:"10px",boxShadow:"0 1px 4px rgba(0,0,0,0.10)",flexWrap:"wrap",minWidth:"0",overflowX:"auto"});const nt=()=>{const e=le();e&&(e.remove(),se(null)),me($,!1)},$=V("mode","Plot mode",()=>{if(le()){nt();return}const e=y.flatMap(g=>g?.dies??[]),t=y.find(g=>g?.testDefs?.length)?.testDefs,{testEntries:n,binEntries:i,stackedEntries:s}=Vn(e,t,{includeStacked:!0}),c=d.plotMode??"hardBin",a=d.activeTest??0;function r(g){return g.plotMode!==c?!1:g.plotMode==="value"?a===(g.activeTest??0):g.plotMode==="metadata"?d.activeMetadataKey===g.activeMetadataKey:!0}function o(g,k){g.activeTest!==void 0?v({plotMode:"value",activeTest:g.activeTest,activeMetadataKey:void 0,logScale:g.logScale}):g.activeMetadataKey!==void 0?v({plotMode:g.plotMode,activeTest:void 0,activeMetadataKey:g.activeMetadataKey,passFailDisplay:"off"}):v({plotMode:g.plotMode,activeTest:void 0,activeMetadataKey:void 0,passFailDisplay:"off"}),k.remove(),se(null),me($,!1)}const l=new Set,f=y.flatMap(g=>g?.metadataFields??[]).filter(g=>!l.has(g.key)&&l.add(g.key)).filter(g=>_n(e,g.key)).map(Gn),h=On($.getBoundingClientRect(),n,i,s,r,o,{makeMenuRow:Nt,makeMenuSection:Vt},c,$.ownerDocument.defaultView??window,f);Et($).appendChild(h),se(h),me($,!0),Ot(h,$,nt)});me($,!1);const Kt=()=>M.flatMap(e=>e?[...e.hbinDefs??[],...e.sbinDefs??[]]:[]).some(e=>e.color),{btn:qt,sync:it}=Pn(J,()=>d.plotMode??"hardBin",()=>d.colorScheme??"default",Kt,e=>v({colorScheme:e}));it();const Xt=B.some(e=>typeof e!="function"&&(e.reticles?.length??0)>0);function Pe(){const e=y.find(n=>n?.testDefs?.length)?.testDefs,{testNumber:t}=xn(d.activeTest??0,e);return{testNumber:t,td:wn(e,t)}}function at(){if((d.plotMode??"hardBin")!=="value")return!1;const{td:e}=Pe();return e!==void 0&&(e.limitLow!==void 0||e.limitHigh!==void 0)}function re(){if((d.plotMode??"hardBin")!=="value")return!1;const{td:e}=Pe();return e!==void 0&&!Wt(e)}function Ut(){if((d.plotMode??"hardBin")!=="value")return!1;const{testNumber:e,td:t}=Pe();return y.some(n=>n?.dies?.some(i=>Un(i,e,t)!==void 0))}const Yt=Hn(J,()=>[{label:"Ring boundaries",active:!!d.showRingBoundaries,onClick:()=>v({showRingBoundaries:!d.showRingBoundaries})},{label:"Quadrant lines",active:!!d.showQuadrantBoundaries,onClick:()=>v({showQuadrantBoundaries:!d.showQuadrantBoundaries})},{label:"Die labels",active:!!d.showDieLabels,onClick:()=>v({showDieLabels:!d.showDieLabels})},{label:"Reticle grid",active:!!d.showReticle,enabled:Xt,onClick:()=>v({showReticle:!d.showReticle})},{label:"XY indicator",active:!!d.showXYIndicator,onClick:()=>v({showXYIndicator:!d.showXYIndicator})},...zn({functionalActive:re(),hasLimits:at()&&!re(),hasRecorded:Ut()&&!re(),display:Ge(d)},e=>v({passFailDisplay:e}))],()=>!!(d.showRingBoundaries||d.showQuadrantBoundaries||d.showDieLabels||d.showReticle||d.showXYIndicator||Ge(d)!=="off")),{btn:Qt,sync:ot}=Fn(J,()=>({plotMode:d.plotMode,legendPosition:ne}),e=>{ne=e;for(const t of w)t&&t.setOptions({legendPosition:ne})});ot();const Zt=[{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"}],lt=Ze("aggr","Aggregation method",()=>Zt,()=>d.aggregationMethod??"mean",e=>v({aggregationMethod:e}));function st(){const e=d.plotMode==="stackedValues";lt.style.display=e?"":"none"}st();const{btn:Jt,sync:rt}=An(J,()=>({...d,functionalActive:re()}),e=>v(e));rt();const we=V("specRange","Colorbar range: spec limits",()=>{const e=d.colorbarRangeMode==="data"?"spec":"data";v({colorbarRangeMode:e})});function dt(){const e=(d.plotMode??"hardBin")==="value"&&at()&&Ge(d)==="off"&&!re();we.style.display=e?"":"none";const t=(d.colorbarRangeMode??"spec")==="spec";oe(we,t),we.ariaLabel=t?"Colorbar range: spec limits (click for data range)":"Colorbar range: data range (click for spec limits)"}dt();const en=$n(J,()=>d,e=>v(e)),tn=V("downloadAll","Download gallery PNG",hn),nn=Ze("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"}],()=>L!=null?String(L):"auto",e=>xt(e==="auto"?void 0:Number(e))),C=document.createElement("div");Object.assign(C.style,{display:"inline-flex",alignItems:"center",gap:"0"}),E.appendChild(C),Pt&&C.appendChild($),C.appendChild(qt),C.appendChild(lt),C.appendChild(Jt),C.appendChild(we),C.appendChild(P()),C.appendChild(Yt),C.appendChild(P()),C.appendChild(Qt),C.appendChild(P()),C.appendChild(en),C.appendChild(P()),C.appendChild(nn),C.appendChild(P()),C.appendChild(tn),(D||ye())&&(T=V("findings","Summary panel",()=>{if(!p)return;const e=p.style.display!=="none";e||I(),p.style.display=e?"none":"flex",oe(T,!e),X()}),E.appendChild(P()),E.appendChild(T));let j=null,ve=null,_=[],ct=!1;function ft(){const e=Yn({lotStatsSummary:D,result:{warnings:y.flatMap(n=>n?.warnings??[])}}),t=e.length!==_.length||e.some((n,i)=>n.code!==_[i]?.code||n.message!==_[i]?.message);_=e,(t||!ct)&&(ct=!0,u.warnings?.onWarning?.(e)),an()}function an(){if(!j||!ve)return;const e=_.length,t=e>0;if(j.style.display=t?"flex":"none",ve.style.display=t?"":"none",!t)return;const n=Zn(_[0]),i=`${e} data ${e===1?"warning":"warnings"}`;j.style.color=n==="error"?m.errText:m.warnText,j.ariaLabel=n==="error"?`${i} \u2014 wafers in this lot may be positionally wrong`:i}(u.warnings?.display??!0)&&(ve=P(),j=V("warning","Data warnings",()=>{const e=le();if(xe(new MouseEvent("click")),e)return;const t=Qn(j.getBoundingClientRect(),_);Et(S).appendChild(t),se(t),Ot(t,j,()=>xe(new MouseEvent("click")))}),me(j,!1),E.appendChild(ve),E.appendChild(j)),ft();let U=null;Xe&&(U=V("analysis","Insights",()=>{const e=ee?.style.display!=="none";ut(!e)}),U.dataset.wmapInsightsBtn="1",E.appendChild(P()),E.appendChild(U));function pt(){import("./userGuideHtml.js").then(e=>Rn({buildWaferMap:Xn,renderWaferMap:Ke,renderWaferGallery,analyzeWaferMap:Ee},e.USER_GUIDE_HTML,Ft,S))}At&&(E.appendChild(P()),E.appendChild(V("help","User guide",()=>pt())));const G=document.createElement("div");Object.assign(G.style,{display:"flex",flexDirection:"column",gap:"6px",background:m.menuBg,border:`1px solid ${m.menuBorder}`,borderRadius:"6px",padding:"6px 10px",marginBottom:"10px",boxShadow:"0 1px 4px rgba(0,0,0,0.10)",fontSize:"12px",lineHeight:"1",boxSizing:"border-box",width:"100%",minWidth:"0"});const A=document.createElement("div");Object.assign(A.style,{display:"flex",flexDirection:"row",gap:"12px",alignItems:"flex-start"});let de=null,ee=null;Xe&&(de=li({getItems:()=>y,getLotStats:()=>D,getColorSchemeName:()=>d.colorScheme??"default",passBins:Ue,getRingCount:()=>d.ringCount??4,defaultView:u.insights?.defaultView,backTab:{label:"Gallery",onBack:()=>ut(!1)},showMetadataStrip:!1,onSaveImage:u.onSaveImage,onSaveText:u.onSaveText,openWafer:(e,t,n)=>{const i=y[e];if(!i)return;let s=null;const c=Wn({title:t,onClose:()=>s?.destroy(),anchor:S});vt(c,t,i.wafer.metadata??void 0),s=ze(c.contentWrap,i,n)}}),ee=de.el);let Ae=!1;function ut(e){!de||!ee||(Ae=e,ee.style.display=e?"flex":"none",A.style.display=e?"none":"flex",C.style.display=e?"none":"inline-flex",U&&(oe(U,e),U.innerHTML=e?pe.wafer:pe.analysis,U.ariaLabel=e?"Back to gallery view":"Insights"),X(),Me(),e&&de.render())}const on=4,Fe=240,mt=480,ln=720,sn=1.25,gt=u.maxSize;let z=gt??mt;function ht(e){const t=Y*2+110+2;let n=null;for(const i of e){if(i==null||!i.dies?.length)continue;const s=i.dies[0].width;if(!(s>0))continue;const c=i.wafer.radius*2*(on/s),a=Math.ceil(c+t);(n==null||a>n)&&(n=a)}return n}function rn(e){const t=ht(e);return t==null?Fe:Math.min(z,Math.max(Fe,t))}function dn(e){if(gt!=null)return!1;const t=ht(e);if(t==null)return!1;const n=Math.min(ln,Math.max(mt,t));return n<=z?!1:(z=n,!0)}function cn(){for(const e of Array.from(O.children))e.classList.contains("wmap-gallery-card")&&(e.style.maxWidth=`${z}px`,e.style.maxHeight=`${z}px`)}let Ce=Fe,bt=0;function yt(e){const t=dn(e);if(t&&cn(),L!=null){t&&Se();return}const n=rn(e);n>Ce&&(Ce=n),Se()}function xt(e){L=e,Se()}function He(e){return`repeat(${e}, minmax(0, ${z}px))`}function Se(){if(L!=null){O.style.gridTemplateColumns=He(L);return}const e=Math.max(1,bt),t=12,n=O.clientWidth||0,i=c=>(n-t*(c-1))/c;let s=Math.max(1,Math.ceil(Math.sqrt(e)));if(n>0){for(;s>1&&i(s)<Ce;)s--;const c=Math.min(z,Ce*sn);for(;s<e&&i(s+1)>=c;)s++}O.style.gridTemplateColumns=He(s)}const O=document.createElement("div");if(Object.assign(O.style,{flex:"1 1 0",minWidth:"0",display:"grid",gridTemplateColumns:He(1),gap:"12px",justifyContent:"start",alignContent:"start"}),ae=D?"lot":"wafers",Q?.placement){const e=Q.placement;p=qe(e),p.style.maxHeight="calc(100vh - 80px)",p.style.position="sticky",p.style.top="8px",p.style.display="flex",p.style.flexDirection="column"}else if(D||ye()){const e=!!Q?.defaultOpen;p=qe("right"),p.style.maxHeight="calc(100vh - 80px)",p.style.position="sticky",p.style.top="8px",p.style.flexDirection="column",p.style.display=e?"flex":"none",I()}X(),p?.style.display!=="none"&&T&&oe(T,!0),(Q?.placement??"right")==="left"?(p&&A.appendChild(p),A.appendChild(O)):(A.appendChild(O),p&&A.appendChild(p)),S.appendChild(E),S.appendChild(G),S.appendChild(A),ee&&S.appendChild(ee);function Me(){G.innerHTML="";const e=d.plotMode??"hardBin",t=!Ae&&Mn.has(e),n=M.filter(b=>b!=null),i=n.find(b=>b.isLotStack),s=ei(n.map(b=>({metadata:b.wafer.metadata??void 0})),i?{lotSize:i.lotSize??n.length,aggrMethod:i.aggrMethod}:void 0),c=e==="metadata",a=d.activeMetadataKey,r=new Set;if(t&&!c)for(const b of n)for(const W of b.dies){if(W.partial)continue;const x=e==="softBin"?W.sbin:W.hbin;x!=null&&r.add(x)}const o=t&&!c?[...r].sort((b,W)=>b-W):[],l=new Set;if(t&&c&&a)for(const b of n)for(const W of b.dies){if(W.partial)continue;const x=W.metadata?.[a];x!=null&&(typeof x=="string"||typeof x=="number"||typeof x=="boolean")&&l.add(String(x))}const f=t&&c?[...l].sort(Jn):[];if(!s&&!o.length&&!f.length){G.style.display="none";return}if(G.style.display="flex",s&&G.appendChild(s),!o.length&&!f.length)return;const h=document.createElement("div");Object.assign(h.style,{display:"flex",flexWrap:"wrap",alignItems:"center",gap:"6px 14px"}),s&&Object.assign(h.style,{borderTop:`1px solid ${m.separator}`,paddingTop:"6px"}),G.appendChild(h);const g=vn(d.colorScheme),k=d.highlightBin;if(c){const b=d.highlightMetadataValue,W=M.flatMap(x=>x?.metadataFields??[]).find(x=>x.key===a);f.forEach((x,Ne)=>{const Ve=b===x,St=W?.values?.find(_e=>_e.value===x),bn=St?.color??Cn(Ne);Rt(h,{color:bn,isActive:Ve,label:St?.label??x,onClick:()=>{const _e=d.highlightMetadataValue===x?void 0:x;v({highlightMetadataValue:_e})}})});return}const F=e==="softBin"?M.flatMap(b=>b?.sbinDefs??[]):M.flatMap(b=>b?.hbinDefs??[]),H=new Set,K=[];for(const b of F)H.has(b.bin)||(H.add(b.bin),K.push(b));const fe=K.length>0?new Map(K.map(b=>[b.bin,b])):null;for(const b of o){const W=k===b,x=fe?.get(b),Ne=(d.colorScheme==="custom"?x?.color:void 0)??g.forBin(b);Rt(h,{color:Ne,isActive:W,label:x?.name?`${b} \xB7 ${x.name}`:`Bin ${b}`,onClick:()=>{const Ve=d.highlightBin===b?void 0:b;v({highlightBin:Ve})}})}}function ce(e){const t=y.filter(r=>r!==null);if(!t.length)return[];const n=t.map(r=>r.dies.filter(ri)),i=t[0].wafer,s=t.length,c=i.metadata?{...i,metadata:(({wafer:r,waferId:o,...l})=>l)(i.metadata)}:i;function a(r,o){return{...r,stats:{...r.stats,isLotStack:!0,aggregationMethod:o,lotSize:s}}}if(e==="stackedValues"){const r=t.find(f=>f.testDefs?.length)?.testDefs;let o=r?.filter(Wt);if(r?.length||(o=yn(t.flatMap(h=>h.dies)).map(h=>({testNumber:h,name:`Test ${h}`}))),!o?.length)return[];const l=d.aggregationMethod??"mean";return o.map(f=>{const h=Kn(n,l,f.testNumber),g={testNumber:0,name:f.name,unit:f.unit};return{wafer:c,dies:h,testDefs:[g],label:`${f.name} \xB7 ${l}`,isLotStack:!0,aggrMethod:l,lotSize:s,statsSummary:a(Ee({wafer:c,dies:h,testDefs:[g]},{testNumbers:[0]}),l)}})}if(e==="stackedBins"){let r=Oe(t.flatMap(o=>o.hbinDefs??[]));return(!r||r.length===0)&&(r=[...new Set(t.flatMap(l=>l.dies.map(f=>f.hbin).filter(f=>f!=null)))].sort((l,f)=>l-f).map(l=>({bin:l,name:`Bin ${l}`}))),r.map(o=>{const l=Tt(n,o.bin,"hard"),f=[{bin:o.bin,name:o.name}];return{wafer:c,dies:l,hbinDefs:f,label:`${o.bin} \xB7 ${o.name}`,isLotStack:!0,aggrMethod:"countBin",lotSize:s,statsSummary:a(Ee({wafer:c,dies:l,hbinDefs:f}),"countBin")}})}if(e==="stackedSoftBins"){let r=Oe(t.flatMap(o=>o.sbinDefs??[]));return(!r||r.length===0)&&(r=[...new Set(t.flatMap(l=>l.dies.map(f=>f.sbin).filter(f=>f!=null)))].sort((l,f)=>l-f).map(l=>({bin:l,name:`Bin ${l}`}))),r.map(o=>{const l=Tt(n,o.bin,"soft"),f=[{bin:o.bin,name:o.name}];return{wafer:c,dies:l,sbinDefs:f,label:`${o.bin} \xB7 ${o.name}`,isLotStack:!0,aggrMethod:"countBin",lotSize:s,statsSummary:a(Ee({wafer:c,dies:l,sbinDefs:f}),"countBin")}})}return t}function $e(e){const t=y.length;return e==="stackedBins"||e==="stackedSoftBins"?{valueRange:[0,t],lotSize:t}:e==="stackedValues"?{aggregationMethod:d.aggregationMethod??"mean",valueRange:void 0,lotSize:void 0}:{}}const fn=new Set(["default","accessible","custom"]);function v(e,{fireCallback:t=!0}={}){const n=d.plotMode;d={...d,...e};const i=d.plotMode,s=ue.has(i),c=n!==void 0&&ue.has(n),a=be>0;if(e.plotMode!==void 0&&e.plotMode!==n&&(i==="hardBin"||i==="softBin")&&!fn.has(d.colorScheme??"")&&(d={...d,colorScheme:"default"}),e.plotMode!==void 0)if(s){const o=$e(i);if(d={...d,...o},a)for(const l of w)l&&l.setOptions(e);else N(ce(i))}else if(c){const{valueRange:o,lotSize:l,aggregationMethod:f,...h}=d;if(d=h,a)for(const g of w)g&&g.setOptions(e);else N(y.filter(g=>g!==null))}else for(const o of w)o&&o.setOptions(e);else if(e.aggregationMethod!==void 0&&i==="stackedValues")if(a)for(const o of w)o&&o.setOptions(e);else N(ce("stackedValues"));else for(const o of w)o&&o.setOptions(e);Me(),st(),ot(),it();const r=e.plotMode!==void 0&&e.plotMode!==n;if((e.colorScheme!==void 0||r)&&I(),rt(),dt(),t){const o=Object.keys(e);u.onViewOptionsChange?.(d,o,qn(o))}}function wt(e,t,n){const i=e.createElement("div");Object.assign(i.style,{display:"flex",alignItems:"center",gap:"4px",flex:"1",minWidth:"0"});const s=e.createElement("span");s.textContent=t,Object.assign(s.style,{fontWeight:"700",fontSize:"13px",overflow:"hidden",textOverflow:"ellipsis",whiteSpace:"nowrap"}),i.appendChild(s);const c=It(n??{});let a=null;if(c.length>0){const r=e.createElement("span");Object.assign(r.style,{fontSize:"12px",lineHeight:"1",color:m.label,flexShrink:"0"}),r.textContent="\u25BE",i.appendChild(r),Object.assign(i.style,{cursor:"pointer"}),a=e.createElement("div"),a.dataset.wmapCardMetaPanel="1",Object.assign(a.style,{position:"absolute",top:"0",left:"0",right:"0",zIndex:kt,background:m.menuBg,borderBottom:`1px solid ${m.menuBorder}`,boxShadow:"0 2px 6px rgba(0,0,0,0.15)",padding:"8px 10px",fontSize:"11px",display:"none"});const o=Lt(n??{});o&&a.appendChild(o),i.setAttribute("aria-expanded","false"),i.setAttribute("aria-label",`Wafer info for ${t||"this card"}. Click to expand.`),Dt(i,l=>{r.textContent=l?"\u25B4":"\u25BE",i.setAttribute("aria-expanded",String(l)),a&&(a.style.display=l?"block":"none")})}return{wrap:i,metaPanel:a}}function vt(e,t,n){const i=e.box.querySelector("[data-wmap-window-title]"),s=It(n??{});if(!i||s.length===0)return;const c=i.parentElement,a=document.createElement("div");Object.assign(a.style,{display:"flex",alignItems:"center",gap:"4px",flex:"1",minWidth:"0",cursor:"pointer"}),c?.insertBefore(a,i),a.appendChild(i);const r=document.createElement("span");Object.assign(r.style,{fontSize:"12px",lineHeight:"1",color:m.label,flexShrink:"0"}),r.textContent="\u25BE",a.appendChild(r);const o=document.createElement("div");o.dataset.wmapCardMetaPanel="1",Object.assign(o.style,{position:"absolute",top:"0",left:"0",right:"0",zIndex:kt,background:m.menuBg,borderBottom:`1px solid ${m.menuBorder}`,boxShadow:"0 2px 6px rgba(0,0,0,0.15)",padding:"8px 10px",fontSize:"11px",display:"none"});const l=Lt(n??{});l&&o.appendChild(l),e.contentWrap.style.position="relative",e.contentWrap.appendChild(o),a.setAttribute("aria-expanded","false"),a.setAttribute("aria-label",`Wafer info for ${t}. Click to expand.`),Dt(a,f=>{r.textContent=f?"\u25B4":"\u25BE",a.setAttribute("aria-expanded",String(f)),o.style.display=f?"block":"none"})}function je(e,t,n){const i=document.createElement("div");i.className="wmap-gallery-card",Object.assign(i.style,{background:m.menuBg,border:`1px solid ${m.menuBorder}`,borderRadius:"10px",overflow:"hidden",display:"flex",flexDirection:"column",position:"relative",aspectRatio:"1",maxWidth:`${z}px`,maxHeight:`${z}px`});const s=document.createElement("div");Object.assign(s.style,{display:"flex",alignItems:"center",padding:"8px 10px 6px",borderBottom:`1px solid ${m.menuBorder}`,flexShrink:"0",gap:"6px"});const{wrap:c,metaPanel:a}=wt(document,e.label??"",e.wafer.metadata??void 0);s.appendChild(c);const r=document.createElement("button");r.dataset.wmapExpandBtn="1",r.title="Open full view",r.innerHTML=pe.expand,Object.assign(r.style,{display:"flex",alignItems:"center",justifyContent:"center",border:`1px solid ${m.menuBorder}`,borderRadius:"4px",background:m.panelBg,color:m.label,padding:"2px",cursor:"pointer",flexShrink:"0",width:"22px",height:"22px"}),s.appendChild(r),i.appendChild(s);const o=document.createElement("div");Object.assign(o.style,{position:"relative",flex:"1",minHeight:"0",overflow:"hidden",display:"flex",flexDirection:"column"}),i.appendChild(o),a&&o.appendChild(a),O.appendChild(i);const l=Ke(o,e,{viewOptions:e.viewOptions?{...d,...e.viewOptions}:d,toolbarControls:"full",showTooltip:!0,padding:Y,legendPosition:ne,fallbackFormat:he,statsSummary:e.statsSummary,onSaveImage:u.onSaveImage,onSaveText:u.onSaveText,onClick:e.onClick,onSelect:e.onSelect,onExpand:()=>ke(t,e),showMetadataBadge:!1});return l.setViewControlsVisible(!1),l.setSummaryVisible(!1),r.onclick=()=>ke(t,e),{card:i,ctrl:l,canvasWrapper:o,expandBtn:r}}function pn(e){const t=Z[e];if(!t)return;const n=[...R.values()].find(i=>i.cardIndex===e);n?(t.innerHTML=pe.minimize,t.title="Reattach to gallery",t.setAttribute("aria-label","Reattach to gallery"),t.onclick=()=>mn(n.id)):(t.innerHTML=pe.expand,t.title="Open full view",t.setAttribute("aria-label","Open full view"),t.onclick=()=>ke(e,M[e]))}function N(e){gn(),le()?.remove(),se(null),Re(),M=[];for(const a of w)a&&a.destroy();w=[],q=[],Z=[],be=0,O.innerHTML="",bt=e.length,yt(e.map(a=>typeof a=="function"?null:a));const t=[];for(let a=0;a<e.length;a++){const r=e[a];if(typeof r=="function"){const o=document.createElement("div");o.className="wmap-gallery-card",Object.assign(o.style,{background:m.menuBg,border:`1px solid ${m.menuBorder}`,borderRadius:"10px",aspectRatio:"1",display:"flex",alignItems:"center",justifyContent:"center"});const l=document.createElement("span");l.textContent="\u2026",Object.assign(l.style,{color:m.label,fontSize:"18px"}),o.appendChild(l),O.appendChild(o),M.push(null),w.push(null),q.push(null),Z.push(null),t.push({index:a,factory:r,placeholder:o}),be++}else{const{ctrl:o,canvasWrapper:l,expandBtn:f}=je(r,a,e.length);M.push(r),w.push(o),q.push(l),Z.push(f)}}M.flatMap(a=>a?[...a.hbinDefs??[],...a.sbinDefs??[]]:[]).some(a=>a.color)&&!u.viewOptions?.colorScheme&&(d={...d,colorScheme:"custom"}),Me();const i=++De;let s=0;function c(){if(i!==De)return;if(s>=t.length){if(ue.has(d.plotMode)){const F=d.plotMode;N(ce(F))}return}const{index:a,factory:r,placeholder:o}=t[s++],l=r();M[a]=l,y[a]=l,yt([l]);const{card:f,ctrl:h,canvasWrapper:g,expandBtn:k}=je(l,a,e.length);w[a]=h,q[a]=g,Z[a]=k,be--,o.replaceWith(f),Me(),!p&&!Q?.placement&&l.statsSummary?.findings.length?(D||(ae="wafers"),p=qe("right"),p.style.maxHeight="calc(100vh - 80px)",p.style.position="sticky",p.style.top="8px",p.style.flexDirection="column",p.style.display="none",(Q?.placement??"right")==="left"?A.insertBefore(p,O):A.appendChild(p),I(),T||(T=V("findings","Summary panel",()=>{if(!p)return;const H=p.style.display!=="none";H||I(),p.style.display=H?"none":"flex",oe(T,!H),X()}),E.appendChild(P()),E.appendChild(T)),X()):p&&p.style.display!=="none"&&I(),X(),setTimeout(c,0)}t.length>0&&setTimeout(c,0)}if(ue.has(d.plotMode)&&y.length>0){const e=$e(d.plotMode);d={...d,...e},N(ce(d.plotMode))}else N(B);const Ct=new ResizeObserver(()=>{L==null&&Se()});Ct.observe(O),p&&I();function un(e){const t=q[e];if(!t)return;t.innerHTML="",Object.assign(t.style,{alignItems:"center",justifyContent:"center"});const n=document.createElement("span");n.textContent="Detached \u2014 open in its own window",Object.assign(n.style,{color:m.label,fontSize:"12px",textAlign:"center",padding:"0 12px"}),t.appendChild(n)}function ke(e,t){for(const a of R.values())if(a.cardIndex===e)return;const n=t.label??"Wafer map",i=$t++,s=w[e]?.getOptions(),c=Dn(n);if(c){const a=c.document;a.title=n,Object.assign(a.documentElement.style,{height:"100%"}),Object.assign(a.body.style,{margin:"0",height:"100vh",overflow:"hidden",display:"flex",flexDirection:"column",fontFamily:'system-ui, -apple-system, "Segoe UI", sans-serif'}),Ln(S,a.documentElement);const r=In(S,a.documentElement,()=>F.setOptions({})),o=a.createElement("div");o.dataset.wmapWindowBanner="1",o.textContent=n,Object.assign(o.style,{display:"none",padding:"6px 12px",fontSize:"12px",fontWeight:"700",background:m.warnBg,color:m.warnText,borderBottom:`1px solid ${m.warnBorder}`,flexShrink:"0"}),a.body.appendChild(o);const l=a.createElement("div");Object.assign(l.style,{flex:"1",minHeight:"0",display:"flex",flexDirection:"column"}),a.body.appendChild(l);const{wrap:f,metaPanel:h}=wt(a,n,t.wafer.metadata??void 0),g=a.createElement("div");Object.assign(g.style,{display:"flex",alignItems:"center",padding:"8px 10px 6px",borderBottom:`1px solid ${m.menuBorder}`,flexShrink:"0",gap:"6px"}),g.appendChild(f),l.appendChild(g);const k=a.createElement("div");Object.assign(k.style,{position:"relative",flex:"1",minHeight:"0",overflow:"hidden",display:"flex",flexDirection:"column"}),h&&k.appendChild(h),l.appendChild(k);const F=ze(k,t,void 0,s),H=setInterval(()=>{c.closed&&Be(i)},400);c.addEventListener("pagehide",()=>Be(i)),R.set(i,{id:i,ctrl:F,cardIndex:e,label:n,closePollId:H,stopThemeSync:r,close:()=>{c.closed||c.close()},setTitle:K=>{a.title=K,o.textContent=K,o.style.display="block"}})}else{const a=Tn({title:n,onClose:()=>Be(i),anchor:S});a.contentWrap.style.flexDirection="column",vt(a,n,t.wafer.metadata??void 0);const r=ze(a.contentWrap,t,void 0,s);R.set(i,{id:i,ctrl:r,cardIndex:e,label:n,closePollId:null,close:()=>a.close(),setTitle:o=>{const l=a.box.querySelector("[data-wmap-window-title]");l&&(l.textContent=o,l.title=o)}})}w[e]?.destroy(),w[e]=null,un(e),pn(e)}function ze(e,t,n,i){const s=t.viewOptions?{...d,...t.viewOptions}:d,c=i?{...s,...i}:s,a=n!==void 0?{...c,plotMode:"value",activeTest:n}:c,r=Ke(e,t,{viewOptions:a,toolbarControls:"full",showTooltip:!0,padding:Y,legendPosition:ne,fallbackFormat:he,statsSummary:t.statsSummary,onSaveImage:u.onSaveImage,onSaveText:u.onSaveText,onClick:t.onClick,onSelect:t.onSelect,showExpandButton:!1,showMetadataBadge:!1});return r.setViewControlsVisible(!0),r.setSummaryVisible(!0),r.setExpandVisible(!1),r}function Be(e){const t=R.get(e);if(!t)return;R.delete(e),t.closePollId!=null&&clearInterval(t.closePollId),t.stopThemeSync?.();const n=t.ctrl.getOptions();if(t.ctrl.destroy(),t.cardIndex===null)return;const i=t.cardIndex,s=M[i];if(!s)return;const c={...s,viewOptions:n};M[i]=c;const{card:a,ctrl:r,canvasWrapper:o,expandBtn:l}=je(c,i,M.length);q[i]?.parentElement?.replaceWith(a),w[i]=r,q[i]=o,Z[i]=l}function mn(e){const t=R.get(e);t&&(t.close(),Be(e))}function gn(){for(const e of R.values())e.cardIndex!==null&&(e.cardIndex=null,e.setTitle(`${e.label} \u2014 unlinked from gallery`))}function hn(){const e=[...O.querySelectorAll("canvas")];if(!e.length)return;const t=e.length,n=Math.ceil(Math.sqrt(t)),i=Math.ceil(t/n),s=e[0].width,c=e[0].height,a=8,r=window.devicePixelRatio||1,o=Math.round(26*r),l=Math.round(12*r),f=document.createElement("canvas");f.width=n*s+(n-1)*a,f.height=i*(c+o)+(i-1)*a;const h=f.getContext("2d"),g=Sn(O);h.fillStyle=g.background,h.fillRect(0,0,f.width,f.height),e.forEach((k,F)=>{const H=F%n,K=Math.floor(F/n),fe=H*(s+a),b=K*(c+o+a),W=k.closest(".wmap-gallery-card")?.querySelector("span")?.textContent??"";h.fillStyle=g.surface,h.fillRect(fe,b,s,o),h.fillStyle=g.text,h.font=`700 ${l}px system-ui, sans-serif`,h.textAlign="left",h.textBaseline="middle",h.fillText(W,fe+Math.round(10*r),b+o/2,s-Math.round(20*r)),h.drawImage(k,fe,b+o)}),f.toBlob(k=>{k&&jn(k,te,u.onSaveImage)})}return{setItems(e){y=e.map(n=>typeof n=="function"?null:n);const t=d.plotMode;if(ue.has(t))if(e.some(i=>typeof i=="function"))N(e);else{const i=$e(t);d={...d,...i},N(ce(t))}else N(e)},setOptions(e){v(e,{fireCallback:!1})},getOptions(){return{...d}},setFallbackFormat(e){he=e;for(const t of w)t&&t.setFallbackFormat(e)},setLotStatsSummary(e){D=e,ft(),p&&I(),X()},setColumns(e){xt(e)},openUserGuide:pt,destroy(){De++;for(const e of[...R.values()])e.closePollId!=null&&clearInterval(e.closePollId),e.close(),e.ctrl.destroy();R.clear();for(const e of w)e&&e.destroy();w=[],le()?.remove(),Ct.disconnect(),S.ownerDocument.removeEventListener("click",xe,!0),window.removeEventListener("blur",Je),Ht(),Bt(),E.remove(),G.remove(),A.remove(),p?.remove(),de?.destroy()}}}
20
+ </html>`;ni(a)}),u.appendChild(n)}if(e.length===0){const n=document.createElement("div");Object.assign(n.style,{color:m.icon,fontSize:"11px",padding:"4px 0"}),n.textContent="No findings on any wafer.",u.appendChild(n)}else for(const{index:n,item:i,unusualCount:s,notableCount:c,totalCount:a}of e){const r=s?"unusual":c?"notable":"info",o=s+c||a,l=document.createElement("button");l.type="button",Object.assign(l.style,{display:"flex",alignItems:"center",justifyContent:"space-between",width:"100%",padding:"5px 8px",marginBottom:"4px",border:"none",borderLeft:`3px solid ${Mt(r)}`,borderRadius:"3px",background:m.bgHover,cursor:"pointer",fontSize:"11px",textAlign:"left",boxSizing:"border-box"}),l.addEventListener("mouseover",()=>{l.style.background=m.bgActive}),l.addEventListener("mouseout",()=>{l.style.background=m.bgHover});const f=document.createElement("span");f.textContent=i.label??`W${n+1}`,Object.assign(f.style,{color:m.iconHover,overflow:"hidden",textOverflow:"ellipsis",whiteSpace:"nowrap"});const h=document.createElement("span");h.textContent=String(o),Object.assign(h.style,{marginLeft:"6px",flexShrink:"0",background:Mt(r),color:"#fff",borderRadius:"8px",padding:"1px 5px",fontSize:"10px",fontWeight:"600"}),l.appendChild(f),l.appendChild(h),l.addEventListener("click",()=>ke(n,i)),u.appendChild(l)}const t=document.createElement("div");t.style.height="12px",u.appendChild(t)}function I(){if(u)if(ae==="lot"&&D){const e=Oe(y.flatMap(n=>n?.hbinDefs??[])),t=Oe(y.flatMap(n=>n?.sbinDefs??[]));ti(u,{lotSummary:D,items:y,hbinDefs:e.length?e:void 0,sbinDefs:t.length?t:void 0,testDefs:y.find(n=>n?.testDefs?.length)?.testDefs,passBins:Ue,ringCount:d.ringCount,colorScheme:d.colorScheme,fallbackFormat:he,activeFindingId:ie,warnings:p.warnings?.display??!0?_:[],findingsFilter:jt,onFindingsFilterChange:I,onSaveText:p.onSaveText,onFindingClick:(n,i)=>{ie===n.id?Re():Gt(n,i),I()},onWaferClick:n=>{Te([n])},dieListOptions:p.dieList}),ye()&&u.insertBefore(et(),u.firstChild)}else _t()}function tt(e){return[e.variable.kind,e.variable.index??"",e.variable.bin??"",e.comparison.family,e.comparison.left,e.effect.direction].join("|")}function Re(){ie=null,We(),Le(),v({highlightBin:void 0},{fireCallback:!1})}function Gt(e,t){if(ie===e.id){Re();return}ie=e.id,t.style.background=m.bgActive,t.style.fontWeight="600";const{kind:n,index:i}=e.variable;n==="test"?v({plotMode:"value",activeTest:i??0,highlightBin:void 0},{fireCallback:!1}):n==="softBin"?v({plotMode:"softBin",highlightBin:void 0},{fireCallback:!1}):v({plotMode:"hardBin",highlightBin:void 0},{fireCallback:!1}),We(),Le();const s=e.highlight;if(s.kind==="wafer"){Te(s.waferIndices);const c=tt(e);for(const a of s.waferIndices){const l=M[a]?.statsSummary?.findings.find(f=>tt(f)===c)?.highlight?.dieKeys;l?.length&&Ie(l,[a])}}else s.kind==="bin"?s.dieKeys?.length&&Ie(s.dieKeys):(s.kind==="region"||s.kind==="dies")&&s.dieKeys?.length&&Ie(s.dieKeys)}function X(){if(!T)return;const e=!!u;T.style.display=(D||ye())&&e&&!Ae?"flex":"none";const t=u?u.style.display!=="none":!1;(D?.hasNotableFindings||y.some(i=>i?.statsSummary?.hasNotableFindings))&&!t?T.style.color=m.findingIndicator:T.dataset.active||(T.style.color=m.icon)}const E=document.createElement("div");E.dataset.wmapToolbar="gallery",Object.assign(E.style,{display:"inline-flex",flexDirection:"row",alignItems:"center",gap:"0",background:m.menuBg,border:`1px solid ${m.menuBorder}`,borderRadius:"6px",padding:"3px 4px",marginBottom:"10px",boxShadow:"0 1px 4px rgba(0,0,0,0.10)",flexWrap:"wrap",minWidth:"0",overflowX:"auto"});const nt=()=>{const e=le();e&&(e.remove(),se(null)),me($,!1)},$=V("mode","Plot mode",()=>{if(le()){nt();return}const e=y.flatMap(g=>g?.dies??[]),t=y.find(g=>g?.testDefs?.length)?.testDefs,{testEntries:n,binEntries:i,stackedEntries:s}=Vn(e,t,{includeStacked:!0}),c=d.plotMode??"hardBin",a=d.activeTest??0;function r(g){return g.plotMode!==c?!1:g.plotMode==="value"?a===(g.activeTest??0):g.plotMode==="metadata"?d.activeMetadataKey===g.activeMetadataKey:!0}function o(g,k){g.activeTest!==void 0?v({plotMode:"value",activeTest:g.activeTest,activeMetadataKey:void 0,logScale:g.logScale}):g.activeMetadataKey!==void 0?v({plotMode:g.plotMode,activeTest:void 0,activeMetadataKey:g.activeMetadataKey,passFailDisplay:"off"}):v({plotMode:g.plotMode,activeTest:void 0,activeMetadataKey:void 0,passFailDisplay:"off"}),k.remove(),se(null),me($,!1)}const l=new Set,f=y.flatMap(g=>g?.metadataFields??[]).filter(g=>!l.has(g.key)&&l.add(g.key)).filter(g=>_n(e,g.key)).map(Gn),h=On($.getBoundingClientRect(),n,i,s,r,o,{makeMenuRow:Nt,makeMenuSection:Vt},c,$.ownerDocument.defaultView??window,f);Et($).appendChild(h),se(h),me($,!0),Ot(h,$,nt)});me($,!1);const Kt=()=>M.flatMap(e=>e?[...e.hbinDefs??[],...e.sbinDefs??[]]:[]).some(e=>e.color),{btn:qt,sync:it}=Pn(J,()=>d.plotMode??"hardBin",()=>d.colorScheme??"default",Kt,e=>v({colorScheme:e}));it();const Xt=B.some(e=>typeof e!="function"&&(e.reticles?.length??0)>0);function Pe(){const e=y.find(n=>n?.testDefs?.length)?.testDefs,{testNumber:t}=xn(d.activeTest??0,e);return{testNumber:t,td:wn(e,t)}}function at(){if((d.plotMode??"hardBin")!=="value")return!1;const{td:e}=Pe();return e!==void 0&&(e.limitLow!==void 0||e.limitHigh!==void 0)}function re(){if((d.plotMode??"hardBin")!=="value")return!1;const{td:e}=Pe();return e!==void 0&&!Wt(e)}function Ut(){if((d.plotMode??"hardBin")!=="value")return!1;const{testNumber:e,td:t}=Pe();return y.some(n=>n?.dies?.some(i=>Un(i,e,t)!==void 0))}const Yt=Hn(J,()=>[{label:"Ring boundaries",active:!!d.showRingBoundaries,onClick:()=>v({showRingBoundaries:!d.showRingBoundaries})},{label:"Quadrant lines",active:!!d.showQuadrantBoundaries,onClick:()=>v({showQuadrantBoundaries:!d.showQuadrantBoundaries})},{label:"Die labels",active:!!d.showDieLabels,onClick:()=>v({showDieLabels:!d.showDieLabels})},{label:"Reticle grid",active:!!d.showReticle,enabled:Xt,onClick:()=>v({showReticle:!d.showReticle})},{label:"XY indicator",active:!!d.showXYIndicator,onClick:()=>v({showXYIndicator:!d.showXYIndicator})},...zn({functionalActive:re(),hasLimits:at()&&!re(),hasRecorded:Ut()&&!re(),display:Ge(d)},e=>v({passFailDisplay:e}))],()=>!!(d.showRingBoundaries||d.showQuadrantBoundaries||d.showDieLabels||d.showReticle||d.showXYIndicator||Ge(d)!=="off")),{btn:Qt,sync:ot}=Fn(J,()=>({plotMode:d.plotMode,legendPosition:ne}),e=>{ne=e;for(const t of w)t&&t.setOptions({legendPosition:ne})});ot();const Zt=[{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"}],lt=Ze("aggr","Aggregation method",()=>Zt,()=>d.aggregationMethod??"mean",e=>v({aggregationMethod:e}));function st(){const e=d.plotMode==="stackedValues";lt.style.display=e?"":"none"}st();const{btn:Jt,sync:rt}=An(J,()=>({...d,functionalActive:re()}),e=>v(e));rt();const we=V("specRange","Colorbar range: spec limits",()=>{const e=d.colorbarRangeMode==="data"?"spec":"data";v({colorbarRangeMode:e})});function dt(){const e=(d.plotMode??"hardBin")==="value"&&at()&&Ge(d)==="off"&&!re();we.style.display=e?"":"none";const t=(d.colorbarRangeMode??"spec")==="spec";oe(we,t),we.ariaLabel=t?"Colorbar range: spec limits (click for data range)":"Colorbar range: data range (click for spec limits)"}dt();const en=$n(J,()=>d,e=>v(e)),tn=V("downloadAll","Download gallery PNG",hn),nn=Ze("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"}],()=>L!=null?String(L):"auto",e=>xt(e==="auto"?void 0:Number(e))),C=document.createElement("div");Object.assign(C.style,{display:"inline-flex",alignItems:"center",gap:"0"}),E.appendChild(C),Pt&&C.appendChild($),C.appendChild(qt),C.appendChild(lt),C.appendChild(Jt),C.appendChild(we),C.appendChild(P()),C.appendChild(Yt),C.appendChild(P()),C.appendChild(Qt),C.appendChild(P()),C.appendChild(en),C.appendChild(P()),C.appendChild(nn),C.appendChild(P()),C.appendChild(tn),(D||ye())&&(T=V("findings","Summary panel",()=>{if(!u)return;const e=u.style.display!=="none";e||I(),u.style.display=e?"none":"flex",oe(T,!e),X()}),E.appendChild(P()),E.appendChild(T));let j=null,ve=null,_=[],ct=!1;function ft(){const e=Yn({lotStatsSummary:D,result:{warnings:y.flatMap(n=>n?.warnings??[])}}),t=e.length!==_.length||e.some((n,i)=>n.code!==_[i]?.code||n.message!==_[i]?.message);_=e,(t||!ct)&&(ct=!0,p.warnings?.onWarning?.(e)),an()}function an(){if(!j||!ve)return;const e=_.length,t=e>0;if(j.style.display=t?"flex":"none",ve.style.display=t?"":"none",!t)return;const n=Zn(_[0]),i=`${e} data ${e===1?"warning":"warnings"}`;j.style.color=n==="error"?m.errText:m.warnText,j.ariaLabel=n==="error"?`${i} \u2014 wafers in this lot may be positionally wrong`:i}(p.warnings?.display??!0)&&(ve=P(),j=V("warning","Data warnings",()=>{const e=le();if(xe(new MouseEvent("click")),e)return;const t=Qn(j.getBoundingClientRect(),_);Et(S).appendChild(t),se(t),Ot(t,j,()=>xe(new MouseEvent("click")))}),me(j,!1),E.appendChild(ve),E.appendChild(j)),ft();let U=null;Xe&&(U=V("analysis","Insights",()=>{const e=ee?.style.display!=="none";ut(!e)}),U.dataset.wmapInsightsBtn="1",E.appendChild(P()),E.appendChild(U));function pt(){import("./userGuideHtml.js").then(e=>Rn({buildWaferMap:Xn,renderWaferMap:Ke,renderWaferGallery,analyzeWaferMap:Ee},e.USER_GUIDE_HTML,Ft,S))}At&&(E.appendChild(P()),E.appendChild(V("help","User guide",()=>pt())));const G=document.createElement("div");Object.assign(G.style,{display:"flex",flexDirection:"column",gap:"6px",background:m.menuBg,border:`1px solid ${m.menuBorder}`,borderRadius:"6px",padding:"6px 10px",marginBottom:"10px",boxShadow:"0 1px 4px rgba(0,0,0,0.10)",fontSize:"12px",lineHeight:"1",boxSizing:"border-box",width:"100%",minWidth:"0"});const A=document.createElement("div");Object.assign(A.style,{display:"flex",flexDirection:"row",gap:"12px",alignItems:"flex-start"});let de=null,ee=null;Xe&&(de=li({getItems:()=>y,getLotStats:()=>D,getColorSchemeName:()=>d.colorScheme??"default",passBins:Ue,getRingCount:()=>d.ringCount??4,defaultView:p.insights?.defaultView,backTab:{label:"Gallery",onBack:()=>ut(!1)},showMetadataStrip:!1,onSaveImage:p.onSaveImage,onSaveText:p.onSaveText,openWafer:(e,t,n)=>{const i=y[e];if(!i)return;let s=null;const c=Wn({title:t,onClose:()=>s?.destroy(),anchor:S});vt(c,t,i.wafer.metadata??void 0),s=ze(c.contentWrap,i,n)}}),ee=de.el);let Ae=!1;function ut(e){!de||!ee||(Ae=e,ee.style.display=e?"flex":"none",A.style.display=e?"none":"flex",C.style.display=e?"none":"inline-flex",U&&(oe(U,e),U.innerHTML=e?pe.wafer:pe.analysis,U.ariaLabel=e?"Back to gallery view":"Insights"),X(),Me(),e&&de.render())}const on=4,Fe=240,mt=480,ln=720,sn=1.25,gt=p.maxSize;let z=gt??mt;function ht(e){const t=Y*2+110+2;let n=null;for(const i of e){if(i==null||!i.dies?.length)continue;const s=i.dies[0].width;if(!(s>0))continue;const c=i.wafer.radius*2*(on/s),a=Math.ceil(c+t);(n==null||a>n)&&(n=a)}return n}function rn(e){const t=ht(e);return t==null?Fe:Math.min(z,Math.max(Fe,t))}function dn(e){if(gt!=null)return!1;const t=ht(e);if(t==null)return!1;const n=Math.min(ln,Math.max(mt,t));return n<=z?!1:(z=n,!0)}function cn(){for(const e of Array.from(O.children))e.classList.contains("wmap-gallery-card")&&(e.style.maxWidth=`${z}px`,e.style.maxHeight=`${z}px`)}let Ce=Fe,bt=0;function yt(e){const t=dn(e);if(t&&cn(),L!=null){t&&Se();return}const n=rn(e);n>Ce&&(Ce=n),Se()}function xt(e){L=e,Se()}function He(e){return`repeat(${e}, minmax(0, ${z}px))`}function Se(){if(L!=null){O.style.gridTemplateColumns=He(L);return}const e=Math.max(1,bt),t=12,n=O.clientWidth||0,i=c=>(n-t*(c-1))/c;let s=Math.max(1,Math.ceil(Math.sqrt(e)));if(n>0){for(;s>1&&i(s)<Ce;)s--;const c=Math.min(z,Ce*sn);for(;s<e&&i(s+1)>=c;)s++}O.style.gridTemplateColumns=He(s)}const O=document.createElement("div");if(Object.assign(O.style,{flex:"1 1 0",minWidth:"0",display:"grid",gridTemplateColumns:He(1),gap:"12px",justifyContent:"start",alignContent:"start"}),ae=D?"lot":"wafers",Q?.placement){const e=Q.placement;u=qe(e),u.style.maxHeight="calc(100vh - 80px)",u.style.position="sticky",u.style.top="8px",u.style.display="flex",u.style.flexDirection="column"}else if(D||ye()){const e=!!Q?.defaultOpen;u=qe("right"),u.style.maxHeight="calc(100vh - 80px)",u.style.position="sticky",u.style.top="8px",u.style.flexDirection="column",u.style.display=e?"flex":"none",I()}X(),u?.style.display!=="none"&&T&&oe(T,!0),(Q?.placement??"right")==="left"?(u&&A.appendChild(u),A.appendChild(O)):(A.appendChild(O),u&&A.appendChild(u)),S.appendChild(E),S.appendChild(G),S.appendChild(A),ee&&S.appendChild(ee);function Me(){G.innerHTML="";const e=d.plotMode??"hardBin",t=!Ae&&Mn.has(e),n=M.filter(b=>b!=null),i=n.find(b=>b.isLotStack),s=ei(n.map(b=>({metadata:b.wafer.metadata??void 0})),i?{lotSize:i.lotSize??n.length,aggrMethod:i.aggrMethod}:void 0),c=e==="metadata",a=d.activeMetadataKey,r=new Set;if(t&&!c)for(const b of n)for(const W of b.dies){if(W.partial)continue;const x=e==="softBin"?W.sbin:W.hbin;x!=null&&r.add(x)}const o=t&&!c?[...r].sort((b,W)=>b-W):[],l=new Set;if(t&&c&&a)for(const b of n)for(const W of b.dies){if(W.partial)continue;const x=W.metadata?.[a];x!=null&&(typeof x=="string"||typeof x=="number"||typeof x=="boolean")&&l.add(String(x))}const f=t&&c?[...l].sort(Jn):[];if(!s&&!o.length&&!f.length){G.style.display="none";return}if(G.style.display="flex",s&&G.appendChild(s),!o.length&&!f.length)return;const h=document.createElement("div");Object.assign(h.style,{display:"flex",flexWrap:"wrap",alignItems:"center",gap:"6px 14px"}),s&&Object.assign(h.style,{borderTop:`1px solid ${m.separator}`,paddingTop:"6px"}),G.appendChild(h);const g=vn(d.colorScheme),k=d.highlightBin;if(c){const b=d.highlightMetadataValue,W=M.flatMap(x=>x?.metadataFields??[]).find(x=>x.key===a);f.forEach((x,Ne)=>{const Ve=b===x,St=W?.values?.find(_e=>_e.value===x),bn=St?.color??Cn(Ne);Rt(h,{color:bn,isActive:Ve,label:St?.label??x,onClick:()=>{const _e=d.highlightMetadataValue===x?void 0:x;v({highlightMetadataValue:_e})}})});return}const F=e==="softBin"?M.flatMap(b=>b?.sbinDefs??[]):M.flatMap(b=>b?.hbinDefs??[]),H=new Set,K=[];for(const b of F)H.has(b.bin)||(H.add(b.bin),K.push(b));const fe=K.length>0?new Map(K.map(b=>[b.bin,b])):null;for(const b of o){const W=k===b,x=fe?.get(b),Ne=(d.colorScheme==="custom"?x?.color:void 0)??g.forBin(b);Rt(h,{color:Ne,isActive:W,label:x?.name?`${b} \xB7 ${x.name}`:`Bin ${b}`,onClick:()=>{const Ve=d.highlightBin===b?void 0:b;v({highlightBin:Ve})}})}}function ce(e){const t=y.filter(r=>r!==null);if(!t.length)return[];const n=t.map(r=>r.dies.filter(ri)),i=t[0].wafer,s=t.length,c=i.metadata?{...i,metadata:(({wafer:r,waferId:o,...l})=>l)(i.metadata)}:i;function a(r,o){return{...r,stats:{...r.stats,isLotStack:!0,aggregationMethod:o,lotSize:s}}}if(e==="stackedValues"){const r=t.find(f=>f.testDefs?.length)?.testDefs;let o=r?.filter(Wt);if(r?.length||(o=yn(t.flatMap(h=>h.dies)).map(h=>({testNumber:h,name:`Test ${h}`}))),!o?.length)return[];const l=d.aggregationMethod??"mean";return o.map(f=>{const h=Kn(n,l,f.testNumber),g={testNumber:0,name:f.name,unit:f.unit};return{wafer:c,dies:h,testDefs:[g],label:`${f.name} \xB7 ${l}`,isLotStack:!0,aggrMethod:l,lotSize:s,statsSummary:a(Ee({wafer:c,dies:h,testDefs:[g]},{testNumbers:[0]}),l)}})}if(e==="stackedBins"){let r=Oe(t.flatMap(o=>o.hbinDefs??[]));return(!r||r.length===0)&&(r=[...new Set(t.flatMap(l=>l.dies.map(f=>f.hbin).filter(f=>f!=null)))].sort((l,f)=>l-f).map(l=>({bin:l,name:`Bin ${l}`}))),r.map(o=>{const l=Tt(n,o.bin,"hard"),f=[{bin:o.bin,name:o.name}];return{wafer:c,dies:l,hbinDefs:f,label:`${o.bin} \xB7 ${o.name}`,isLotStack:!0,aggrMethod:"countBin",lotSize:s,statsSummary:a(Ee({wafer:c,dies:l,hbinDefs:f}),"countBin")}})}if(e==="stackedSoftBins"){let r=Oe(t.flatMap(o=>o.sbinDefs??[]));return(!r||r.length===0)&&(r=[...new Set(t.flatMap(l=>l.dies.map(f=>f.sbin).filter(f=>f!=null)))].sort((l,f)=>l-f).map(l=>({bin:l,name:`Bin ${l}`}))),r.map(o=>{const l=Tt(n,o.bin,"soft"),f=[{bin:o.bin,name:o.name}];return{wafer:c,dies:l,sbinDefs:f,label:`${o.bin} \xB7 ${o.name}`,isLotStack:!0,aggrMethod:"countBin",lotSize:s,statsSummary:a(Ee({wafer:c,dies:l,sbinDefs:f}),"countBin")}})}return t}function $e(e){const t=y.length;return e==="stackedBins"||e==="stackedSoftBins"?{valueRange:[0,t],lotSize:t}:e==="stackedValues"?{aggregationMethod:d.aggregationMethod??"mean",valueRange:void 0,lotSize:void 0}:{}}const fn=new Set(["default","accessible","custom"]);function v(e,{fireCallback:t=!0}={}){const n=d.plotMode;d={...d,...e};const i=d.plotMode,s=ue.has(i),c=n!==void 0&&ue.has(n),a=be>0;if(e.plotMode!==void 0&&e.plotMode!==n&&(i==="hardBin"||i==="softBin")&&!fn.has(d.colorScheme??"")&&(d={...d,colorScheme:"default"}),e.plotMode!==void 0)if(s){const o=$e(i);if(d={...d,...o},a)for(const l of w)l&&l.setOptions(e);else N(ce(i))}else if(c){const{valueRange:o,lotSize:l,aggregationMethod:f,...h}=d;if(d=h,a)for(const g of w)g&&g.setOptions(e);else N(y.filter(g=>g!==null))}else for(const o of w)o&&o.setOptions(e);else if(e.aggregationMethod!==void 0&&i==="stackedValues")if(a)for(const o of w)o&&o.setOptions(e);else N(ce("stackedValues"));else for(const o of w)o&&o.setOptions(e);Me(),st(),ot(),it();const r=e.plotMode!==void 0&&e.plotMode!==n;if((e.colorScheme!==void 0||r)&&I(),rt(),dt(),t){const o=Object.keys(e);p.onViewOptionsChange?.(d,o,qn(o))}}function wt(e,t,n){const i=e.createElement("div");Object.assign(i.style,{display:"flex",alignItems:"center",gap:"4px",flex:"1",minWidth:"0"});const s=e.createElement("span");s.textContent=t,Object.assign(s.style,{fontWeight:"700",fontSize:"13px",overflow:"hidden",textOverflow:"ellipsis",whiteSpace:"nowrap"}),i.appendChild(s);const c=It(n??{});let a=null;if(c.length>0){const r=e.createElement("span");Object.assign(r.style,{fontSize:"12px",lineHeight:"1",color:m.label,flexShrink:"0"}),r.textContent="\u25BE",i.appendChild(r),Object.assign(i.style,{cursor:"pointer"}),a=e.createElement("div"),a.dataset.wmapCardMetaPanel="1",Object.assign(a.style,{position:"absolute",top:"0",left:"0",right:"0",zIndex:kt,background:m.menuBg,borderBottom:`1px solid ${m.menuBorder}`,boxShadow:"0 2px 6px rgba(0,0,0,0.15)",padding:"8px 10px",fontSize:"11px",display:"none"});const o=Lt(n??{});o&&a.appendChild(o),i.setAttribute("aria-expanded","false"),i.setAttribute("aria-label",`Wafer info for ${t||"this card"}. Click to expand.`),Dt(i,l=>{r.textContent=l?"\u25B4":"\u25BE",i.setAttribute("aria-expanded",String(l)),a&&(a.style.display=l?"block":"none")})}return{wrap:i,metaPanel:a}}function vt(e,t,n){const i=e.box.querySelector("[data-wmap-window-title]"),s=It(n??{});if(!i||s.length===0)return;const c=i.parentElement,a=document.createElement("div");Object.assign(a.style,{display:"flex",alignItems:"center",gap:"4px",flex:"1",minWidth:"0",cursor:"pointer"}),c?.insertBefore(a,i),a.appendChild(i);const r=document.createElement("span");Object.assign(r.style,{fontSize:"12px",lineHeight:"1",color:m.label,flexShrink:"0"}),r.textContent="\u25BE",a.appendChild(r);const o=document.createElement("div");o.dataset.wmapCardMetaPanel="1",Object.assign(o.style,{position:"absolute",top:"0",left:"0",right:"0",zIndex:kt,background:m.menuBg,borderBottom:`1px solid ${m.menuBorder}`,boxShadow:"0 2px 6px rgba(0,0,0,0.15)",padding:"8px 10px",fontSize:"11px",display:"none"});const l=Lt(n??{});l&&o.appendChild(l),e.contentWrap.style.position="relative",e.contentWrap.appendChild(o),a.setAttribute("aria-expanded","false"),a.setAttribute("aria-label",`Wafer info for ${t}. Click to expand.`),Dt(a,f=>{r.textContent=f?"\u25B4":"\u25BE",a.setAttribute("aria-expanded",String(f)),o.style.display=f?"block":"none"})}function je(e,t,n){const i=document.createElement("div");i.className="wmap-gallery-card",Object.assign(i.style,{background:m.menuBg,border:`1px solid ${m.menuBorder}`,borderRadius:"10px",overflow:"hidden",display:"flex",flexDirection:"column",position:"relative",aspectRatio:"1",maxWidth:`${z}px`,maxHeight:`${z}px`});const s=document.createElement("div");Object.assign(s.style,{display:"flex",alignItems:"center",padding:"8px 10px 6px",borderBottom:`1px solid ${m.menuBorder}`,flexShrink:"0",gap:"6px"});const{wrap:c,metaPanel:a}=wt(document,e.label??"",e.wafer.metadata??void 0);s.appendChild(c);const r=document.createElement("button");r.dataset.wmapExpandBtn="1",r.title="Open full view",r.innerHTML=pe.expand,Object.assign(r.style,{display:"flex",alignItems:"center",justifyContent:"center",border:`1px solid ${m.menuBorder}`,borderRadius:"4px",background:m.panelBg,color:m.label,padding:"2px",cursor:"pointer",flexShrink:"0",width:"22px",height:"22px"}),s.appendChild(r),i.appendChild(s);const o=document.createElement("div");Object.assign(o.style,{position:"relative",flex:"1",minHeight:"0",overflow:"hidden",display:"flex",flexDirection:"column"}),i.appendChild(o),a&&o.appendChild(a),O.appendChild(i);const l=Ke(o,e,{viewOptions:e.viewOptions?{...d,...e.viewOptions}:d,toolbarControls:"full",showTooltip:!0,padding:Y,legendPosition:ne,fallbackFormat:he,statsSummary:e.statsSummary,onSaveImage:p.onSaveImage,onSaveText:p.onSaveText,onClick:e.onClick,onSelect:e.onSelect,onExpand:()=>ke(t,e),showMetadataBadge:!1});return l.setViewControlsVisible(!1),l.setSummaryVisible(!1),r.onclick=()=>ke(t,e),{card:i,ctrl:l,canvasWrapper:o,expandBtn:r}}function pn(e){const t=Z[e];if(!t)return;const n=[...R.values()].find(i=>i.cardIndex===e);n?(t.innerHTML=pe.minimize,t.title="Reattach to gallery",t.setAttribute("aria-label","Reattach to gallery"),t.onclick=()=>mn(n.id)):(t.innerHTML=pe.expand,t.title="Open full view",t.setAttribute("aria-label","Open full view"),t.onclick=()=>ke(e,M[e]))}function N(e){gn(),le()?.remove(),se(null),Re(),M=[];for(const a of w)a&&a.destroy();w=[],q=[],Z=[],be=0,O.innerHTML="",bt=e.length,yt(e.map(a=>typeof a=="function"?null:a));const t=[];for(let a=0;a<e.length;a++){const r=e[a];if(typeof r=="function"){const o=document.createElement("div");o.className="wmap-gallery-card",Object.assign(o.style,{background:m.menuBg,border:`1px solid ${m.menuBorder}`,borderRadius:"10px",aspectRatio:"1",display:"flex",alignItems:"center",justifyContent:"center"});const l=document.createElement("span");l.textContent="\u2026",Object.assign(l.style,{color:m.label,fontSize:"18px"}),o.appendChild(l),O.appendChild(o),M.push(null),w.push(null),q.push(null),Z.push(null),t.push({index:a,factory:r,placeholder:o}),be++}else{const{ctrl:o,canvasWrapper:l,expandBtn:f}=je(r,a,e.length);M.push(r),w.push(o),q.push(l),Z.push(f)}}M.flatMap(a=>a?[...a.hbinDefs??[],...a.sbinDefs??[]]:[]).some(a=>a.color)&&!p.viewOptions?.colorScheme&&(d={...d,colorScheme:"custom"}),Me();const i=++De;let s=0;function c(){if(i!==De)return;if(s>=t.length){if(ue.has(d.plotMode)){const F=d.plotMode;N(ce(F))}return}const{index:a,factory:r,placeholder:o}=t[s++],l=r();M[a]=l,y[a]=l,yt([l]);const{card:f,ctrl:h,canvasWrapper:g,expandBtn:k}=je(l,a,e.length);w[a]=h,q[a]=g,Z[a]=k,be--,o.replaceWith(f),Me(),!u&&!Q?.placement&&l.statsSummary?.findings.length?(D||(ae="wafers"),u=qe("right"),u.style.maxHeight="calc(100vh - 80px)",u.style.position="sticky",u.style.top="8px",u.style.flexDirection="column",u.style.display="none",(Q?.placement??"right")==="left"?A.insertBefore(u,O):A.appendChild(u),I(),T||(T=V("findings","Summary panel",()=>{if(!u)return;const H=u.style.display!=="none";H||I(),u.style.display=H?"none":"flex",oe(T,!H),X()}),E.appendChild(P()),E.appendChild(T)),X()):u&&u.style.display!=="none"&&I(),X(),setTimeout(c,0)}t.length>0&&setTimeout(c,0)}if(ue.has(d.plotMode)&&y.length>0){const e=$e(d.plotMode);d={...d,...e},N(ce(d.plotMode))}else N(B);const Ct=new ResizeObserver(()=>{L==null&&Se()});Ct.observe(O),u&&I();function un(e){const t=q[e];if(!t)return;t.innerHTML="",Object.assign(t.style,{alignItems:"center",justifyContent:"center"});const n=document.createElement("span");n.textContent="Detached \u2014 open in its own window",Object.assign(n.style,{color:m.label,fontSize:"12px",textAlign:"center",padding:"0 12px"}),t.appendChild(n)}function ke(e,t){for(const a of R.values())if(a.cardIndex===e)return;const n=t.label??"Wafer map",i=$t++,s=w[e]?.getOptions(),c=Dn(n);if(c){const a=c.document;a.title=n,Object.assign(a.documentElement.style,{height:"100%"}),Object.assign(a.body.style,{margin:"0",height:"100vh",overflow:"hidden",display:"flex",flexDirection:"column",fontFamily:'system-ui, -apple-system, "Segoe UI", sans-serif'}),Ln(S,a.documentElement);const r=In(S,a.documentElement,()=>F.setOptions({})),o=a.createElement("div");o.dataset.wmapWindowBanner="1",o.textContent=n,Object.assign(o.style,{display:"none",padding:"6px 12px",fontSize:"12px",fontWeight:"700",background:m.warnBg,color:m.warnText,borderBottom:`1px solid ${m.warnBorder}`,flexShrink:"0"}),a.body.appendChild(o);const l=a.createElement("div");Object.assign(l.style,{flex:"1",minHeight:"0",display:"flex",flexDirection:"column"}),a.body.appendChild(l);const{wrap:f,metaPanel:h}=wt(a,n,t.wafer.metadata??void 0),g=a.createElement("div");Object.assign(g.style,{display:"flex",alignItems:"center",padding:"8px 10px 6px",borderBottom:`1px solid ${m.menuBorder}`,flexShrink:"0",gap:"6px"}),g.appendChild(f),l.appendChild(g);const k=a.createElement("div");Object.assign(k.style,{position:"relative",flex:"1",minHeight:"0",overflow:"hidden",display:"flex",flexDirection:"column"}),h&&k.appendChild(h),l.appendChild(k);const F=ze(k,t,void 0,s),H=setInterval(()=>{c.closed&&Be(i)},400);c.addEventListener("pagehide",()=>Be(i)),R.set(i,{id:i,ctrl:F,cardIndex:e,label:n,closePollId:H,stopThemeSync:r,close:()=>{c.closed||c.close()},setTitle:K=>{a.title=K,o.textContent=K,o.style.display="block"}})}else{const a=Tn({title:n,onClose:()=>Be(i),anchor:S});a.contentWrap.style.flexDirection="column",vt(a,n,t.wafer.metadata??void 0);const r=ze(a.contentWrap,t,void 0,s);R.set(i,{id:i,ctrl:r,cardIndex:e,label:n,closePollId:null,close:()=>a.close(),setTitle:o=>{const l=a.box.querySelector("[data-wmap-window-title]");l&&(l.textContent=o,l.title=o)}})}w[e]?.destroy(),w[e]=null,un(e),pn(e)}function ze(e,t,n,i){const s=t.viewOptions?{...d,...t.viewOptions}:d,c=i?{...s,...i}:s,a=n!==void 0?{...c,plotMode:"value",activeTest:n}:c,r=Ke(e,t,{viewOptions:a,toolbarControls:"full",showTooltip:!0,padding:Y,legendPosition:ne,fallbackFormat:he,statsSummary:t.statsSummary,onSaveImage:p.onSaveImage,onSaveText:p.onSaveText,onClick:t.onClick,onSelect:t.onSelect,showExpandButton:!1,showMetadataBadge:!1});return r.setViewControlsVisible(!0),r.setSummaryVisible(!0),r.setExpandVisible(!1),r}function Be(e){const t=R.get(e);if(!t)return;R.delete(e),t.closePollId!=null&&clearInterval(t.closePollId),t.stopThemeSync?.();const n=t.ctrl.getOptions();if(t.ctrl.destroy(),t.cardIndex===null)return;const i=t.cardIndex,s=M[i];if(!s)return;const c={...s,viewOptions:n};M[i]=c;const{card:a,ctrl:r,canvasWrapper:o,expandBtn:l}=je(c,i,M.length);q[i]?.parentElement?.replaceWith(a),w[i]=r,q[i]=o,Z[i]=l}function mn(e){const t=R.get(e);t&&(t.close(),Be(e))}function gn(){for(const e of R.values())e.cardIndex!==null&&(e.cardIndex=null,e.setTitle(`${e.label} \u2014 unlinked from gallery`))}function hn(){const e=[...O.querySelectorAll("canvas")];if(!e.length)return;const t=e.length,n=Math.ceil(Math.sqrt(t)),i=Math.ceil(t/n),s=e[0].width,c=e[0].height,a=8,r=window.devicePixelRatio||1,o=Math.round(26*r),l=Math.round(12*r),f=document.createElement("canvas");f.width=n*s+(n-1)*a,f.height=i*(c+o)+(i-1)*a;const h=f.getContext("2d"),g=Sn(O);h.fillStyle=g.background,h.fillRect(0,0,f.width,f.height),e.forEach((k,F)=>{const H=F%n,K=Math.floor(F/n),fe=H*(s+a),b=K*(c+o+a),W=k.closest(".wmap-gallery-card")?.querySelector("span")?.textContent??"";h.fillStyle=g.surface,h.fillRect(fe,b,s,o),h.fillStyle=g.text,h.font=`700 ${l}px system-ui, sans-serif`,h.textAlign="left",h.textBaseline="middle",h.fillText(W,fe+Math.round(10*r),b+o/2,s-Math.round(20*r)),h.drawImage(k,fe,b+o)}),f.toBlob(k=>{k&&jn(k,te,p.onSaveImage)})}return{setItems(e){y=e.map(n=>typeof n=="function"?null:n);const t=d.plotMode;if(ue.has(t))if(e.some(i=>typeof i=="function"))N(e);else{const i=$e(t);d={...d,...i},N(ce(t))}else N(e)},setOptions(e){v(e,{fireCallback:!1})},getOptions(){return{...d}},setFallbackFormat(e){he=e;for(const t of w)t&&t.setFallbackFormat(e)},setLotStatsSummary(e){D=e,ft(),u&&I(),X()},setColumns(e){xt(e)},openUserGuide:pt,destroy(){De++;for(const e of[...R.values()])e.closePollId!=null&&clearInterval(e.closePollId),e.close(),e.ctrl.destroy();R.clear();for(const e of w)e&&e.destroy();w=[],le()?.remove(),Ct.disconnect(),S.ownerDocument.removeEventListener("click",xe,!0),window.removeEventListener("blur",Je),Ht(),Bt(),E.remove(),G.remove(),A.remove(),u?.remove(),de?.destroy()}}}
@@ -7,6 +7,7 @@ import { type SaveImageHandler, type SaveTextHandler, type UserGuideExtension }
7
7
  import type { SummaryPanelOptions } from './summaryPanel.js';
8
8
  import { type WarningsOptions } from './warnings.js';
9
9
  import { type InsightsOptions } from './insightsTab.js';
10
+ import { type DieListDisplayOptions } from './dieList.js';
10
11
  /**
11
12
  * Stable display preferences — worth persisting to localStorage or a user profile.
12
13
  * These describe how the user wants to view wafer maps in general.
@@ -191,6 +192,18 @@ export interface RenderOptions extends Omit<ToCanvasOptions, 'viewport' | 'hbinD
191
192
  * Insights has no map for a finding to highlight against.
192
193
  */
193
194
  summaryPanel?: SummaryPanelOptions;
195
+ /**
196
+ * Display preferences for every built-in die-list surface: the
197
+ * coordinate-less map replacement, the "+N dies without position data"
198
+ * footer, and the "View die list" link in the Summary panel (§5.4.4) — on
199
+ * by default whenever `summaryPanel` is set; pass `{ enabled: false }` to
200
+ * hide just that link, leaving the other two surfaces unaffected. This
201
+ * option carries preferences only — the wafer metadata and metadata field
202
+ * definitions the table needs are always taken from the current build
203
+ * result, never from this option, so a host cannot substitute the wrong
204
+ * identity data into an export.
205
+ */
206
+ dieList?: DieListDisplayOptions;
194
207
  /**
195
208
  * Custom tooltip renderer. When provided, replaces the built-in tooltip content.
196
209
  * Return a string (set as innerHTML), an HTMLElement (appended directly), or null to suppress the tooltip.