@wafertools/wafermap 0.32.0 → 0.34.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (167) hide show
  1. package/AGENTS.md +7 -7
  2. package/CHANGELOG.md +374 -1
  3. package/README.md +5 -5
  4. package/dist/packages/canvas-adapter/chartPopulation.d.ts +15 -10
  5. package/dist/packages/canvas-adapter/chartPopulation.js +1 -1
  6. package/dist/packages/canvas-adapter/charts/barPanel.d.ts +7 -0
  7. package/dist/packages/canvas-adapter/charts/barPanel.js +1 -1
  8. package/dist/packages/canvas-adapter/charts/binCluster.d.ts +7 -1
  9. package/dist/packages/canvas-adapter/charts/binCluster.js +1 -1
  10. package/dist/packages/canvas-adapter/charts/boxplot.js +1 -1
  11. package/dist/packages/canvas-adapter/charts/capability.js +1 -1
  12. package/dist/packages/canvas-adapter/charts/chartShell.d.ts +57 -15
  13. package/dist/packages/canvas-adapter/charts/chartShell.js +1 -1
  14. package/dist/packages/canvas-adapter/charts/correlation.js +1 -2
  15. package/dist/packages/canvas-adapter/charts/groupedBarPlot.d.ts +2 -0
  16. package/dist/packages/canvas-adapter/charts/groupedBarPlot.js +1 -1
  17. package/dist/packages/canvas-adapter/charts/histogram.d.ts +16 -0
  18. package/dist/packages/canvas-adapter/charts/histogram.js +1 -1
  19. package/dist/packages/canvas-adapter/charts/palette.d.ts +2 -0
  20. package/dist/packages/canvas-adapter/charts/palette.js +1 -1
  21. package/dist/packages/canvas-adapter/charts/plotChart.d.ts +27 -0
  22. package/dist/packages/canvas-adapter/charts/plotChart.js +1 -0
  23. package/dist/packages/canvas-adapter/charts/pointInteractions.d.ts +45 -0
  24. package/dist/packages/canvas-adapter/charts/pointInteractions.js +1 -0
  25. package/dist/packages/canvas-adapter/charts/regionYieldDiagram.d.ts +2 -0
  26. package/dist/packages/canvas-adapter/charts/regionYieldDiagram.js +1 -1
  27. package/dist/packages/canvas-adapter/charts/scatter.d.ts +19 -0
  28. package/dist/packages/canvas-adapter/charts/scatter.js +1 -1
  29. package/dist/packages/canvas-adapter/charts/sweep.d.ts +12 -0
  30. package/dist/packages/canvas-adapter/charts/sweep.js +1 -1
  31. package/dist/packages/canvas-adapter/charts/testPassRate.d.ts +7 -0
  32. package/dist/packages/canvas-adapter/charts/testPassRate.js +1 -1
  33. package/dist/packages/canvas-adapter/charts/trend.d.ts +11 -0
  34. package/dist/packages/canvas-adapter/charts/trend.js +1 -1
  35. package/dist/packages/canvas-adapter/confirmDialog.d.ts +13 -0
  36. package/dist/packages/canvas-adapter/confirmDialog.js +1 -0
  37. package/dist/packages/canvas-adapter/dataTab.d.ts +131 -0
  38. package/dist/packages/canvas-adapter/dataTab.js +1 -0
  39. package/dist/packages/canvas-adapter/dieList.d.ts +39 -4
  40. package/dist/packages/canvas-adapter/dieList.js +6 -7
  41. package/dist/packages/canvas-adapter/drilldown.d.ts +20 -3
  42. package/dist/packages/canvas-adapter/drilldown.js +1 -1
  43. package/dist/packages/canvas-adapter/guideWindow.d.ts +96 -0
  44. package/dist/packages/canvas-adapter/guideWindow.js +1 -0
  45. package/dist/packages/canvas-adapter/identityHeader.d.ts +16 -0
  46. package/dist/packages/canvas-adapter/identityHeader.js +1 -1
  47. package/dist/packages/canvas-adapter/index.d.ts +3 -3
  48. package/dist/packages/canvas-adapter/index.js +1 -1
  49. package/dist/packages/canvas-adapter/insightsTab.d.ts +73 -54
  50. package/dist/packages/canvas-adapter/insightsTab.js +1 -1
  51. package/dist/packages/canvas-adapter/maplessSummary.d.ts +2 -2
  52. package/dist/packages/canvas-adapter/plotEditor.d.ts +29 -0
  53. package/dist/packages/canvas-adapter/plotEditor.js +1 -0
  54. package/dist/packages/canvas-adapter/plotItems.d.ts +32 -0
  55. package/dist/packages/canvas-adapter/plotItems.js +1 -0
  56. package/dist/packages/canvas-adapter/plotModal.d.ts +30 -0
  57. package/dist/packages/canvas-adapter/plotModal.js +1 -0
  58. package/dist/packages/canvas-adapter/plotStore.d.ts +52 -0
  59. package/dist/packages/canvas-adapter/plotStore.js +1 -0
  60. package/dist/packages/canvas-adapter/plotTab.d.ts +30 -0
  61. package/dist/packages/canvas-adapter/plotTab.js +1 -0
  62. package/dist/packages/canvas-adapter/renderWaferGallery.d.ts +3 -2
  63. package/dist/packages/canvas-adapter/renderWaferGallery.js +1 -1
  64. package/dist/packages/canvas-adapter/renderWaferMap.d.ts +51 -2
  65. package/dist/packages/canvas-adapter/renderWaferMap.js +1 -1
  66. package/dist/packages/canvas-adapter/summaryPanel.d.ts +49 -11
  67. package/dist/packages/canvas-adapter/summaryPanel.js +2 -3
  68. package/dist/packages/canvas-adapter/summaryRail.d.ts +7 -0
  69. package/dist/packages/canvas-adapter/summaryRail.js +1 -0
  70. package/dist/packages/canvas-adapter/sweepEditor.d.ts +17 -0
  71. package/dist/packages/canvas-adapter/sweepEditor.js +1 -0
  72. package/dist/packages/canvas-adapter/tableExport.d.ts +22 -0
  73. package/dist/packages/canvas-adapter/tableExport.js +3 -0
  74. package/dist/packages/canvas-adapter/toCanvas.js +1 -1
  75. package/dist/packages/canvas-adapter/toolbar.d.ts +74 -93
  76. package/dist/packages/canvas-adapter/toolbar.js +2 -2
  77. package/dist/packages/canvas-adapter/userGuideHtml.d.ts +1 -1
  78. package/dist/packages/canvas-adapter/userGuideHtml.js +409 -51
  79. package/dist/packages/canvas-adapter/version.d.ts +2 -2
  80. package/dist/packages/canvas-adapter/version.js +1 -1
  81. package/dist/packages/canvas-adapter/viewSwitch.d.ts +13 -0
  82. package/dist/packages/canvas-adapter/viewSwitch.js +1 -0
  83. package/dist/packages/canvas-adapter/virtualTable.d.ts +44 -0
  84. package/dist/packages/canvas-adapter/virtualTable.js +1 -0
  85. package/dist/packages/core/aggregates.d.ts +6 -0
  86. package/dist/packages/core/aggregates.js +1 -1
  87. package/dist/packages/core/classify.d.ts +3 -2
  88. package/dist/packages/core/classify.js +1 -1
  89. package/dist/packages/core/compact.d.ts +205 -0
  90. package/dist/packages/core/compact.js +3 -0
  91. package/dist/packages/core/passBins.d.ts +23 -4
  92. package/dist/packages/core/passBins.js +1 -1
  93. package/dist/packages/core/ringCount.d.ts +22 -0
  94. package/dist/packages/core/ringCount.js +1 -0
  95. package/dist/packages/core/tableCsv.d.ts +43 -0
  96. package/dist/packages/core/tableCsv.js +3 -0
  97. package/dist/packages/renderer/axisTicks.d.ts +14 -0
  98. package/dist/packages/renderer/axisTicks.js +1 -1
  99. package/dist/packages/renderer/binColors.d.ts +6 -6
  100. package/dist/packages/renderer/binColors.js +1 -1
  101. package/dist/packages/renderer/buildView.d.ts +27 -2
  102. package/dist/packages/renderer/buildView.js +1 -1
  103. package/dist/packages/renderer/buildWaferMap.js +1 -1
  104. package/dist/packages/renderer/compactView.d.ts +22 -0
  105. package/dist/packages/renderer/compactView.js +1 -0
  106. package/dist/packages/renderer/xyIndicator.d.ts +27 -0
  107. package/dist/packages/renderer/xyIndicator.js +1 -0
  108. package/dist/packages/stats/analyzeWaferLot.d.ts +28 -0
  109. package/dist/packages/stats/analyzeWaferLot.js +1 -1
  110. package/dist/packages/stats/analyzeWaferMap.d.ts +36 -1
  111. package/dist/packages/stats/analyzeWaferMap.js +1 -1
  112. package/dist/packages/stats/binPareto.d.ts +1 -1
  113. package/dist/packages/stats/binPareto.js +1 -1
  114. package/dist/packages/stats/binRows.d.ts +38 -0
  115. package/dist/packages/stats/binRows.js +1 -0
  116. package/dist/packages/stats/filterFindings.d.ts +25 -0
  117. package/dist/packages/stats/filterFindings.js +1 -1
  118. package/dist/packages/stats/findingText.d.ts +9 -0
  119. package/dist/packages/stats/findingText.js +2 -0
  120. package/dist/packages/stats/histogram.d.ts +27 -0
  121. package/dist/packages/stats/histogram.js +1 -1
  122. package/dist/packages/stats/index.d.ts +2 -0
  123. package/dist/packages/stats/index.js +1 -1
  124. package/dist/packages/stats/lotDrift.d.ts +16 -0
  125. package/dist/packages/stats/lotDrift.js +1 -0
  126. package/dist/packages/stats/lotPattern.d.ts +33 -0
  127. package/dist/packages/stats/lotPattern.js +1 -0
  128. package/dist/packages/stats/math.d.ts +60 -6
  129. package/dist/packages/stats/math.js +1 -1
  130. package/dist/packages/stats/patternClassification.d.ts +32 -1
  131. package/dist/packages/stats/patternClassification.js +1 -1
  132. package/dist/packages/stats/plotData.d.ts +258 -0
  133. package/dist/packages/stats/plotData.js +1 -0
  134. package/dist/packages/stats/plotId.d.ts +3 -0
  135. package/dist/packages/stats/plotId.js +1 -0
  136. package/dist/packages/stats/plotSpec.d.ts +119 -0
  137. package/dist/packages/stats/plotSpec.js +4 -0
  138. package/dist/packages/stats/presentation.d.ts +44 -0
  139. package/dist/packages/stats/presentation.js +1 -0
  140. package/dist/packages/stats/regions.d.ts +15 -0
  141. package/dist/packages/stats/regions.js +1 -1
  142. package/dist/packages/stats/renderSummaryReport.d.ts +23 -5
  143. package/dist/packages/stats/renderSummaryReport.js +32 -27
  144. package/dist/packages/stats/reportHtml.d.ts +54 -9
  145. package/dist/packages/stats/reportHtml.js +145 -55
  146. package/dist/packages/stats/scatter.d.ts +6 -0
  147. package/dist/packages/stats/scatter.js +1 -1
  148. package/dist/packages/stats/sweep.d.ts +37 -1
  149. package/dist/packages/stats/sweep.js +1 -1
  150. package/dist/packages/stats/sweepText.d.ts +14 -0
  151. package/dist/packages/stats/sweepText.js +1 -0
  152. package/dist/packages/stats/sweepXFromName.d.ts +14 -0
  153. package/dist/packages/stats/sweepXFromName.js +1 -1
  154. package/dist/packages/stats/synthesis.d.ts +76 -0
  155. package/dist/packages/stats/synthesis.js +2 -0
  156. package/dist/packages/stats/testPassRate.d.ts +6 -0
  157. package/dist/packages/stats/testPassRate.js +1 -1
  158. package/dist/packages/stats/trend.js +1 -1
  159. package/dist/packages/stats/types.d.ts +2 -2
  160. package/dist/packages/stats/yield.d.ts +15 -6
  161. package/dist/packages/stats/yield.js +1 -1
  162. package/dist/packages/stats/yieldRows.d.ts +31 -0
  163. package/dist/packages/stats/yieldRows.js +1 -0
  164. package/llms.txt +16 -4
  165. package/package.json +3 -3
  166. package/dist/packages/stats/findingsNarrative.d.ts +0 -3
  167. package/dist/packages/stats/findingsNarrative.js +0 -1
@@ -0,0 +1,131 @@
1
+ import type { Die } from '../core/dies.js';
2
+ import { type MetadataFieldDef, type TestDef } from '../renderer/buildWaferMap.js';
3
+ import { type DieListDisplayOptions } from './dieList.js';
4
+ import { type SaveTextHandler } from './toolbar.js';
5
+ import type { InsightsItem } from './insightsTab.js';
6
+ import type { Wafer } from '../core/wafer.js';
7
+ export type DataView = 'statistics' | 'dies' | 'wafers';
8
+ /** What a table needs of a wafer. An `InsightsItem` fits; so does one wafer's share
9
+ * of a drilldown population, which has no `Wafer` object or stats of its own. */
10
+ export interface DataItem {
11
+ label: string;
12
+ dies: Die[];
13
+ waferIndex: number;
14
+ wafer?: Wafer;
15
+ /** The wafer's own pass bins: the Wafers view's yield judges by them. */
16
+ passBins: readonly number[];
17
+ statsSummary?: InsightsItem['statsSummary'];
18
+ }
19
+ export declare const DATA_VIEWS: ReadonlyArray<{
20
+ key: DataView;
21
+ label: string;
22
+ }>;
23
+ /** Rows Excel can open. A file with more is still valid; the reader is told. */
24
+ export declare const EXCEL_ROW_LIMIT = 1048576;
25
+ /** Cells above which Copy is not offered: a paste that size freezes the target. */
26
+ export declare const COPY_CELL_LIMIT = 200000;
27
+ /** Parametric tests given a mean column in the Wafers view. */
28
+ export declare const WAFER_TEST_COLUMNS = 50;
29
+ export interface DataSectionDeps {
30
+ doc: Document;
31
+ items: DataItem[];
32
+ /** Parametric tests of the scope. */
33
+ testDefs: TestDef[];
34
+ /** Every test of the scope, functional ones included. */
35
+ allTestDefs: TestDef[];
36
+ /** Active grouping, when any: Statistics then shows one block per group. */
37
+ groups?: {
38
+ key: string;
39
+ items: DataItem[];
40
+ }[];
41
+ groupLabelText?: string;
42
+ ringCount: number;
43
+ /** Lot yield per wafer index (the figure every other yield display uses). */
44
+ yieldByWaferIndex: ReadonlyMap<number, number | null>;
45
+ onSaveText?: SaveTextHandler;
46
+ /** The Statistics view's tables, built by the Insights tab (they are the
47
+ * test-values and functional cards that used to sit in Overview). */
48
+ buildStatistics: (items: DataItem[]) => {
49
+ cards: HTMLElement[];
50
+ destroy: () => void;
51
+ };
52
+ /** Which views to offer. Default all three. */
53
+ views?: readonly DataView[];
54
+ /** Host display choices for the Dies table (`RenderOptions.dieList`): which metadata columns to show. */
55
+ dieListOptions?: DieListDisplayOptions;
56
+ /** Label/order hints for die metadata columns, e.g. `WaferMapResult.metadataFields`. */
57
+ metadataFields?: MetadataFieldDef[];
58
+ /** A die row was clicked: show that die on its map. When set, Dies rows are clickable and say so. */
59
+ onLocateDie?: (die: Die, waferIndex: number | undefined) => void;
60
+ /** A wafer row was clicked: open that wafer. When set, Wafers rows are clickable and say so. */
61
+ onOpenWafer?: (waferIndex: number, label: string) => void;
62
+ /** Set when the tables describe part of a population (a drilldown selection): what to say after the
63
+ * counts ("selected on W03"), and a wafer column and file-name tag so a file says so too. */
64
+ population?: {
65
+ phrase: string;
66
+ fileTag: string;
67
+ };
68
+ view: DataView;
69
+ onViewChange: (view: DataView) => void;
70
+ /** The Dies view's Wide | Long choice. */
71
+ diesLayout: 'wide' | 'long';
72
+ onDiesLayoutChange: (layout: 'wide' | 'long') => void;
73
+ }
74
+ export declare function renderDataSection(deps: DataSectionDeps): {
75
+ card: HTMLElement;
76
+ destroy: () => void;
77
+ };
78
+ export interface DataTablesInput {
79
+ doc: Document;
80
+ /** One entry per wafer. `wafer` gives ring/quadrant and the lot/product columns; `passBins` and
81
+ * `statsSummary` give the Wafers view its yield and means. */
82
+ items: ReadonlyArray<{
83
+ label: string;
84
+ dies: Die[];
85
+ waferIndex?: number;
86
+ wafer?: Wafer;
87
+ passBins: readonly number[];
88
+ statsSummary?: DataItem['statsSummary'];
89
+ }>;
90
+ testDefs: TestDef[] | undefined;
91
+ /** Set when the tables describe part of a population — a drilldown selection: who the dies are, in
92
+ * words ("selected on W03"). A saved file then says so in its name and a Wafer column. Omit for a whole wafer or lot. */
93
+ population?: string;
94
+ view: DataView;
95
+ /** Which views to offer. Default Dies and Statistics, plus Wafers when there is more than one wafer. */
96
+ views?: readonly DataView[];
97
+ /** The wafers' ring count (`WaferMapResult.ringCount`): ring and quadrant columns use it. Required, never defaulted. */
98
+ ringCount: number;
99
+ metadataFields?: MetadataFieldDef[];
100
+ /** A die row clicked: show that die on its map. The caller closes the table around it. */
101
+ onLocateDie?: (die: Die, waferIndex: number | undefined) => void;
102
+ dieListOptions?: DieListDisplayOptions;
103
+ onSaveText?: SaveTextHandler;
104
+ }
105
+ /**
106
+ * The Data tab's tables for a population, in the same controls, for a modal —
107
+ * a drilldown selection, or a wafer or lot opened from the Summary panel. It does
108
+ * not need Insights to be enabled: it is built from the dies it is given.
109
+ * Statistics is the Test Values and Functional Tests tables over just those dies,
110
+ * stamped with their identity so a saved file says what it covers.
111
+ */
112
+ export declare function renderDataTables(input: DataTablesInput): {
113
+ el: HTMLElement;
114
+ destroy: () => void;
115
+ };
116
+ /** A drilldown population as tables: Dies and Statistics, named for who the dies are. */
117
+ export declare function renderSelectionTables(input: {
118
+ doc: Document;
119
+ /** The wafers' ring count (from their built results): ring and quadrant columns use it. */
120
+ ringCount: number;
121
+ items: DataTablesInput['items'];
122
+ testDefs: TestDef[] | undefined;
123
+ population: string;
124
+ view: DataView;
125
+ onSaveText?: SaveTextHandler;
126
+ onLocateDie?: DataTablesInput['onLocateDie'];
127
+ }): {
128
+ el: HTMLElement;
129
+ destroy: () => void;
130
+ };
131
+ //# sourceMappingURL=dataTab.d.ts.map
@@ -0,0 +1 @@
1
+ import{isYieldEligibleDie as W}from"../core/dies.js";import{testValue as O}from"../core/dieTable.js";import{metadataDisplayValue as I}from"../core/metadata.js";import{prettyKey as z}from"../core/utils.js";import{tableToTsv as N}from"../core/tableCsv.js";import{commonMetadata as H}from"../stats/facets.js";import{buildYieldData as Y}from"../stats/yield.js";import{isParametricTest as k}from"../renderer/buildWaferMap.js";import{fmt as G}from"../renderer/fmt.js";import{testLabel as R}from"../renderer/testLabel.js";import{resolveDieColumns as U}from"./dieList.js";import{createVirtualTable as V}from"./virtualTable.js";import{exportCsv as E}from"./tableExport.js";import{CLR as y,FONT as L,RADIUS as j,SPACE as C,controlStyle as K,wireControlHover as A,wireTooltip as $}from"./toolbar.js";import{buildTestSection as q,buildFunctionalTestSection as X}from"./summaryPanel.js";export const DATA_VIEWS=[{key:"statistics",label:"Statistics"},{key:"dies",label:"Dies"},{key:"wafers",label:"Wafers"}],EXCEL_ROW_LIMIT=1048576,COPY_CELL_LIMIT=2e5,WAFER_TEST_COLUMNS=50;const D=(e,i,s=i+"s")=>`${e.toLocaleString()} ${e===1?i:s}`;export function renderDataSection(e){const{doc:i}=e,s=i.createElement("div");s.dataset.wmapDataTab="1",Object.assign(s.style,{display:"flex",flexDirection:"column",gap:C.md,minWidth:"0"});const c=i.createElement("div");Object.assign(c.style,{display:"flex",alignItems:"center",gap:C.md,flexWrap:"wrap"}),s.appendChild(c);const p=i.createElement("div");Object.assign(p.style,{display:"flex",flexDirection:"column",gap:C.md,minWidth:"0"}),s.appendChild(p);const g=DATA_VIEWS.filter(f=>!e.views||e.views.includes(f.key)),r=F(i,"Table",g,e.view,"wmapDataView",f=>e.onViewChange(f));c.appendChild(r.el);let d;switch(e.view){case"dies":d=Q(e);break;case"wafers":d=Z(e);break;default:d=J(e)}let u=()=>{};if(e.view==="dies"){const f=F(i,"Export format",[{key:"wide",label:"Wide"},{key:"long",label:"Long"}],e.diesLayout,"wmapDataLayout",t=>{f.set(t),e.onDiesLayoutChange(t),d.actions?.setLayout?.(t),u()});$(f.el,"Wide: a column per test. Long: a row per die per test, for tools that want tidy data. The table on screen is always wide.");const l=i.createElement("span");Object.assign(l.style,{display:"inline-flex",alignItems:"center",gap:C.sm});const o=i.createElement("span");o.textContent="Export format:",Object.assign(o.style,{color:y.label,fontSize:L.body}),l.append(o,f.el),c.appendChild(l)}if(d.actions){const f=d.actions,l=i.createElement("div");l.style.flex="1",c.appendChild(l);const o=B(i,"Export CSV");o.dataset.wmapDataExport="1",o.addEventListener("click",()=>{if(o.disabled)return;const m=o.textContent,h=f.exportCsv();h&&(o.disabled=!0,o.setAttribute("aria-busy","true"),h.catch(()=>{}).finally(()=>{o.disabled=!1,o.removeAttribute("aria-busy"),o.textContent=m}))});const t=B(i,"Copy");t.dataset.wmapDataCopy="1",f.copyText?($(t,"Copy the table as tab-separated text, to paste into a spreadsheet"),t.addEventListener("click",()=>{const m=f.copyText(),h=i.defaultView?.navigator?.clipboard;if(!h)return;const w=t.textContent;h.writeText(m).then(()=>{t.textContent="Copied",i.defaultView?.setTimeout(()=>{t.textContent=w},1500)}).catch(()=>{})})):(t.disabled=!0,$(t,`Too large to copy (over ${COPY_CELL_LIMIT.toLocaleString()} cells). Use Export CSV.`)),c.append(t,o);const a=i.createElement("div");a.dataset.wmapDataNote="1",a.textContent=f.note(),u=()=>{a.textContent=f.note()},Object.assign(a.style,{color:y.label,fontSize:L.body}),s.insertBefore(a,p)}return p.appendChild(d.el),{card:s,destroy:()=>d.destroy()}}function J(e){const{doc:i}=e,s=i.createElement("div");Object.assign(s.style,{display:"flex",flexDirection:"column",gap:C.lg,minWidth:"0"});const c=[],p=e.groups&&e.groups.length>0?e.groups:void 0,g=p??[{key:"",items:e.items}];for(const r of g){if(p){const u=i.createElement("div");u.textContent=`${e.groupLabelText??"Group"}: ${r.key}`,Object.assign(u.style,{fontWeight:"700",color:y.text,fontSize:L.body}),s.appendChild(u)}const d=e.buildStatistics(r.items);c.push(d.destroy);for(const u of d.cards)s.appendChild(u)}return!s.querySelector("table")&&!s.textContent&&(s.textContent="No test statistics to show for these wafers.",Object.assign(s.style,{color:y.label,fontSize:L.body})),{el:s,destroy:()=>{for(const r of c)r()}}}function Q(e){const{doc:i,items:s}=e,c=new WeakMap,p=new WeakMap,g=new WeakMap,r=[];let d=!1;for(const n of s){n.wafer&&(d=!0);for(const b of n.dies)c.set(b,n.label),g.set(b,n.waferIndex),n.wafer&&p.set(b,n.wafer),r.push(b)}if(r.length===0)return M(i,"No dies to show.");const{columns:u,visibleColumns:f}=U(r,e.allTestDefs,{...e.dieListOptions,metadataFields:e.metadataFields,extraColumn:s.length>1||e.population?{label:"Wafer",get:n=>c.get(n)}:void 0,getWafer:d?(n=>p.get(n)):void 0,ringCount:e.ringCount,waferMetadata:s.some(n=>n.wafer)?H(s.map(n=>({metadata:n.wafer?.metadata}))):void 0,ownerDocument:i}),l=f.map(n=>({header:n.label,get:n.get,numeric:n.test!==void 0&&k(n.test),...n.test!==void 0&&k(n.test)?{sortKey:b=>O(b,n.test.testNumber)}:{}})),o=V({columns:l,rows:r,ariaLabel:`Dies (${r.length})`,ownerDocument:i,onRowClick:e.onLocateDie?n=>e.onLocateDie(n,g.get(n)):void 0});o.el.style.maxHeight="70vh",o.el.style.minHeight="240px";const t=e.population?`-${e.population.fileTag}`:"",a=u.filter(n=>n.test!==void 0),m=u.filter(n=>n.test===void 0),h=r.length*u.length,w=r.length*a.length,x=()=>u.map(n=>({header:n.csvLabel??n.label,get:n.csvGet??n.get})),v=()=>[...m.map(n=>({header:n.csvLabel??n.label,get:b=>(n.csvGet??n.get)(b.die)})),{header:"Test",get:n=>R(n.test,n.test.testNumber)},{header:"Test number",get:n=>n.test.testNumber},{header:"Unit",get:n=>n.test.unit??""},{header:"Value",get:n=>n.value}];function*_(){for(const n of o.orderedRows())for(const b of a){const S=(b.csvGet??b.get)(n);S===""||S===void 0||S===null||(yield{die:n,test:b.test,value:S})}}let T=e.diesLayout==="long"&&a.length>0;const P=()=>T?`Long format: up to ${D(w,"row")} (a die with no result for a test has no row)`+(w>EXCEL_ROW_LIMIT?` \u2014 more than Excel opens (${EXCEL_ROW_LIMIT.toLocaleString()}).`:"."):`Wide format: ${D(r.length,"row")} \xD7 ${D(u.length,"column")}`+(r.length>EXCEL_ROW_LIMIT?` \u2014 more than Excel opens (${EXCEL_ROW_LIMIT.toLocaleString()} rows).`:".");return{el:o.el,destroy:()=>o.destroy(),actions:{note:()=>`${D(r.length,"die")} ${e.population?.phrase??`on ${D(s.length,"wafer")}`}. ${P()} Export follows the order shown.${e.onLocateDie?" Click a row to show that die on the map.":""}`,setLayout:n=>{T=n==="long"&&a.length>0},exportCsv:()=>T?E(v(),_(),`die-list-long${t}.csv`,e.onSaveText,{rowCount:w}):E(x(),o.orderedRows(),`die-list${t}.csv`,e.onSaveText,{rowCount:r.length}),copyText:h<=COPY_CELL_LIMIT?()=>N(x(),o.orderedRows()):null}}}function Z(e){const{doc:i,items:s}=e;if(s.length===0)return M(i,"No wafers to show.");const c=s.map(t=>{const a=e.yieldByWaferIndex.get(t.waferIndex),m=Y([{label:t.label,dies:t.dies,passBins:t.passBins,yieldPercent:a,key:t.waferIndex}])[0]?.percent;return{item:t,yieldPercent:t.dies.length?m??null:null}}),p=[];for(const{item:t}of c)for(const a of Object.keys(t.wafer?.metadata??{}))!p.includes(a)&&I((t.wafer?.metadata)[a])!==void 0&&p.push(a);const g=e.testDefs.slice(0,WAFER_TEST_COLUMNS),r=(t,a)=>{const m=t.item.statsSummary?.stats.perTestStats?.find(x=>x.testNumber===a.testNumber);if(m)return m.mean;let h=0,w=0;for(const x of t.item.dies){if(!W(x))continue;const v=O(x,a.testNumber);v!==void 0&&Number.isFinite(v)&&(h++,w+=v)}return h?w/h:void 0},d=[{header:"Wafer",cell:t=>t.item.label},...p.map(t=>({header:z(t),cell:a=>I(a.item.wafer?.metadata?.[t])??""})),{header:"Dies",cell:t=>t.item.dies.length,numeric:!0},{header:"Yield-eligible dies",cell:t=>t.item.dies.filter(a=>W(a)).length,numeric:!0},{header:"Yield %",cell:t=>t.yieldPercent===null?"":t.yieldPercent,show:t=>t.yieldPercent===null?"":t.yieldPercent.toFixed(1),numeric:!0},...g.map(t=>({header:`${R(t,t.testNumber)}${t.unit?` (${t.unit})`:""} mean`,cell:a=>r(a,t),show:a=>{const m=r(a,t);return m===void 0?"":G(m,t.unit)},numeric:!0}))],u=d.map(t=>({header:t.header,get:a=>t.show?t.show(a):String(t.cell(a)??""),numeric:t.numeric,...t.numeric?{sortKey:a=>{const m=t.cell(a);return typeof m=="number"?m:void 0}}:{}})),f=V({columns:u,rows:c,ariaLabel:`Wafers (${c.length})`,ownerDocument:i,onRowClick:e.onOpenWafer?t=>e.onOpenWafer(t.item.waferIndex,t.item.label):void 0});f.el.style.maxHeight="70vh",f.el.style.minHeight="120px";const l=()=>d.map(t=>({header:t.header,get:t.cell})),o=e.testDefs.length>g.length?` Mean columns cover the first ${g.length} of ${e.testDefs.length} tests.`:"";return{el:f.el,destroy:()=>f.destroy(),actions:{note:()=>`${D(c.length,"wafer")}.${o}${e.onOpenWafer?" Click a row to open that wafer.":""} Export follows the order shown.`,exportCsv:()=>E(l(),f.orderedRows(),"wafers.csv",e.onSaveText,{rowCount:c.length}),copyText:c.length*d.length<=COPY_CELL_LIMIT?()=>N(l(),f.orderedRows()):null}}}function M(e,i){const s=e.createElement("div");return s.textContent=i,Object.assign(s.style,{color:y.label,fontSize:L.body,padding:C.md}),{el:s,destroy:()=>{}}}function B(e,i){const s=e.createElement("button");return s.type="button",s.textContent=i,Object.assign(s.style,{fontSize:L.body,padding:"3px 10px",borderRadius:j.control,...K("outlined"),background:y.menuBg}),A(s),s}function F(e,i,s,c,p,g){const r=e.createElement("div");r.setAttribute("role","radiogroup"),r.setAttribute("aria-label",i),Object.assign(r.style,{display:"inline-flex",background:y.menuBg,border:`1px solid ${y.menuBorder}`,borderRadius:j.control,overflow:"hidden"});const d=[];let u=c;const f=()=>{for(const l of d){const o=l.dataset[p]===u;l.setAttribute("aria-checked",String(o)),l.tabIndex=o?0:-1,Object.assign(l.style,{background:o?y.bgActive:"transparent",color:o?y.iconActive:y.label,fontWeight:o?"600":"400"})}};for(const l of s){const o=e.createElement("button");o.type="button",o.textContent=l.label,o.dataset[p]=l.key,o.setAttribute("role","radio"),Object.assign(o.style,{border:"none",cursor:"pointer",fontSize:L.body,padding:`${C.xs} ${C.md}`,whiteSpace:"nowrap"}),A(o,"bare"),o.addEventListener("click",()=>{u!==l.key&&g(l.key)}),d.push(o),r.appendChild(o)}return f(),r.addEventListener("keydown",l=>{if(l.key!=="ArrowLeft"&&l.key!=="ArrowRight")return;const o=d.indexOf(e.activeElement);if(o<0)return;l.preventDefault();const t=d[(o+(l.key==="ArrowRight"?1:-1)+d.length)%d.length];t.click(),t.focus()}),{el:r,set:l=>{u=l,f()}}}export function renderDataTables(e){const{doc:i}=e,s=i.createElement("div");Object.assign(s.style,{display:"flex",flexDirection:"column",minWidth:"0",minHeight:"0",flex:"1",padding:C.md});const c=e.items.map((a,m)=>({...a,waferIndex:a.waferIndex??m})),p=c.flatMap(a=>a.dies),g=e.testDefs??[],r=g.filter(k),d=e.views??(c.length>1?["statistics","dies","wafers"]:["statistics","dies"]),u=e.population?{populationLabel:`${D(p.length,"die")} ${e.population}`}:c.length>1?{perWaferMetadata:c.map(a=>a.wafer?.metadata??{}),populationLabel:`${D(c.length,"wafer")} pooled`}:{waferMetadata:c[0]?.wafer?.metadata};let f=d.includes(e.view)?e.view:d[0],l="wide",o=null;const t=()=>{o?.destroy(),o=renderDataSection({doc:i,items:c,testDefs:r,allTestDefs:g,ringCount:e.ringCount,yieldByWaferIndex:new Map,onSaveText:e.onSaveText,views:d,view:f,dieListOptions:e.dieListOptions,metadataFields:e.metadataFields,onLocateDie:e.onLocateDie,population:e.population?{phrase:e.population,fileTag:"selection"}:void 0,buildStatistics:a=>{const m=a.flatMap(v=>v.dies),h=[],w=q(m,r,void 0,void 0,e.onSaveText,u,a.map(v=>({dies:v.dies})),void 0,"full");w&&h.push(w);const x=X(m,g,void 0,e.onSaveText,u);return x&&h.push(x),{cards:h,destroy:()=>{}}},onViewChange:a=>{f=a,t()},diesLayout:l,onDiesLayoutChange:a=>{l=a}}),s.replaceChildren(o.card)};return t(),{el:s,destroy:()=>o?.destroy()}}export function renderSelectionTables(e){return renderDataTables({...e,views:e.items.length>1?["statistics","dies","wafers"]:["statistics","dies"]})}
@@ -4,6 +4,7 @@ import { type MetadataFieldDef, type TestDef } from '../renderer/buildWaferMap.j
4
4
  import type { WaferMetadata } from '../core/metadata.js';
5
5
  import { type MetadataKeySelection } from '../stats/metadataColumns.js';
6
6
  import { type SaveTextHandler } from './toolbar.js';
7
+ import { type CsvCell } from './tableExport.js';
7
8
  /** Display preferences for the built-in die-list table. Everything here is a
8
9
  * choice about what to show; the *data* it acts on (wafer metadata, metadata
9
10
  * field definitions) is supplied by the library from its own build result
@@ -12,10 +13,12 @@ import { type SaveTextHandler } from './toolbar.js';
12
13
  * identity data by mistake. */
13
14
  export interface DieListDisplayOptions {
14
15
  /**
15
- * Show a "View die list" link inside the Summary panel (`renderWaferMap`)
16
+ * Show a "Data tables" button inside the Summary panel (`renderWaferMap`)
16
17
  * / lot Summary panel (`renderWaferGallery`) that opens this wafer's — or,
17
- * in the gallery, the whole lot's — dies as a table in a modal, with the
18
- * same "Export CSV" button every die list has. **Default
18
+ * in the gallery, the whole lot's — tables in a modal: Statistics and Dies
19
+ * (and, for a lot, Wafers), each with Export CSV and Copy. The Dies table is
20
+ * virtual, so `maxRows` does not apply to it. Works whether or not Insights
21
+ * is enabled. **Default
19
22
  * `true`** — set `enabled: false` to hide it. Requires a Summary panel to
20
23
  * be reachable at all (`RenderOptions.summaryPanel` / `GalleryOptions.summaryPanel`),
21
24
  * since that panel is this link's only home; irrelevant otherwise.
@@ -24,7 +27,8 @@ export interface DieListDisplayOptions {
24
27
  * without position" footer, both of which show a die list unconditionally
25
28
  * regardless of this flag — those exist because there is literally nothing
26
29
  * else to show, not as an opt-in extra. The rest of this options object
27
- * (column selection, `maxRows`, …) is shared by both surfaces.
30
+ * (column selection, `maxRows`, …) is shared by those two surfaces and the modal's Dies table
31
+ * (which ignores `maxRows`).
28
32
  */
29
33
  enabled?: boolean;
30
34
  /**
@@ -123,6 +127,37 @@ export interface DieListOptions extends DieListDisplayOptions {
123
127
  */
124
128
  ownerDocument?: Document;
125
129
  }
130
+ /** One scoped `<style>` block for die-list cells, shared by every table
131
+ * instance, instead of an inline style object per `<td>`/`<th>`. At lot
132
+ * scale (hundreds of thousands of dies) that is the difference between one
133
+ * style recalculation and one Object.assign per cell — a real cost when the
134
+ * table already has no virtualisation. CLR's tokens are `var(--wmap-*, …)`
135
+ * strings, so they resolve identically in a stylesheet rule as inline.
136
+ * Injected into `doc` specifically — see `stylesInjectedInto`'s comment. */
137
+ export declare function ensureStylesInjected(doc: Document): void;
138
+ export type DieColumn = {
139
+ label: string;
140
+ /** CSV header when it must differ from the on-screen one — a derived test's
141
+ * column, where the screen shows the † mark and the file, which has no key
142
+ * to explain a glyph, states what it was derived from. */
143
+ csvLabel?: string;
144
+ get: (die: Die) => string;
145
+ /** CSV rendering when it must differ from the on-screen cell — see the test
146
+ * columns, where the screen shows `300 mV` and the file a bare `300`. */
147
+ csvGet?: (die: Die) => CsvCell;
148
+ csvOnly?: boolean;
149
+ /** Set on a test's column — what the long-format export (one row per die per test) unpivots. */
150
+ test?: TestDef;
151
+ };
152
+ /** The columns of a die table — what the screen shows, and (where it differs)
153
+ * what the file carries — for `dies`. Shared by the die-list modal and the Data
154
+ * tab's Dies view, so the two cannot describe a die differently. */
155
+ export declare function resolveDieColumns(dies: Die[], testDefs: TestDef[] | undefined, options?: DieListOptions): {
156
+ columns: DieColumn[];
157
+ visibleColumns: DieColumn[];
158
+ testColumns: TestDef[];
159
+ truncatedKeys: string[];
160
+ };
126
161
  /**
127
162
  * Build a scrollable die-list table + "Export CSV" button. Returns `null`
128
163
  * when `dies` is empty (nothing to show).
@@ -1,13 +1,12 @@
1
- import{hasPosition as F,isPositionedDie as V}from"../core/dies.js";import{testsPresent as G,testValue as H}from"../core/dieTable.js";import{classifyDie as Y}from"../core/classify.js";import{isParametricTest as _,getTestPassStatus as S}from"../renderer/buildWaferMap.js";import{resolveMetadataColumns as U}from"../stats/metadataColumns.js";import{LEADING as N,TRACKING as X,wireControlHover as Q,controlStyle as q,SPACE as J,RADIUS as P,FONT as g,CLR as d,saveTextFile as Z}from"./toolbar.js";import{csvField as k}from"./summaryPanel.js";import{fmt as j}from"../renderer/fmt.js";import{testLabel as E,markedTestLabel as M,derivedFields as ee,derivedKeyText as te,derivedTestSource as ne,isDerivedTest as ie,DERIVED_KEY as le}from"../renderer/testLabel.js";const L=(t,a)=>a?`${t} (${a})`:t,ae=5e4,z=new WeakSet;function re(t){if(z.has(t))return;z.add(t);const a=t.createElement("style");a.textContent=`
1
+ import{requireRingCount as F}from"../core/ringCount.js";import{hasPosition as T,isPositionedDie as M}from"../core/dies.js";import{testsPresent as K,testValue as I}from"../core/dieTable.js";import{classifyDie as z}from"../core/classify.js";import{isParametricTest as B,getTestPassStatus as y}from"../renderer/buildWaferMap.js";import{resolveMetadataColumns as O}from"../stats/metadataColumns.js";import{LEADING as N,TRACKING as W,wireControlHover as j,controlStyle as V,SPACE as G,RADIUS as P,FONT as w,CLR as c}from"./toolbar.js";import{exportCsv as Y}from"./tableExport.js";import{fmt as _}from"../renderer/fmt.js";import{testLabel as L,markedTestLabel as H,derivedFields as U,derivedKeyText as X,derivedTestSource as q,isDerivedTest as Q,DERIVED_KEY as J}from"../renderer/testLabel.js";const $=(t,r)=>r?`${t} (${r})`:t,Z=5e4,k=new WeakSet;export function ensureStylesInjected(t){if(k.has(t))return;k.add(t);const r=t.createElement("style");r.textContent=`
2
2
  .wmap-dielist-table { width: 100%; border-collapse: collapse; font-size: 12px; font-variant-numeric: tabular-nums; }
3
3
  .wmap-dielist-th {
4
4
  text-align: left; padding: 4px 8px; position: sticky; top: 0;
5
- background: ${d.panelBg}; color: ${d.label}; font-weight: 600;
6
- border-bottom: 1px solid ${d.menuBorder}; white-space: nowrap;
5
+ background: ${c.panelBg}; color: ${c.label}; font-weight: 600;
6
+ border-bottom: 1px solid ${c.menuBorder}; white-space: nowrap;
7
7
  }
8
8
  .wmap-dielist-td {
9
- padding: 4px 8px; color: ${d.text};
10
- border-bottom: 1px solid ${d.menuBorder}; white-space: nowrap;
9
+ padding: 4px 8px; color: ${c.text};
10
+ border-bottom: 1px solid ${c.menuBorder}; white-space: nowrap;
11
11
  }
12
- `,t.head.appendChild(a)}function s(t,a,n,i){const r=t.createElement(a);return n&&Object.assign(r.style,n),i!==void 0&&(r.textContent=i),r}function oe(t){return F(t)?String(t.x):""}function se(t){return F(t)?String(t.y):""}function de(t,a){return a!==void 0?a.filter(n=>n.testNumber!==void 0):G(t,"values").map(n=>({testNumber:n,name:E(void 0,n)}))}function ce(t,a,n){if(!a)return;const i=new Map;for(const r of t){if(!V(r))continue;const u=a(r);u&&i.set(r,Y(r,u,{ringCount:n}))}return i.size>0?i:void 0}export function buildDieListSection(t,a,n={}){if(!t.length)return null;const i=n.ownerDocument??document;re(i);const r=de(t,a),u=n.maxRows??ae,C=Math.max(0,t.length-u),K=C>0?t.slice(0,u):t,m=s(i,"div",{display:"flex",flexDirection:"column",gap:J.md,flex:"1",minHeight:"0",minWidth:"0"}),v=s(i,"div",{display:"flex",alignItems:"center",justifyContent:"space-between",flexShrink:"0"});v.appendChild(s(i,"div",{fontSize:g.meta,fontWeight:"700",letterSpacing:X,textTransform:"uppercase",color:d.label},n.title??`Die list (${t.length})`));const h=s(i,"button",{fontSize:g.body,padding:"3px 8px",borderRadius:P.control,...q("outlined"),background:d.menuBg},"Export CSV");h.type="button",Q(h),v.appendChild(h),m.appendChild(v),n.note&&m.appendChild(s(i,"div",{fontSize:g.body,color:d.label,lineHeight:N.base,flexShrink:"0"},n.note));const w=ce(t,n.getWafer,n.ringCount??4),B=t.some(e=>e.edgeExcluded===!0),O=[...n.extraColumn?[n.extraColumn.label]:[],"X","Y","Ring","Quadrant","Edge excluded","Site","Hard bin","Soft bin",...r.map(e=>L(E(e,e.testNumber),e.unit))],{columns:W,truncatedKeys:x}=U({dies:t,waferMetadata:n.waferMetadata,metadataFields:n.metadataFields,dieKeys:n.metadataColumns,waferKeys:n.waferMetadataColumns==="none"?"none":void 0,waferPlacement:n.waferMetadataColumns??"csv",reservedLabels:O}),y=[...n.extraColumn?[{label:n.extraColumn.label,get:e=>n.extraColumn.get(e)??""}]:[],{label:"X",get:oe},{label:"Y",get:se},...w?[{label:"Ring",get:e=>{const l=w.get(e);return l?String(l.ring):""}},{label:"Quadrant",get:e=>w.get(e)?.quadrant??""}]:[],...B?[{label:"Edge excluded",get:e=>e.edgeExcluded?"Yes":"No"}]:[],{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):""},...r.map(e=>{const l=!_(e);return{label:L(M(e,e.testNumber),e.unit),...ie(e)?{csvLabel:`${L(E(e,e.testNumber),e.unit)} [derived from ${ne(e)??le}]`}:{},get:o=>{if(l){const p=S(o,e.testNumber,e);return p===void 0?"":p?"PASS":"FAIL"}const c=H(o,e.testNumber);if(c!==void 0)return j(c,e.unit);const b=S(o,e.testNumber,e);return b===void 0?"":b?"PASS":"FAIL"},csvGet:o=>{if(l){const p=S(o,e.testNumber,e);return p===void 0?"":p?"PASS":"FAIL"}const c=H(o,e.testNumber);if(c!==void 0)return j(c,void 0,"engineering");const b=S(o,e.testNumber,e);return b===void 0?"":b?"PASS":"FAIL"}}}),...W.map(e=>({label:e.label,get:e.get,csvOnly:e.csvOnly}))],$=y.filter(e=>!e.csvOnly),A=te(r.map(e=>({label:M(e,e.testNumber),...ee(e)})));A&&m.appendChild(s(i,"div",{fontSize:g.body,color:d.label,lineHeight:N.base,flexShrink:"0"},A));const D=s(i,"div",{overflow:"auto",flex:"1",minHeight:"0",minWidth:"0",border:`1px solid ${d.menuBorder}`,borderRadius:P.control,...n.maxHeight?{maxHeight:n.maxHeight}:{}}),f=i.createElement("table");f.className="wmap-dielist-table",f.setAttribute("aria-label",n.title??`Die list (${t.length} dies)`);const R=s(i,"thead"),T=s(i,"tr");for(const e of $){const l=i.createElement("th");l.className="wmap-dielist-th",l.scope="col",l.textContent=e.label,T.appendChild(l)}R.appendChild(T),f.appendChild(R);const I=s(i,"tbody");for(const e of K){const l=s(i,"tr");for(const o of $){const c=i.createElement("td");c.className="wmap-dielist-td",c.textContent=o.get(e),l.appendChild(c)}I.appendChild(l)}if(f.appendChild(I),D.appendChild(f),m.appendChild(D),C>0||x.length>0){const e=[];C>0&&e.push(`Showing the first ${u.toLocaleString()} of ${t.length.toLocaleString()} dies. The CSV export contains all ${t.length.toLocaleString()}.`),x.length>0&&e.push(`${x.length} further metadata field${x.length===1?"":"s"} not shown.`),m.appendChild(s(i,"div",{fontSize:g.body,color:d.label,lineHeight:N.base,flexShrink:"0"},e.join(" ")))}return h.addEventListener("click",()=>{const e=[y.map(l=>k(l.csvLabel??l.label)).join(",")];for(const l of t)e.push(y.map(o=>k((o.csvGet??o.get)(l))).join(","));Z(e.join(`
13
- `),"die-list.csv","text/csv",n.onSaveText)}),m}
12
+ `,t.head.appendChild(r)}function d(t,r,n,i){const l=t.createElement(r);return n&&Object.assign(l.style,n),i!==void 0&&(l.textContent=i),l}function ee(t){return T(t)?String(t.x):""}function te(t){return T(t)?String(t.y):""}function ne(t,r){return r!==void 0?r.filter(n=>n.testNumber!==void 0):K(t,"values").map(n=>({testNumber:n,name:L(void 0,n)}))}function ie(t,r,n){if(!r)return;const i=F(n,"the die list"),l=new Map;for(const m of t){if(!M(m))continue;const C=r(m);C&&l.set(m,z(m,C,{ringCount:i}))}return l.size>0?l:void 0}export function resolveDieColumns(t,r,n={}){const i=ne(t,r),l=ie(t,n.getWafer,n),m=t.some(e=>e.edgeExcluded===!0),C=[...n.extraColumn?[n.extraColumn.label]:[],"X","Y","Ring","Quadrant","Edge excluded","Site","Hard bin","Soft bin",...i.map(e=>$(L(e,e.testNumber),e.unit))],{columns:p,truncatedKeys:v}=O({dies:t,waferMetadata:n.waferMetadata,metadataFields:n.metadataFields,dieKeys:n.metadataColumns,waferKeys:n.waferMetadataColumns==="none"?"none":void 0,waferPlacement:n.waferMetadataColumns??"csv",reservedLabels:C}),a=[...n.extraColumn?[{label:n.extraColumn.label,get:e=>n.extraColumn.get(e)??""}]:[],{label:"X",get:ee},{label:"Y",get:te},...l?[{label:"Ring",get:e=>{const x=l.get(e);return x?String(x.ring):""}},{label:"Quadrant",get:e=>l.get(e)?.quadrant??""}]:[],...m?[{label:"Edge excluded",get:e=>e.edgeExcluded?"Yes":"No"}]:[],{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=>{const x=!B(e);return{test:e,label:$(H(e,e.testNumber),e.unit),...Q(e)?{csvLabel:`${$(L(e,e.testNumber),e.unit)} [derived from ${q(e)??J}]`}:{},get:s=>{if(x){const u=y(s,e.testNumber,e);return u===void 0?"":u?"PASS":"FAIL"}const g=I(s,e.testNumber);if(g!==void 0)return _(g,e.unit);const h=y(s,e.testNumber,e);return h===void 0?"":h?"PASS":"FAIL"},csvGet:s=>{if(x){const u=y(s,e.testNumber,e);return u===void 0?"":u?"PASS":"FAIL"}const g=I(s,e.testNumber);if(g!==void 0)return g;const h=y(s,e.testNumber,e);return h===void 0?"":h?"PASS":"FAIL"}}}),...p.map(e=>({label:e.label,get:e.get,csvOnly:e.csvOnly}))],E=a.filter(e=>!e.csvOnly);return{columns:a,visibleColumns:E,testColumns:i,truncatedKeys:v}}export function buildDieListSection(t,r,n={}){if(!t.length)return null;const i=n.ownerDocument??document;ensureStylesInjected(i);const l=n.maxRows??Z,m=Math.max(0,t.length-l),C=m>0?t.slice(0,l):t,p=d(i,"div",{display:"flex",flexDirection:"column",gap:G.md,flex:"1",minHeight:"0",minWidth:"0"}),v=d(i,"div",{display:"flex",alignItems:"center",justifyContent:"space-between",flexShrink:"0"});v.appendChild(d(i,"div",{fontSize:w.meta,fontWeight:"700",letterSpacing:W,textTransform:"uppercase",color:c.label},n.title??`Die list (${t.length})`));const a=d(i,"button",{fontSize:w.body,padding:"3px 8px",borderRadius:P.control,...V("outlined"),background:c.menuBg},"Export CSV");a.type="button",j(a),v.appendChild(a),p.appendChild(v),n.note&&p.appendChild(d(i,"div",{fontSize:w.body,color:c.label,lineHeight:N.base,flexShrink:"0"},n.note));const{columns:E,visibleColumns:e,testColumns:x,truncatedKeys:s}=resolveDieColumns(t,r,n),g=X(x.map(o=>({label:H(o,o.testNumber),...U(o)})));g&&p.appendChild(d(i,"div",{fontSize:w.body,color:c.label,lineHeight:N.base,flexShrink:"0"},g));const h=d(i,"div",{overflow:"auto",flex:"1",minHeight:"0",minWidth:"0",border:`1px solid ${c.menuBorder}`,borderRadius:P.control,...n.maxHeight?{maxHeight:n.maxHeight}:{}}),u=i.createElement("table");u.className="wmap-dielist-table",u.setAttribute("aria-label",n.title??`Die list (${t.length} dies)`);const A=d(i,"thead"),D=d(i,"tr");for(const o of e){const f=i.createElement("th");f.className="wmap-dielist-th",f.scope="col",f.textContent=o.label,D.appendChild(f)}A.appendChild(D),u.appendChild(A);const R=d(i,"tbody");for(const o of C){const f=d(i,"tr");for(const b of e){const S=i.createElement("td");S.className="wmap-dielist-td",S.textContent=b.get(o),f.appendChild(S)}R.appendChild(f)}if(u.appendChild(R),h.appendChild(u),p.appendChild(h),m>0||s.length>0){const o=[];m>0&&o.push(`Showing the first ${l.toLocaleString()} of ${t.length.toLocaleString()} dies. The CSV export contains all ${t.length.toLocaleString()}.`),s.length>0&&o.push(`${s.length} further metadata field${s.length===1?"":"s"} not shown.`),p.appendChild(d(i,"div",{fontSize:w.body,color:c.label,lineHeight:N.base,flexShrink:"0"},o.join(" ")))}return a.addEventListener("click",()=>{if(a.disabled)return;const o=a.textContent,f=Y(E.map(b=>({header:b.csvLabel??b.label,get:b.csvGet??b.get})),t,"die-list.csv",n.onSaveText,{onProgress:(b,S)=>{a.textContent=S?`Exporting ${Math.floor(100*b/S)}%`:`Exporting ${b.toLocaleString()} rows`}});f&&(a.disabled=!0,a.setAttribute("aria-busy","true"),f.catch(()=>{}).finally(()=>{a.disabled=!1,a.removeAttribute("aria-busy"),a.textContent=o}))}),p}
@@ -1,11 +1,28 @@
1
- import { type SweepSpec } from '../stats/sweep.js';
2
- import { type SaveImageHandler } from './toolbar.js';
1
+ import type { Die } from '../core/dies.js';
2
+ import { type SaveImageHandler, type SaveTextHandler } from './toolbar.js';
3
3
  import type { DrilldownSource, DrilldownItem } from './chartPopulation.js';
4
+ import type { PlotStore } from './plotStore.js';
4
5
  export type { DrilldownSource, DrilldownItem };
5
6
  /** What the targets can draw on — the host's chart definitions and hooks. */
6
7
  export interface DrilldownContext {
7
- sweeps?: SweepSpec[];
8
+ /** The reader's saved plots: each is a row in the menu, opened on the selection. */
9
+ plots?: PlotStore;
8
10
  onSaveImage?: SaveImageHandler;
11
+ /** The host's CSV save hook, for the tables' Export CSV. */
12
+ onSaveText?: SaveTextHandler;
13
+ /** A die row clicked in a table: show that die on the map it belongs to. The table steps aside first. */
14
+ onLocateDie?: (die: Die, waferIndex: number | undefined) => void;
15
+ /**
16
+ * A narrower population for the same dies, offered as the menu's first row: the menu reopens on it, and its own
17
+ * `narrower` is the population it left, so the row swaps back. Set by a gallery when dies are selected on several
18
+ * wafers and the right-click is on one of them.
19
+ */
20
+ narrower?: {
21
+ label: string; /** The swap-back row's wording before the population; default "All selected". */
22
+ backLabel?: string;
23
+ source: DrilldownSource;
24
+ ctx?: DrilldownContext;
25
+ };
9
26
  }
10
27
  /**
11
28
  * Open the drilldown menu for `source` at viewport point `at`.
@@ -1 +1 @@
1
- import{isYieldEligibleDie as b}from"../core/dies.js";import{testValue as T}from"../core/dieTable.js";import{isParametricTest as y}from"../renderer/buildWaferMap.js";import{buildSweepData as C}from"../stats/sweep.js";import{renderSweepPanel as E}from"./charts/sweep.js";import{renderHistogramPanel as $}from"./charts/histogram.js";import{renderCapabilityPanel as I}from"./charts/capability.js";import{openChartExpandModal as M,populationPhrase as w}from"./charts/chartShell.js";import{buildCheckMenuEl as x,createToolbarHelpers as N,getTooltip as R,menuLayerFor as L,wireMenuA11y as V,CLR as k,FONT as F,SPACE as O}from"./toolbar.js";const P=30,v=e=>e.items.flatMap(t=>t.dies);function A(e){return e.items.map(t=>({...t,dies:t.dies.filter(a=>b(a))}))}function H(e,t,a){return(e.testDefs??[]).filter(y).map(s=>s.testNumber).filter(s=>{let o=0;for(const i of t){const n=T(i,s);if(n!==void 0&&Number.isFinite(n)&&++o>=a)return!0}return!1})}function S(e,t,a,s,o){const n=e.ownerDocument.createElement("div");n.textContent=a,n.dataset.wmapPopulation="1",Object.assign(n.style,{color:k.label,fontSize:F.body,marginBottom:O.sm});const r=e.querySelector("[data-wmap-chart-expand]")?.parentElement;r?.parentElement===e?r.after(n):e.prepend(n),M(e,t,{anchor:s,onClosed:o})}function W(e,t,a,s){const o=v(t);let i=t.notMeasuredReason??null;return!i&&!o.some(n=>b(n))&&(i="These dies are all partial or edge-excluded, which charts leave out"),i||C(o,t.testDefs,e).series.some(r=>r.points.some(d=>d.count>0))||(i="These dies have no values for this sweep\u2019s tests"),{section:"Sweep",label:e.title,unavailable:i,open:()=>{const n=E({spec:e,dies:o,population:t.population,testDefs:t.testDefs,onSaveImage:a.onSaveImage,ownerDocument:s.ownerDocument}),r=o.filter(d=>b(d)).length;M(n.card,`${e.title} \u2014 ${w(r,o.length,t.population)}`,{anchor:s,onClosed:()=>n.destroy()})}}}function B(e,t,a){const s=A(e),o=v(e).length,i=s.flatMap(l=>l.dies),n=w(i.length,o,e.population),r=(e.testDefs??[]).filter(y),d=a.ownerDocument,f=(l,c)=>e.notMeasuredReason?e.notMeasuredReason:i.length===0?"These dies are all partial or edge-excluded, which charts leave out":H(e,i,l).length>0?null:c,u={section:"Distributions",label:"Value histogram",unavailable:f(1,"These dies have no parametric test values"),open:()=>{const l=$({title:"Value histogram",items:s,testDefs:r,selectedTestNumber:e.activeTest,onSaveImage:t.onSaveImage,ownerDocument:d});S(l.card,`Value histogram \u2014 ${n}`,`Population: ${n}`,a,()=>l.destroy())}},h={section:"Distributions",label:"Process capability",unavailable:f(2,"Needs at least 2 of these dies with values for one test"),open:()=>{const l=I({title:"Process capability",items:s,testDefs:r,selectedTestNumber:e.activeTest,onSaveImage:t.onSaveImage,ownerDocument:d}),c=i.length<P?` \xB7 fewer than ${P} dies, so each Ppk is a rough estimate`:"";S(l.card,`Process capability \u2014 ${n}`,`Population: ${n}${c}`,a,()=>l.destroy())}};return[u,h]}function _(e,t,a){return[...B(e,t,a),...(t.sweeps??[]).map(s=>W(s,e,t,a))]}const g=new WeakMap;export function openDrilldownMenu(e,t,a,s){const o=t.ownerDocument,i=o.defaultView??window;g.get(o)?.();const n=_(a,s,t),r=[];let d;for(const p of n)p.section!==d&&(r.push({section:p.section}),d=p.section),r.push({label:p.label,active:!1,action:!0,enabled:p.unavailable===null,disabledHint:p.unavailable??void 0,onClick:()=>{m(),p.open()}});const{makeMenuSection:f}=N(R(o)),u=x(new DOMRect(e.x,e.y-4,0,0),r,{makeMenuSection:f},i),h=v(a).length;u.setAttribute("role","menu"),u.setAttribute("aria-label",`Open a chart of ${w(h,h,a.population)}`),u.dataset.wmapDrilldownMenu="1",L(t).appendChild(u);const l=u.getBoundingClientRect();l.bottom>i.innerHeight-4&&(u.style.top=`${Math.max(4,e.y-l.height)}px`),l.right>i.innerWidth-4&&(u.style.left=`${Math.max(4,i.innerWidth-l.width-4)}px`);let c=!1;function m(){c||(c=!0,u.remove(),o.removeEventListener("pointerdown",D,!0),i.removeEventListener("blur",m),g.get(o)===m&&g.delete(o))}function D(p){u.contains(p.target)||m()}return o.addEventListener("pointerdown",D,!0),i.addEventListener("blur",m),V(u,t,m),g.set(o,m),m}
1
+ import{isYieldEligibleDie as P}from"../core/dies.js";import{testValue as k}from"../core/dieTable.js";import{isParametricTest as C}from"../renderer/buildWaferMap.js";import{buildSweepData as x}from"../stats/sweep.js";import{plotToSweep as L}from"../stats/plotSpec.js";import{renderHistogramPanel as O}from"./charts/histogram.js";import{renderCapabilityPanel as W}from"./charts/capability.js";import{openChartExpandModal as A,populationPhrase as D}from"./charts/chartShell.js";import{buildCheckMenuEl as F,createToolbarHelpers as V,getTooltip as H,menuLayerFor as j,wireMenuA11y as q,CLR as z,FONT as B,SPACE as M,openReparentedModal as _,controlStyle as K,wireControlHover as U}from"./toolbar.js";import{requireRingCount as Y}from"../core/ringCount.js";import{defaultPlot as G,defaultSweep as J,fieldCatalogue as Q,plotTitle as X,resolvePlot as Z}from"../stats/plotData.js";import{newPlotId as y}from"../stats/plotSpec.js";import{sourceFromPoints as ee,toPlotItems as S}from"./plotItems.js";const E=30,g=e=>e.items.flatMap(t=>t.dies);function te(e){return e.items.map(t=>({...t,dies:t.dies.filter(n=>P(n))}))}function N(e,t,n){return(e.testDefs??[]).filter(C).map(l=>l.testNumber).filter(l=>{let o=0;for(const s of t){const u=k(s,l);if(u!==void 0&&Number.isFinite(u)&&++o>=n)return!0}return!1})}function $(e,t,n,l,o,s){const u=e.ownerDocument,a=u.createElement("div"),f=u.createElement("span");f.textContent=n,f.dataset.wmapPopulation="1",Object.assign(a.style,{color:z.label,fontSize:B.body,marginBottom:M.sm,display:"flex",alignItems:"center",gap:M.lg,flexWrap:"wrap"}),a.appendChild(f);let h=null;if(s){const c=u.createElement("button");c.type="button",c.textContent=s.label,c.dataset[s.hook]="1",Object.assign(c.style,K("outlined")),U(c),c.addEventListener("click",()=>s.run(()=>h?.close())),a.appendChild(c)}const i=e.querySelector("[data-wmap-chart-expand]")?.parentElement;i?.parentElement===e?i.after(a):e.prepend(a),h=A(e,t,{anchor:l,onClosed:o})}function ne(e,t){const n=g(t);return t.notMeasuredReason?t.notMeasuredReason:n.some(o=>P(o))?x(n,t.testDefs,e).series.some(o=>o.points.some(s=>s.count>0))?null:"These dies have no values for this sweep\u2019s tests":"These dies are all partial or edge-excluded, which charts leave out"}function oe(e,t,n){const l=te(e),o=g(e).length,s=l.flatMap(r=>r.dies),u=D(s.length,o,e.population),a=(e.testDefs??[]).filter(C),f=n.ownerDocument,h=(r,p)=>e.notMeasuredReason?e.notMeasuredReason:s.length===0?"These dies are all partial or edge-excluded, which charts leave out":N(e,s,r).length>0?null:p,i={section:"Distributions",label:"Value histogram",unavailable:h(1,"These dies have no parametric test values"),open:()=>{let r=e.activeTest??a[0]?.testNumber;const p=O({title:"Value histogram",items:l,testDefs:a,selectedTestNumber:e.activeTest,onTestChange:m=>{r=m},onSaveImage:t.onSaveImage,ownerDocument:f}),d=I(e,t,n),b=d&&r!==void 0?{label:"Edit as new plot",hook:"wmapEditAsPlot",run:m=>{const w=a.find(v=>v.testNumber===r);m(),d({id:y(),chart:"histogram",fields:{y:{test:r,...w?.name?{name:w.name}:{}},color:{none:!0}}},!0)()}}:void 0;$(p.card,`Value histogram \u2014 ${u}`,`Population: ${u}`,n,()=>p.destroy(),b)}},c={section:"Distributions",label:"Process capability",unavailable:h(2,"Needs at least 2 of these dies with values for one test"),open:()=>{const r=W({title:"Process capability",items:l,testDefs:a,selectedTestNumber:e.activeTest,onSaveImage:t.onSaveImage,ownerDocument:f}),p=s.length<E?` \xB7 fewer than ${E} dies, so each Ppk is a rough estimate`:"";$(r.card,`Process capability \u2014 ${u}`,`Population: ${u}${p}`,n,()=>r.destroy())}};return[i,c]}function I(e,t,n){const l=t.plots;if(!l)return null;const o=n.ownerDocument,s=S(e.items),u={testDefs:e.testDefs},a=g(e),f=D(a.length,a.length,e.population),h=D(a.filter(i=>P(i)).length,a.length,e.population);return(i,c)=>()=>{const r=i.chart==="sweep",p=r?h:f;import("./plotModal.js").then(({openPlotEditor:d})=>d({doc:o,anchor:n,store:l,plot:i,draft:c,items:s,ctx:u,population:p,rawPopulation:e.population,title:`${c?r?"New sweep":"New plot":r?"Sweep":"Plot"} \u2014 ${p}`,chart:{onSaveImage:t.onSaveImage,onSelectPoints:(b,m,w)=>{const v=ee(b,s,e.testDefs);v&&openDrilldownMenu(m,w,v,t)}}}))}}function se(e,t,n){const l=t.plots,o=I(e,t,n);if(!l||!o)return[];const s=S(e.items),u={testDefs:e.testDefs},a=g(e),f=e.notMeasuredReason??(a.length===0?"Nothing is selected":null),h=l.get().map(p=>{const d=L(p);if(d)return{section:"Plots",label:d.title,unavailable:ne(d,e),open:o(p,!1)};const b=Z(p,s,u);return{section:"Plots",label:X(b)||"Plot",unavailable:f??b.issues[0]??(b.plotted===0?"No die here has the values this plot needs":null),open:o(p,!1)}}),i=Q(s,u),c=i.some(p=>p.group==="Tests");h.push({section:"Plots",label:"New plot\u2026",unavailable:f??(c?null:"These dies have no parametric test values"),open:o(G(i,y()),!0)});const r=(e.testDefs??[]).filter(p=>C(p));return h.push({section:"Plots",label:"New sweep\u2026",unavailable:f??(r.length>0?null:"These dies have no parametric test values"),open:o(J(e.testDefs??[],y()),!0)}),h}function le(e,t,n){const l=g(e),o=e.notMeasuredReason??(l.length===0?"Nothing is selected":null),s=l.length,u=D(s,s,e.population),a=(f,h)=>()=>{import("./dataTab.js").then(({renderSelectionTables:i})=>{const c=n.ownerDocument;let r;const p=i({doc:c,ringCount:Y(e.items[0],"the drilldown tables"),items:e.items,testDefs:e.testDefs,population:e.population,view:f,onSaveText:t.onSaveText,onLocateDie:t.onLocateDie?(b,m)=>{r?.(),t.onLocateDie(b,m)}:void 0}),d=_([p.el],{title:`${h} \u2014 ${u}`,anchor:n,ownerDocument:c,boxSize:{width:"min(96vw, 1200px)",height:"min(92vh, 780px)"},onClosed:p.destroy});d?r=()=>d.close():p.destroy()})};return[{section:"Tables",label:"Dies",unavailable:o,open:a("dies","Dies")},{section:"Tables",label:"Test statistics",unavailable:o??(N(e,l,1).length>0?null:"These dies have no parametric test values"),open:a("statistics","Test statistics")},...e.items.length>1?[{section:"Tables",label:"Wafers",unavailable:o,open:a("wafers","Wafers")}]:[]]}function ae(e,t,n){return[...oe(e,t,n),...se(e,t,n),...le(e,t,n)]}const T=new WeakMap;export function openDrilldownMenu(e,t,n,l){const o=t.ownerDocument,s=o.defaultView??window;T.get(o)?.();const u=ae(n,l,t),a=[];if(l.narrower){const{label:m,source:w,ctx:v}=l.narrower,R={label:`${l.narrower.backLabel??"All selected"} \u2014 ${D(g(n).length,g(n).length,n.population)}`,source:n,ctx:l};a.push({section:"Dies"}),a.push({label:m,active:!1,action:!0,enabled:!0,onClick:()=>{d(),openDrilldownMenu(e,t,w,{...v??l,narrower:R})}})}let f;for(const m of u)m.section!==f&&(a.push({section:m.section}),f=m.section),a.push({label:m.label,active:!1,action:!0,enabled:m.unavailable===null,disabledHint:m.unavailable??void 0,onClick:()=>{d(),m.open()}});const{makeMenuSection:h}=V(H(o)),i=F(new DOMRect(e.x,e.y-4,0,0),a,{makeMenuSection:h},s),c=g(n).length;i.setAttribute("role","menu"),i.setAttribute("aria-label",`Open a chart or table of ${D(c,c,n.population)}`),i.dataset.wmapDrilldownMenu="1",j(t).appendChild(i);const r=i.getBoundingClientRect();r.bottom>s.innerHeight-4&&(i.style.top=`${Math.max(4,e.y-r.height)}px`),r.right>s.innerWidth-4&&(i.style.left=`${Math.max(4,s.innerWidth-r.width-4)}px`);let p=!1;function d(){p||(p=!0,i.remove(),o.removeEventListener("pointerdown",b,!0),s.removeEventListener("blur",d),T.get(o)===d&&T.delete(o))}function b(m){i.contains(m.target)||d()}return o.addEventListener("pointerdown",b,!0),s.addEventListener("blur",d),q(i,t,d),T.set(o,d),d}
@@ -0,0 +1,96 @@
1
+ import { type OverlayHandle } from './toolbar.js';
2
+ type GuideApi = {
3
+ buildWaferMap: unknown;
4
+ renderWaferMap: unknown;
5
+ renderWaferGallery: unknown;
6
+ analyzeWaferMap: unknown;
7
+ };
8
+ /**
9
+ * Host-supplied content inserted into wmap's own embedded user-guide window,
10
+ * so a host app's own documentation and wmap's reference live behind one
11
+ * help button instead of two separate ones.
12
+ */
13
+ export interface UserGuideExtension {
14
+ /**
15
+ * Host-provided HTML, inserted before wmap's own guide content. Static
16
+ * content only — must not contain `<script>` tags. wmap re-executes
17
+ * exactly one inline script (its own live-demo bootstrap, always the
18
+ * last element in `html`) by finding the content div's first
19
+ * `<script>` in document order; a `<script>` in this HTML would be
20
+ * found instead, silently breaking wmap's own live demos.
21
+ */
22
+ html: string;
23
+ /**
24
+ * Overrides the window's title text (default `'Wafer Map — User Guide'`).
25
+ * Use this when the host's own content should frame the whole document —
26
+ * wmap's own `<h1>` stays as-is further down the page, so the result
27
+ * reads as one combined guide with the host's section first, not a title
28
+ * rewrite.
29
+ */
30
+ title?: string;
31
+ }
32
+ /**
33
+ * Open the embedded user-guide window and populate its live demos. Closes any
34
+ * previously open guide window first (at most one may be open at a time).
35
+ * `api` must contain the four library functions the guide demos call at runtime.
36
+ * `html` is USER_GUIDE_HTML — passed in so toolbar.ts has no dependency on userGuideHtml.ts.
37
+ * `extension`, when provided, prepends host content and/or overrides the window title — see `UserGuideExtension`.
38
+ *
39
+ * Opens a real, separate window when available (draggable outside the host
40
+ * window's own bounds), falling back to the in-page floating window when
41
+ * `window.open` is blocked/unavailable — silently returns `null` in some
42
+ * embedded WebViews (Tauri, Electron, WebView2), same gap `openDetachWindow`
43
+ * exists for. Unlike gallery card detach, this doesn't go through
44
+ * `setDetachWindowOpener` — that opener's contract is scoped to per-wafer
45
+ * detach windows a host may have built specifically for that shape of
46
+ * content, and reusing it silently for the guide would broaden it without
47
+ * documentation.
48
+ */
49
+ export declare function openUserGuideWindow(api: GuideApi, html: string, extension?: UserGuideExtension, anchor?: Element): void;
50
+ /**
51
+ * Opens wmap's own guide window with no live `WaferMapController`/
52
+ * `GalleryController` required — `WaferMapController.openUserGuide()`/
53
+ * `GalleryController.openUserGuide()` are thin wrappers around this exact
54
+ * same call (see their own `openGuideWindow` closures in `renderWaferMap.ts`/
55
+ * `renderWaferGallery.ts`), built from the library's own top-level functions
56
+ * rather than anything derived from a specific render — so a host with
57
+ * nothing rendered yet (e.g. an empty-state "Help" menu, before any file is
58
+ * loaded) can reach the same guide, with the same `extension` content, the
59
+ * same way. Prefer the controller method when one exists — this is for the
60
+ * gap it can't cover.
61
+ *
62
+ * `renderWaferMap`/`renderWaferGallery` are dynamically imported (like
63
+ * `USER_GUIDE_HTML` below) since `toolbar.ts` is itself imported BY both of
64
+ * them — a static import here would cycle back.
65
+ */
66
+ export declare function openWaferMapGuide(extension?: UserGuideExtension, anchor?: Element): void;
67
+ /**
68
+ * Opens report HTML (from `renderSummaryReportHtml`/`renderLotSummaryReportHtml`)
69
+ * in an in-app modal — no host-registered `setReportOpener` required just to
70
+ * *view* a report. `setReportOpener`/`openHtmlReport` remain available as an
71
+ * "Open as full page" action inside this modal (its header's print button, plus
72
+ * a link in the toolbar strip below it), for a host that wants the report as a
73
+ * real separate page — to keep it open outside this modal, or as a fallback if
74
+ * in-app printing misbehaves on a given platform.
75
+ *
76
+ * The report HTML is a complete standalone document (its own `<style>`, its own
77
+ * `<!DOCTYPE html>`) — rendered via `<iframe srcdoc>` rather than parsed apart
78
+ * and inserted into the modal's own DOM (the way the guide's `buildGuideContent`
79
+ * does it). This keeps the report's stylesheet naturally isolated from the host
80
+ * page with no parsing, and lets `iframe.contentWindow.print()` scope printing
81
+ * to just the report — `openOverlay`'s sibling-hiding print stylesheet (built
82
+ * for content that lives directly in the host document, like the guide) isn't
83
+ * needed here and wouldn't reach into the iframe's own document anyway.
84
+ */
85
+ export declare function openReportModal(html: string, opts?: {
86
+ anchor?: Element;
87
+ ownerDocument?: Document;
88
+ /**
89
+ * A report built with `live` tells this page which finding a click on its row meant. Called with that
90
+ * finding's id and the modal's handle, so the host can close the modal and show the finding on the map.
91
+ * Only messages from this report's own frame are heeded.
92
+ */
93
+ onFinding?: (findingId: string, handle: OverlayHandle) => void;
94
+ }): OverlayHandle;
95
+ export {};
96
+ //# sourceMappingURL=guideWindow.d.ts.map
@@ -0,0 +1 @@
1
+ import{openHtmlReport as N}from"../stats/renderFindingsReport.js";import{buildWaferMap as G}from"../renderer/buildWaferMap.js";import{analyzeWaferMap as M}from"../stats/analyzeWaferMap.js";import{CLR as f,FONT as $,RADIUS as k,MOTION as O,wireControlHover as H,copyWmapThemeTokens as B,syncWmapPopupTheme as F,openModal as R,openFloatingWindow as z,wireExpandToggle as P}from"./toolbar.js";let y=null;const S=40,j=S+8;function U(e,i){const s=e.createElement("div");s.classList.add("wmap-guide-search"),s.style.cssText="display:flex;align-items:center;gap:2px;margin-left:10px;flex:1;min-width:80px;max-width:260px;";const t=e.createElement("input");t.type="text",t.placeholder="Search guide\u2026",t.setAttribute("aria-label","Search guide"),t.style.cssText=`flex:1;min-width:0;padding:4px 8px;font-size:12px;background:${f.menuBg};color:${f.text};border:1px solid ${f.menuBorder};border-radius:${k.control};outline:none;`,t.addEventListener("focus",()=>{t.style.borderColor=f.iconActive}),t.addEventListener("blur",()=>{t.style.borderColor=f.menuBorder});const n=e.createElement("span");n.setAttribute("role","status"),n.setAttribute("aria-live","polite"),n.style.cssText=`font-size:12px;color:${f.label};min-width:44px;text-align:center;flex-shrink:0;white-space:nowrap;`;function r(u,m){const d=e.createElement("button");return d.type="button",d.textContent=u,d.setAttribute("aria-label",m),d.disabled=!0,d.style.cssText=`border:none;background:none;cursor:pointer;padding:4px 6px;border-radius:${k.control};font-size:12px;color:${f.label};flex-shrink:0;opacity:0.4;`,d.addEventListener("mouseenter",()=>{d.disabled||(d.style.background=f.bgHover)}),d.addEventListener("mouseleave",()=>{d.style.background="none"}),d}const a=r("\u25B2","Previous match"),o=r("\u25BC","Next match");let l=[],c=-1;function p(){const u=l.length===0;a.disabled=u,o.disabled=u,a.style.opacity=u?"0.4":"",o.style.opacity=u?"0.4":""}function g(){for(const u of l)u.parentNode?.replaceChild(e.createTextNode(u.textContent??""),u);i.normalize(),l=[],c=-1}function h(u){if(l.length===0)return;c>=0&&(l[c].style.outline="none"),c=(u%l.length+l.length)%l.length;const m=l[c];m.style.outline=`2px solid ${f.iconActive}`,m.style.outlineOffset="1px",m.scrollIntoView({behavior:"smooth",block:"center"}),n.textContent=`${c+1}/${l.length}`}function w(u){g();const m=u.trim();if(!m){n.textContent="",p();return}const d=m.toLowerCase(),x=e.createTreeWalker(i,NodeFilter.SHOW_TEXT,{acceptNode(C){const b=C.parentElement;return!b||b.closest("script,style,.wmap-guide-toc")?NodeFilter.FILTER_REJECT:NodeFilter.FILTER_ACCEPT}}),T=[];let _;for(;_=x.nextNode();)T.push(_);for(const C of T){const b=C.textContent??"",A=b.toLowerCase();if(!A.includes(d))continue;const L=e.createDocumentFragment();let E=0,v=A.indexOf(d);for(;v!==-1;){v>E&&L.appendChild(e.createTextNode(b.slice(E,v)));const D=e.createElement("mark");D.textContent=b.slice(v,v+m.length),L.appendChild(D),l.push(D),E=v+m.length,v=A.indexOf(d,E)}E<b.length&&L.appendChild(e.createTextNode(b.slice(E))),C.parentNode?.replaceChild(L,C)}p(),l.length===0?n.textContent="No matches":h(0)}return t.addEventListener("input",()=>w(t.value)),t.addEventListener("keydown",u=>{u.key==="Enter"?(u.preventDefault(),h(c+(u.shiftKey?-1:1))):u.key==="Escape"&&(t.value?(t.value="",w("")):t.blur())}),a.addEventListener("click",()=>h(c-1)),o.addEventListener("click",()=>h(c+1)),s.append(t,n,a,o),s}function q(e,i,s){const t=[...i.querySelectorAll("h2[id]")];if(t.length<2)return;for(const m of i.querySelectorAll("h1[id],h2[id],h3[id],h4[id]"))m.style.scrollMarginTop=`${j}px`;const n=e.createElement("div");n.classList.add("wmap-guide-toc"),n.style.cssText=`position:sticky;top:0;z-index:2;background:${f.menuBg};border-bottom:1px solid ${f.menuBorder};font-family:system-ui,-apple-system,"Segoe UI",sans-serif;`;const r=e.createElement("div");r.style.cssText=`display:flex;align-items:center;gap:4px;height:${S}px;padding:0 12px;box-sizing:border-box;`;const a=e.createElement("span");a.innerHTML='<svg viewBox="0 0 24 24" width="12" height="12" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m6 9 6 6 6-6"/></svg>',a.style.cssText=`display:inline-flex;transition:transform ${O.base};flex-shrink:0;`;const o=e.createElement("button");o.type="button";const l="wmap-guide-toc-panel";o.setAttribute("aria-expanded","false"),o.setAttribute("aria-controls",l),o.style.cssText=`display:flex;align-items:center;gap:6px;border:none;background:none;cursor:pointer;padding:6px 8px;margin:0 -8px 0 -8px;border-radius:${k.control};font-size:13px;font-weight:600;color:${f.text};`;const c=e.createElement("span");c.textContent=`Contents (${t.length})`,o.append(a,c),o.addEventListener("mouseenter",()=>{o.style.background=f.bgHover}),o.addEventListener("mouseleave",()=>{o.style.background="none"});const p=e.createElement("button");p.type="button",p.textContent="\u2191 Top",p.style.cssText=`margin-left:auto;border:none;background:none;cursor:pointer;padding:6px 8px;border-radius:${k.control};font-size:12px;color:${f.label};`,p.addEventListener("mouseenter",()=>{p.style.background=f.bgHover}),p.addEventListener("mouseleave",()=>{p.style.background="none"}),p.addEventListener("click",()=>{u.close(),i.scrollTo({top:0,behavior:"smooth"})}),s?r.append(o,U(e,i),p):r.append(o,p);const g=e.createElement("div");g.id=l,g.hidden=!0,g.style.cssText="padding:2px 12px 12px;max-height:min(60vh,440px);overflow-y:auto;";const h=/^\s*\d+(?:\.\d+)*\.\s+/;for(const m of i.querySelectorAll("h2[id],h3[id],h4[id]")){const d=m.firstChild;d?.nodeType===Node.TEXT_NODE&&(d.textContent=(d.textContent??"").replace(h,""))}const w=e.createElement("ul");w.setAttribute("role","list"),w.style.cssText="list-style:none;margin:0;padding:0;columns:200px;column-gap:24px;";for(const m of t){const d=e.createElement("li");d.style.cssText="break-inside:avoid;";const x=e.createElement("a");x.href=`#${m.id}`,x.textContent=m.textContent??"",x.style.cssText=`display:block;padding:4px 6px;margin:0 -6px;border-radius:${k.control};font-size:12.5px;color:${f.iconActive};text-decoration:none;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;`,x.addEventListener("mouseenter",()=>{x.style.background=f.bgHover}),x.addEventListener("mouseleave",()=>{x.style.background="none"}),x.addEventListener("click",T=>{T.preventDefault(),u.close(),m.scrollIntoView({behavior:"smooth",block:"start"})}),d.appendChild(x),w.appendChild(d)}g.appendChild(w);const u=P(o,m=>{g.hidden=!m,o.setAttribute("aria-expanded",String(m)),a.style.transform=m?"rotate(180deg)":"none"});n.append(r,g),i.prepend(n)}function W(e,i,s,t,n){const r=i,a=r.__wmapDemoApi;r.__wmapDemoApi=t;const o=e.createElement("div");return o.classList.add("wmap-guide-content"),o.innerHTML=s,q(e,o,n),{content:o,restoreApi:()=>{r.__wmapDemoApi===t&&(r.__wmapDemoApi=a)}}}function I(e,i){const s=i,t=e.querySelector("script");if(t){const n=e.ownerDocument.createElement("script");n.textContent=t.textContent,e.appendChild(n);const r=e.querySelector(".wmap-guide");return r&&s.__wmapPopulateGuideDemos?.(r),s.__wmapDestroyGuideDemos}}function V(e,i,s,t){const n=t?.ownerDocument??document,r=n.defaultView??window,{content:a,restoreApi:o}=W(n,r,i,s,!0);Object.assign(a.style,{flex:"1",overflow:"auto",minHeight:"0",minWidth:"0"});let l;const c=z({title:e,anchor:t,ownerDocument:n,printable:!0,onMaximizeChange:p=>{a.style.setProperty("--wmap-guide-reading-width",p?"1000px":"720px")},onClose:()=>{y===c&&(y=null),l?.(),o()}});return c.contentWrap.appendChild(a),l=I(a,r),c}function X(e,i,s,t){const n=e.document;n.title=i,Object.assign(n.documentElement.style,{height:"100%"}),Object.assign(n.body.style,{margin:"0",height:"100vh",overflow:"hidden",display:"flex",flexDirection:"column",fontFamily:$.family}),B(document.documentElement,n.documentElement);const r=F(document.documentElement,n.documentElement),{content:a,restoreApi:o}=W(n,e,s,t,!1);Object.assign(a.style,{flex:"1",overflow:"auto",minHeight:"0",minWidth:"0"}),n.body.appendChild(a);const l=I(a,e);let c=!1;const p=()=>{if(!c){c=!0,clearInterval(g),r();try{l?.()}catch{}o(),y===h&&(y=null)}},g=setInterval(()=>{e.closed&&p()},400);e.addEventListener("pagehide",p);const h={close:()=>{e.closed||e.close(),p()}};return h}export function openUserGuideWindow(e,i,s,t){y&&(y.close(),y=null);const n=s?.title??"Wafer Map \u2014 User Guide",r=(s?.html??"")+i,o=(t?.ownerDocument.defaultView??window).open("","_blank","width=800,height=820");y=o?X(o,n,r,e):V(n,r,e,t)}export function openWaferMapGuide(e,i){Promise.all([import("./userGuideHtml.js"),import("./renderWaferMap.js"),import("./renderWaferGallery.js")]).then(([s,t,n])=>{openUserGuideWindow({buildWaferMap:G,renderWaferMap:t.renderWaferMap,renderWaferGallery:n.renderWaferGallery,analyzeWaferMap:M},s.USER_GUIDE_HTML,e,i)})}export function openReportModal(e,i){const s=i?.ownerDocument??document,t=s.createElement("iframe");t.srcdoc=e,t.title="Summary report",Object.assign(t.style,{border:"0",width:"100%",flex:"1",minHeight:"0",background:"#fff"});const n=s.createElement("div");Object.assign(n.style,{display:"flex",justifyContent:"flex-end",padding:"6px 14px",borderBottom:`1px solid ${f.menuBorder}`,flexShrink:"0"});const r=s.createElement("button");r.type="button",H(r,"bare"),r.textContent="Open as full page \u2197",Object.assign(r.style,{border:"none",background:"none",color:f.iconActive,cursor:"pointer",fontSize:$.body,textDecoration:"underline",padding:"0"}),r.addEventListener("click",()=>N(e)),n.appendChild(r);const a=s.defaultView??window,o=c=>{if(c.source!==t.contentWindow)return;const p=c.data?.wmapFinding;typeof p=="string"&&i?.onFinding?.(p,l)};i?.onFinding&&a.addEventListener("message",o);const l=R({title:"Summary report",anchor:i?.anchor,ownerDocument:s,printable:()=>t.contentWindow?.print(),onClose:()=>a.removeEventListener("message",o)});return l.contentWrap.style.flexDirection="column",l.contentWrap.appendChild(n),l.contentWrap.appendChild(t),l}
@@ -41,6 +41,22 @@ export interface IdentityHeaderController {
41
41
  collapse(): void;
42
42
  destroy(): void;
43
43
  }
44
+ /**
45
+ * The overlay that holds a wafer's full metadata while its identity header is
46
+ * expanded: absolutely positioned over the top of the nearest `position:
47
+ * relative` ancestor, `display: none` until opened. Shared by the single-wafer
48
+ * header and the gallery's per-wafer views.
49
+ */
50
+ export declare function createMetaPanelEl(doc: Document): HTMLDivElement;
51
+ /**
52
+ * Make `toggleEl` (a label row the caller has already built) expand into the
53
+ * wafer's full metadata: appends the chevron, the hover, the ARIA state and
54
+ * the click/keyboard toggle, and returns the panel for the caller to mount in
55
+ * a `position: relative` ancestor. Returns `null`, touching nothing, when the
56
+ * wafer has no metadata to show. The fixed-label counterpart of
57
+ * `createIdentityHeader`, for views whose label never changes.
58
+ */
59
+ export declare function attachExpandableMetadata(doc: Document, toggleEl: HTMLElement, label: string, metadata: WaferMetadata | null | undefined): HTMLDivElement | null;
44
60
  export declare function collapsedLabel(meta: WaferMetadata, lotStack: IdentityHeaderLotStack | undefined): string | undefined;
45
61
  export declare function createIdentityHeader(label: string, metadata: WaferMetadata | null | undefined, opts?: IdentityHeaderOptions): IdentityHeaderController;
46
62
  //# sourceMappingURL=identityHeader.d.ts.map
@@ -1 +1 @@
1
- import{metadataEntries as C,buildCompactMetadataRows as W}from"./summaryPanel.js";import{prettyKey as A}from"../stats/facets.js";import{SHADOW as L,LEADING as k,SPACE as w,FONT as x,CLR as E,Z_ABOVE as M,wireExpandToggle as j}from"./toolbar.js";export function collapsedLabel(r,a){if(a)return a.aggrMethod?`${a.lotSize} wafers \xB7 ${a.aggrMethod}`:`${a.lotSize} wafers`;if(r.lot&&r.waferId!==void 0&&r.waferId!==""){const s=String(r.lot),t=String(r.waferId);return t.includes(s)?t:`${s} \xB7 ${t}`}for(const s of["lot","waferId","product","testProgram"]){const t=r[s];if(t!=null&&t!=="")return String(t)}}export function createIdentityHeader(r,a,s={}){let t=r,b=a??{},o=s.lotStack,i=!1,m=!1;const c=s.ownerDocument??document,e=c.createElement("div");Object.assign(e.style,{display:"flex",alignItems:"center",gap:w.xs,minWidth:"0"});const u=c.createElement("span");Object.assign(u.style,{fontWeight:"700",fontSize:x.sub,overflow:"hidden",textOverflow:"ellipsis",whiteSpace:"nowrap"}),e.appendChild(u);const y=c.createElement("span");Object.assign(y.style,{fontSize:x.body,lineHeight:k.none,color:E.label,flexShrink:"0",display:"none"}),e.appendChild(y);const $=4,d=c.createElement("span");Object.assign(d.style,{fontSize:x.body,color:E.label,whiteSpace:"nowrap",overflow:"hidden",flexShrink:"0",display:"none",marginLeft:w.sm}),e.appendChild(d);const l=c.createElement("div");l.dataset.wmapMetaPanel="1",Object.assign(l.style,{position:"absolute",top:"0",left:"0",right:"0",zIndex:M,background:E.menuBg,borderBottom:`1px solid ${E.menuBorder}`,boxShadow:L.menu,padding:`${w.md} ${w.lg}`,fontSize:x.body,display:"none"});function S(){return C(b).length>0||!!o}function O(){return o?[]:C(b).filter(([,n])=>!t.includes(n))}function z(){return e.clientWidth?u.scrollWidth>u.clientWidth||d.scrollWidth>d.clientWidth||e.scrollWidth>e.clientWidth:!0}function g(){u.textContent=t;const n=O();d.textContent=n.map(([p,h])=>`${A(p)}: ${h}`).join(" \xB7 "),d.style.display=n.length>0?"":"none",m=n.length>0&&n.length<=$&&!z(),d.style.display=m?"":"none";const f=S()&&!m;if(y.style.display=f?"":"none",e.style.cursor=f?"pointer":"",f||(i=!1),y.textContent=i?"\u25B4":"\u25BE",l.style.display=i?"block":"none",f?(e.setAttribute("aria-expanded",String(i)),e.setAttribute("aria-label",`Wafer info: ${t||"this wafer"}. ${i?"Click to collapse.":"Click to expand."}`)):(e.removeAttribute("aria-expanded"),e.removeAttribute("aria-label")),l.innerHTML="",i){if(o){const h=c.createElement("div");Object.assign(h.style,{fontWeight:"600",marginBottom:"3px"}),h.textContent=o.aggrMethod?`${o.lotSize} wafers stacked \xB7 ${o.aggrMethod}`:`${o.lotSize} wafers stacked`,l.appendChild(h)}const p=W(b);p&&l.appendChild(p)}}const I=j(e,n=>{!S()||m||(i=n,g())});g();const v=typeof ResizeObserver>"u"?null:new ResizeObserver(()=>g());return v?.observe(e),{wrap:e,metaPanel:l,update(n,f,p){t=n,b=f??{},o=p,g()},isEmpty(){return!t&&!S()},collapse(){i&&(i=!1,g())},destroy(){v?.disconnect(),I.destroy(),e.remove(),l.remove()}}}
1
+ import{metadataEntries as v,buildCompactMetadataRows as $}from"./summaryPanel.js";import{prettyKey as M}from"../stats/facets.js";import{SHADOW as j,LEADING as E,SPACE as C,FONT as g,CLR as x,RADIUS as L,Z_ABOVE as H,wireExpandToggle as z,wireControlHover as P}from"./toolbar.js";export function createMetaPanelEl(i){const e=i.createElement("div");return Object.assign(e.style,{position:"absolute",top:"0",left:"0",right:"0",zIndex:H,background:x.menuBg,borderBottom:`1px solid ${x.menuBorder}`,boxShadow:j.menu,padding:`${C.md} ${C.lg}`,fontSize:g.body,display:"none"}),e}export function attachExpandableMetadata(i,e,s,t){const d=t??{};if(v(d).length===0)return null;const a=i.createElement("span");Object.assign(a.style,{fontSize:g.body,lineHeight:E.none,color:x.label,flexShrink:"0"}),a.textContent="\u25BE",e.appendChild(a),Object.assign(e.style,{cursor:"pointer",borderRadius:L.control}),P(e,"bare");const r=createMetaPanelEl(i);r.dataset.wmapCardMetaPanel="1";const c=$(d);return c&&r.appendChild(c),e.setAttribute("aria-expanded","false"),e.setAttribute("aria-label",`Wafer info for ${s||"this card"}. Click to expand.`),z(e,o=>{a.textContent=o?"\u25B4":"\u25BE",e.setAttribute("aria-expanded",String(o)),r.style.display=o?"block":"none"}),r}export function collapsedLabel(i,e){if(e)return e.aggrMethod?`${e.lotSize} wafers \xB7 ${e.aggrMethod}`:`${e.lotSize} wafers`;if(i.lot&&i.waferId!==void 0&&i.waferId!==""){const s=String(i.lot),t=String(i.waferId);return t.includes(s)?t:`${s} \xB7 ${t}`}for(const s of["lot","waferId","product","testProgram"]){const t=i[s];if(t!=null&&t!=="")return String(t)}}export function createIdentityHeader(i,e,s={}){let t=i,d=e??{},a=s.lotStack,r=!1,c=!1;const o=s.ownerDocument??document,n=o.createElement("div");Object.assign(n.style,{display:"flex",alignItems:"center",gap:C.xs,minWidth:"0"});const h=o.createElement("span");Object.assign(h.style,{fontWeight:"700",fontSize:g.sub,overflow:"hidden",textOverflow:"ellipsis",whiteSpace:"nowrap"}),n.appendChild(h);const w=o.createElement("span");Object.assign(w.style,{fontSize:g.body,lineHeight:E.none,color:x.label,flexShrink:"0",display:"none"}),n.appendChild(w);const A=4,p=o.createElement("span");Object.assign(p.style,{fontSize:g.body,color:x.label,whiteSpace:"nowrap",overflow:"hidden",flexShrink:"0",display:"none",marginLeft:C.sm}),n.appendChild(p);const f=createMetaPanelEl(o);f.dataset.wmapMetaPanel="1";function S(){return v(d).length>0||!!a}function I(){return a?[]:v(d).filter(([,l])=>!t.includes(l))}function W(){return n.clientWidth?h.scrollWidth>h.clientWidth||p.scrollWidth>p.clientWidth||n.scrollWidth>n.clientWidth:!0}function m(){h.textContent=t;const l=I();p.textContent=l.map(([b,y])=>`${M(b)}: ${y}`).join(" \xB7 "),p.style.display=l.length>0?"":"none",c=l.length>0&&l.length<=A&&!W(),p.style.display=c?"":"none";const u=S()&&!c;if(w.style.display=u?"":"none",n.style.cursor=u?"pointer":"",u||(r=!1),w.textContent=r?"\u25B4":"\u25BE",f.style.display=r?"block":"none",u?(n.setAttribute("aria-expanded",String(r)),n.setAttribute("aria-label",`Wafer info: ${t||"this wafer"}. ${r?"Click to collapse.":"Click to expand."}`)):(n.removeAttribute("aria-expanded"),n.removeAttribute("aria-label")),f.innerHTML="",r){if(a){const y=o.createElement("div");Object.assign(y.style,{fontWeight:"600",marginBottom:"3px"}),y.textContent=a.aggrMethod?`${a.lotSize} wafers stacked \xB7 ${a.aggrMethod}`:`${a.lotSize} wafers stacked`,f.appendChild(y)}const b=$(d);b&&f.appendChild(b)}}const k=z(n,l=>{!S()||c||(r=l,m())});m();const O=typeof ResizeObserver>"u"?null:new ResizeObserver(()=>m());return O?.observe(n),{wrap:n,metaPanel:f,update(l,u,b){t=l,d=u??{},a=b,m()},isEmpty(){return!t&&!S()},collapse(){r&&(r=!1,m())},destroy(){O?.disconnect(),k.destroy(),n.remove(),f.remove()}}}
@@ -11,9 +11,9 @@ export type { WarningsOptions } from './warnings.js';
11
11
  export type { InsightsOptions, InsightsView } from './insightsTab.js';
12
12
  export { setDetachWindowOpener } from './toolbar.js';
13
13
  export type { DetachWindowOpener } from './toolbar.js';
14
- export type { UserGuideExtension } from './toolbar.js';
15
- export { openWaferMapGuide } from './toolbar.js';
14
+ export type { UserGuideExtension } from './guideWindow.js';
15
+ export { openWaferMapGuide } from './guideWindow.js';
16
16
  export { ICONS } from './icons.js';
17
17
  export type { OverlayHandle } from './toolbar.js';
18
- export { openReportModal } from './toolbar.js';
18
+ export { openReportModal } from './guideWindow.js';
19
19
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- export{WMAP_VERSION,WMAP_BUILD_TIME}from"./version.js";export{renderWaferMap}from"./renderWaferMap.js";export{renderWaferGallery}from"./renderWaferGallery.js";export{collectWarnings,severityOf}from"./warnings.js";export{setDetachWindowOpener}from"./toolbar.js";export{openWaferMapGuide}from"./toolbar.js";export{ICONS}from"./icons.js";export{openReportModal}from"./toolbar.js";
1
+ export{WMAP_VERSION,WMAP_BUILD_TIME}from"./version.js";export{renderWaferMap}from"./renderWaferMap.js";export{renderWaferGallery}from"./renderWaferGallery.js";export{collectWarnings,severityOf}from"./warnings.js";export{setDetachWindowOpener}from"./toolbar.js";export{openWaferMapGuide}from"./guideWindow.js";export{ICONS}from"./icons.js";export{openReportModal}from"./guideWindow.js";