@wafertools/wafermap 0.33.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.
- package/AGENTS.md +6 -6
- package/CHANGELOG.md +212 -0
- package/README.md +5 -5
- package/dist/packages/canvas-adapter/chartPopulation.d.ts +15 -10
- package/dist/packages/canvas-adapter/chartPopulation.js +1 -1
- package/dist/packages/canvas-adapter/charts/barPanel.d.ts +7 -0
- package/dist/packages/canvas-adapter/charts/barPanel.js +1 -1
- package/dist/packages/canvas-adapter/charts/binCluster.d.ts +7 -1
- package/dist/packages/canvas-adapter/charts/binCluster.js +1 -1
- package/dist/packages/canvas-adapter/charts/boxplot.js +1 -1
- package/dist/packages/canvas-adapter/charts/capability.js +1 -1
- package/dist/packages/canvas-adapter/charts/chartShell.d.ts +55 -3
- package/dist/packages/canvas-adapter/charts/chartShell.js +1 -1
- package/dist/packages/canvas-adapter/charts/correlation.js +1 -2
- package/dist/packages/canvas-adapter/charts/groupedBarPlot.d.ts +2 -0
- package/dist/packages/canvas-adapter/charts/groupedBarPlot.js +1 -1
- package/dist/packages/canvas-adapter/charts/histogram.d.ts +16 -0
- package/dist/packages/canvas-adapter/charts/histogram.js +1 -1
- package/dist/packages/canvas-adapter/charts/palette.d.ts +2 -0
- package/dist/packages/canvas-adapter/charts/palette.js +1 -1
- package/dist/packages/canvas-adapter/charts/plotChart.d.ts +27 -0
- package/dist/packages/canvas-adapter/charts/plotChart.js +1 -0
- package/dist/packages/canvas-adapter/charts/pointInteractions.d.ts +45 -0
- package/dist/packages/canvas-adapter/charts/pointInteractions.js +1 -0
- package/dist/packages/canvas-adapter/charts/regionYieldDiagram.d.ts +2 -0
- package/dist/packages/canvas-adapter/charts/regionYieldDiagram.js +1 -1
- package/dist/packages/canvas-adapter/charts/scatter.d.ts +19 -0
- package/dist/packages/canvas-adapter/charts/scatter.js +1 -1
- package/dist/packages/canvas-adapter/charts/sweep.d.ts +12 -0
- package/dist/packages/canvas-adapter/charts/sweep.js +1 -1
- package/dist/packages/canvas-adapter/charts/testPassRate.d.ts +7 -0
- package/dist/packages/canvas-adapter/charts/testPassRate.js +1 -1
- package/dist/packages/canvas-adapter/charts/trend.d.ts +11 -0
- package/dist/packages/canvas-adapter/charts/trend.js +1 -1
- package/dist/packages/canvas-adapter/confirmDialog.d.ts +13 -0
- package/dist/packages/canvas-adapter/confirmDialog.js +1 -0
- package/dist/packages/canvas-adapter/dataTab.d.ts +131 -0
- package/dist/packages/canvas-adapter/dataTab.js +1 -0
- package/dist/packages/canvas-adapter/dieList.d.ts +39 -4
- package/dist/packages/canvas-adapter/dieList.js +6 -7
- package/dist/packages/canvas-adapter/drilldown.d.ts +20 -3
- package/dist/packages/canvas-adapter/drilldown.js +1 -1
- package/dist/packages/canvas-adapter/guideWindow.d.ts +96 -0
- package/dist/packages/canvas-adapter/guideWindow.js +1 -0
- package/dist/packages/canvas-adapter/identityHeader.d.ts +16 -0
- package/dist/packages/canvas-adapter/identityHeader.js +1 -1
- package/dist/packages/canvas-adapter/index.d.ts +3 -3
- package/dist/packages/canvas-adapter/index.js +1 -1
- package/dist/packages/canvas-adapter/insightsTab.d.ts +73 -54
- package/dist/packages/canvas-adapter/insightsTab.js +1 -1
- package/dist/packages/canvas-adapter/maplessSummary.d.ts +2 -2
- package/dist/packages/canvas-adapter/plotEditor.d.ts +29 -0
- package/dist/packages/canvas-adapter/plotEditor.js +1 -0
- package/dist/packages/canvas-adapter/plotItems.d.ts +32 -0
- package/dist/packages/canvas-adapter/plotItems.js +1 -0
- package/dist/packages/canvas-adapter/plotModal.d.ts +30 -0
- package/dist/packages/canvas-adapter/plotModal.js +1 -0
- package/dist/packages/canvas-adapter/plotStore.d.ts +52 -0
- package/dist/packages/canvas-adapter/plotStore.js +1 -0
- package/dist/packages/canvas-adapter/plotTab.d.ts +30 -0
- package/dist/packages/canvas-adapter/plotTab.js +1 -0
- package/dist/packages/canvas-adapter/renderWaferGallery.d.ts +3 -2
- package/dist/packages/canvas-adapter/renderWaferGallery.js +1 -1
- package/dist/packages/canvas-adapter/renderWaferMap.d.ts +21 -2
- package/dist/packages/canvas-adapter/renderWaferMap.js +1 -1
- package/dist/packages/canvas-adapter/summaryPanel.d.ts +27 -8
- package/dist/packages/canvas-adapter/summaryPanel.js +2 -4
- package/dist/packages/canvas-adapter/summaryRail.d.ts +7 -0
- package/dist/packages/canvas-adapter/summaryRail.js +1 -0
- package/dist/packages/canvas-adapter/sweepEditor.d.ts +17 -0
- package/dist/packages/canvas-adapter/sweepEditor.js +1 -0
- package/dist/packages/canvas-adapter/tableExport.d.ts +22 -0
- package/dist/packages/canvas-adapter/tableExport.js +3 -0
- package/dist/packages/canvas-adapter/toolbar.d.ts +25 -97
- package/dist/packages/canvas-adapter/toolbar.js +2 -2
- package/dist/packages/canvas-adapter/userGuideHtml.d.ts +1 -1
- package/dist/packages/canvas-adapter/userGuideHtml.js +337 -42
- package/dist/packages/canvas-adapter/version.d.ts +2 -2
- package/dist/packages/canvas-adapter/version.js +1 -1
- package/dist/packages/canvas-adapter/viewSwitch.d.ts +13 -0
- package/dist/packages/canvas-adapter/viewSwitch.js +1 -0
- package/dist/packages/canvas-adapter/virtualTable.d.ts +44 -0
- package/dist/packages/canvas-adapter/virtualTable.js +1 -0
- package/dist/packages/core/classify.d.ts +3 -2
- package/dist/packages/core/classify.js +1 -1
- package/dist/packages/core/passBins.d.ts +17 -4
- package/dist/packages/core/passBins.js +1 -1
- package/dist/packages/core/ringCount.d.ts +22 -0
- package/dist/packages/core/ringCount.js +1 -0
- package/dist/packages/core/tableCsv.d.ts +43 -0
- package/dist/packages/core/tableCsv.js +3 -0
- package/dist/packages/renderer/binColors.d.ts +6 -6
- package/dist/packages/renderer/binColors.js +1 -1
- package/dist/packages/renderer/buildView.d.ts +1 -1
- package/dist/packages/renderer/buildView.js +1 -1
- package/dist/packages/renderer/buildWaferMap.js +1 -1
- package/dist/packages/stats/analyzeWaferLot.js +1 -1
- package/dist/packages/stats/analyzeWaferMap.js +1 -1
- package/dist/packages/stats/binPareto.d.ts +1 -1
- package/dist/packages/stats/binPareto.js +1 -1
- package/dist/packages/stats/histogram.d.ts +27 -0
- package/dist/packages/stats/histogram.js +1 -1
- package/dist/packages/stats/index.d.ts +2 -0
- package/dist/packages/stats/index.js +1 -1
- package/dist/packages/stats/lotDrift.js +1 -1
- package/dist/packages/stats/lotPattern.js +1 -1
- package/dist/packages/stats/math.d.ts +9 -6
- package/dist/packages/stats/math.js +1 -1
- package/dist/packages/stats/patternClassification.d.ts +1 -1
- package/dist/packages/stats/patternClassification.js +1 -1
- package/dist/packages/stats/plotData.d.ts +258 -0
- package/dist/packages/stats/plotData.js +1 -0
- package/dist/packages/stats/plotId.d.ts +3 -0
- package/dist/packages/stats/plotId.js +1 -0
- package/dist/packages/stats/plotSpec.d.ts +119 -0
- package/dist/packages/stats/plotSpec.js +4 -0
- package/dist/packages/stats/presentation.d.ts +2 -0
- package/dist/packages/stats/presentation.js +1 -1
- package/dist/packages/stats/regions.d.ts +6 -0
- package/dist/packages/stats/regions.js +1 -1
- package/dist/packages/stats/renderSummaryReport.d.ts +8 -3
- package/dist/packages/stats/renderSummaryReport.js +15 -15
- package/dist/packages/stats/reportHtml.d.ts +1 -1
- package/dist/packages/stats/reportHtml.js +36 -29
- package/dist/packages/stats/scatter.d.ts +6 -0
- package/dist/packages/stats/scatter.js +1 -1
- package/dist/packages/stats/sweep.d.ts +37 -1
- package/dist/packages/stats/sweep.js +1 -1
- package/dist/packages/stats/sweepText.d.ts +14 -0
- package/dist/packages/stats/sweepText.js +1 -0
- package/dist/packages/stats/sweepXFromName.d.ts +14 -0
- package/dist/packages/stats/sweepXFromName.js +1 -1
- package/dist/packages/stats/synthesis.d.ts +14 -4
- package/dist/packages/stats/synthesis.js +1 -1
- package/dist/packages/stats/testPassRate.d.ts +6 -0
- package/dist/packages/stats/testPassRate.js +1 -1
- package/dist/packages/stats/trend.js +1 -1
- package/dist/packages/stats/types.d.ts +1 -1
- package/dist/packages/stats/yield.d.ts +15 -6
- package/dist/packages/stats/yield.js +1 -1
- package/llms.txt +15 -3
- package/package.json +3 -3
|
@@ -1,13 +1,12 @@
|
|
|
1
|
-
import{hasPosition as
|
|
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: ${
|
|
6
|
-
border-bottom: 1px solid ${
|
|
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: ${
|
|
10
|
-
border-bottom: 1px solid ${
|
|
9
|
+
padding: 4px 8px; color: ${c.text};
|
|
10
|
+
border-bottom: 1px solid ${c.menuBorder}; white-space: nowrap;
|
|
11
11
|
}
|
|
12
|
-
`,t.head.appendChild(
|
|
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 {
|
|
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
|
-
|
|
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
|
|
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
|
|
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 './
|
|
15
|
-
export { openWaferMapGuide } from './
|
|
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 './
|
|
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"./
|
|
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";
|
|
@@ -1,31 +1,37 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { Die } from '../core/dies.js';
|
|
2
|
+
import type { Wafer } from '../core/wafer.js';
|
|
3
|
+
import type { LotStatsSummary, StatsSummary } from '../stats/types.js';
|
|
4
|
+
import { type FacetItem } from '../stats/facets.js';
|
|
5
|
+
import { type TestDef } from '../renderer/buildWaferMap.js';
|
|
2
6
|
import type { WaferMapDisplayItem } from './renderWaferGallery.js';
|
|
7
|
+
import { type PlotStore } from './plotStore.js';
|
|
8
|
+
import type { PlotSpec } from '../stats/plotSpec.js';
|
|
3
9
|
import type { BinColors } from '../renderer/binColors.js';
|
|
4
10
|
import { type SaveImageHandler, type SaveTextHandler } from './toolbar.js';
|
|
5
|
-
import {
|
|
6
|
-
export type InsightsView = 'overview' | 'distributions' | 'correlation' | 'sweeps';
|
|
11
|
+
import type { SweepSpec } from '../stats/sweep.js';
|
|
12
|
+
export type InsightsView = 'overview' | 'distributions' | 'correlation' | 'sweeps' | 'data' | 'plot';
|
|
7
13
|
/** Public option shape for `RenderOptions.insights`/`GalleryOptions.insights`. */
|
|
8
14
|
export interface InsightsOptions {
|
|
9
15
|
/**
|
|
10
|
-
* Show an "Insights" tab in the toolbar. Selecting it replaces the canvas/
|
|
16
|
+
* Show an "Insights" tab in the toolbar and a Maps | Insights switch. Selecting it replaces the canvas/
|
|
11
17
|
* grid with wmap's own chart suite across three sub-tabs — Overview
|
|
12
|
-
* (yield, bins, ring/quadrant yield
|
|
18
|
+
* (yield, bins, ring/quadrant yield), Distributions
|
|
13
19
|
* (process capability, boxplot, histogram), and Correlation (matrix +
|
|
14
|
-
* scatter) —
|
|
20
|
+
* scatter) — a Data tab (the scope as tables: statistics, dies, wafers, with CSV
|
|
21
|
+
* export) and a Plot tab, where the reader's own plots and sweeps live. Default true with a toolbar
|
|
22
|
+
* (a gallery always has one); `false` opts out.
|
|
15
23
|
*/
|
|
16
24
|
enabled?: boolean;
|
|
17
|
-
/** Which sub-tab is shown first. Default 'overview'. `'sweeps'`
|
|
18
|
-
* `sweeps` defined falls back to 'overview' — there is no such tab to open. */
|
|
25
|
+
/** Which sub-tab is shown first. Default 'overview'. `'sweeps'` opens the Plot tab, where sweeps are drawn. */
|
|
19
26
|
defaultView?: InsightsView;
|
|
20
27
|
/**
|
|
21
|
-
*
|
|
22
|
-
*
|
|
28
|
+
* @deprecated Sweeps are plots now: give them in `plots` as sweep plots, or read a sweeps file with
|
|
29
|
+
* `readPlotsFile`. Each entry here is still drawn, as a sweep plot on the Plot tab (and offered on selected dies),
|
|
30
|
+
* under the same `id`; a plot saved under that id takes precedence. The first use logs a notice.
|
|
23
31
|
*
|
|
24
|
-
* A sweep reads an ordered run of tests as a response curve rather than as
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
* same quantity measured at a series of power levels, recorded as a block of
|
|
28
|
-
* consecutive test numbers, swept up in one block and down in another.
|
|
32
|
+
* A sweep reads an ordered run of tests as a response curve rather than as independent tests, and measures the
|
|
33
|
+
* PAIR: where the first two series cross, and how far apart they are at given levels. It carries no population:
|
|
34
|
+
* the dies it aggregates are whatever the view is scoped to or the reader selected.
|
|
29
35
|
*
|
|
30
36
|
* ```ts
|
|
31
37
|
* insights: { enabled: true, sweeps: [{
|
|
@@ -38,41 +44,32 @@ export interface InsightsOptions {
|
|
|
38
44
|
* }] }
|
|
39
45
|
* ```
|
|
40
46
|
*
|
|
41
|
-
*
|
|
42
|
-
* says which tests form the curve, and is therefore valid for any population
|
|
43
|
-
* and portable between hosts. The dies it aggregates are whatever the
|
|
44
|
-
* Insights view is currently scoped to.
|
|
45
|
-
*
|
|
46
|
-
* The same definitions drive drilldown: a user can select dies on a map and
|
|
47
|
-
* right-click (or use the toolbar's "Chart the selection") to sweep just
|
|
48
|
-
* those dies. A single map offers this even when `enabled` is off.
|
|
49
|
-
*
|
|
50
|
-
* **Provisional.** This arrived as one site's request. The mechanism — an
|
|
51
|
-
* ordered run of tests read as a curve — is a recurring semiconductor shape
|
|
52
|
-
* (shmoo, VDD/temperature sweeps, retention, endurance, IV), which is the case
|
|
53
|
-
* for it being library-level; against it is that only one host has asked. It
|
|
54
|
-
* is shipping so tsmap can put it in front of that user, and the answer
|
|
55
|
-
* decides it: a second sweep-shaped use means it is general and stays, while
|
|
56
|
-
* "also measure X, and split by Y" means it is a bespoke chart and belongs
|
|
57
|
-
* behind a host-contributed-panel extension point instead. `separationAt` is
|
|
58
|
-
* the narrowest part of the surface and the first thing to drop if it is not
|
|
59
|
-
* used. Do not widen this shape before that question is settled.
|
|
60
|
-
*
|
|
61
|
-
* Range strings in `tests` (`"1010..1030"`) are NOT a widening in that sense:
|
|
62
|
-
* they are input syntax for the same list of tests, and add nothing to what a
|
|
63
|
-
* sweep measures. They were added because ranges are the first thing anyone
|
|
64
|
-
* writing a sweep by hand asks for, and they share the derived-test parser so
|
|
65
|
-
* the syntax is one syntax library-wide.
|
|
47
|
+
* Range strings in `tests` (`"1010..1030"`) share the derived-test parser, so the syntax is one syntax library-wide.
|
|
66
48
|
*/
|
|
67
49
|
sweeps?: SweepSpec[];
|
|
68
50
|
/**
|
|
69
|
-
*
|
|
70
|
-
*
|
|
71
|
-
*
|
|
72
|
-
* with the remaining `sweeps`. Without it the notice still shows, with no
|
|
73
|
-
* button. The library never edits the host's sweep list itself.
|
|
51
|
+
* @deprecated With `sweeps`. Called with the id of a sweep from `sweeps` that the reader deletes on the Plot tab, so the
|
|
52
|
+
* host stops supplying it (otherwise the next render brings it back). Plots deleted from `plots` arrive through
|
|
53
|
+
* `onPlotsChange` like any other.
|
|
74
54
|
*/
|
|
75
55
|
onRemoveSweeps?: (ids: string[]) => void;
|
|
56
|
+
/**
|
|
57
|
+
* The reader's saved plots: the recipes behind the **Plot** tab, in the order the reader keeps them. The
|
|
58
|
+
* library never stores anything itself: hand the list in here (from a file, `localStorage`, a server) and keep
|
|
59
|
+
* it current from `onPlotsChange`. A plot carries no population, so the same list works on any lot; one that
|
|
60
|
+
* needs a test the open lot lacks is kept and shown greyed with the reason, never dropped.
|
|
61
|
+
*/
|
|
62
|
+
plots?: PlotSpec[];
|
|
63
|
+
/**
|
|
64
|
+
* Called with the whole list after the reader adds, edits, duplicates, deletes or imports a plot (a burst of
|
|
65
|
+
* typing is one call). Keep it, and pass it back as `plots` next time.
|
|
66
|
+
*/
|
|
67
|
+
onPlotsChange?: (plots: PlotSpec[]) => void;
|
|
68
|
+
/**
|
|
69
|
+
* Choose a plots file to import, resolving to its text, or `null` when cancelled. Without it the Plot tab uses
|
|
70
|
+
* the browser's own file input; a desktop host passes its native dialog.
|
|
71
|
+
*/
|
|
72
|
+
onPickPlotsFile?: () => Promise<string | null>;
|
|
76
73
|
/**
|
|
77
74
|
* Open the Insights view on mount instead of starting on the map/grid.
|
|
78
75
|
* Default false — the tab is offered, the map is what you see first.
|
|
@@ -97,7 +94,7 @@ export interface InsightsTabDeps {
|
|
|
97
94
|
*/
|
|
98
95
|
getBinColors: () => BinColors;
|
|
99
96
|
/** Read fresh each render — used by the Overview tab's ring/quadrant regional yield cards. Default 4. */
|
|
100
|
-
getRingCount
|
|
97
|
+
getRingCount: () => number;
|
|
101
98
|
onSaveImage?: SaveImageHandler;
|
|
102
99
|
/** Optional host hook for the Overview tab's test-values "Export CSV" button — see `saveTextFile` (toolbar.ts). */
|
|
103
100
|
onSaveText?: SaveTextHandler;
|
|
@@ -127,6 +124,11 @@ export interface InsightsTabDeps {
|
|
|
127
124
|
* lands on this test), and only consulted for a leaf row of the sole item.
|
|
128
125
|
*/
|
|
129
126
|
focusTest?: (testNumber: number) => void;
|
|
127
|
+
/**
|
|
128
|
+
* A die row clicked in a table (the Data tab, or a table opened from a chart): leave Insights and show
|
|
129
|
+
* that die on its map. Omit where there is no map to go back to; the rows are then not clickable.
|
|
130
|
+
*/
|
|
131
|
+
locateDie?: (die: Die, waferIndex: number | undefined) => void;
|
|
130
132
|
/** Default sub-tab shown on first render. Default 'overview'. */
|
|
131
133
|
defaultView?: InsightsView;
|
|
132
134
|
/** Sweep definitions to render in the Distributions view — see `InsightsOptions.sweeps`. */
|
|
@@ -134,15 +136,13 @@ export interface InsightsTabDeps {
|
|
|
134
136
|
/** See `InsightsOptions.onRemoveSweeps`. */
|
|
135
137
|
onRemoveSweeps?: (ids: string[]) => void;
|
|
136
138
|
/**
|
|
137
|
-
*
|
|
138
|
-
*
|
|
139
|
-
* model (a tab row) instead of relying on the host toolbar's icon-swap
|
|
140
|
-
* toggle alone, whose "way back" is discoverable only via tooltip.
|
|
139
|
+
* The live list of saved plots, shared with the drilldown menu so a plot edited here is the one a right-click
|
|
140
|
+
* opens. Created from `plots`/`onPlotsChange` when omitted.
|
|
141
141
|
*/
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
142
|
+
plotStore?: PlotStore;
|
|
143
|
+
plots?: PlotSpec[];
|
|
144
|
+
onPlotsChange?: (plots: PlotSpec[]) => void;
|
|
145
|
+
onPickPlotsFile?: () => Promise<string | null>;
|
|
146
146
|
/** Opens the user guide. Rendered as an icon at the end of the tab row —
|
|
147
147
|
* while Insights is showing, the map toolbar is hidden (it held only a
|
|
148
148
|
* back-to-gallery button, which the back tab already provides, and this
|
|
@@ -184,5 +184,24 @@ export interface InsightsTabHandle {
|
|
|
184
184
|
/** Tears down every live panel's observers and removes `el`. */
|
|
185
185
|
destroy: () => void;
|
|
186
186
|
}
|
|
187
|
+
type Item = FacetItem & {
|
|
188
|
+
dies: Die[];
|
|
189
|
+
label: string;
|
|
190
|
+
waferIndex: number;
|
|
191
|
+
wafer: Wafer;
|
|
192
|
+
statsSummary?: StatsSummary;
|
|
193
|
+
/** This wafer's OWN pass bins — a lot can mix wafers built with different ones. */
|
|
194
|
+
passBins: readonly number[];
|
|
195
|
+
/** This wafer's OWN test defs, carried through so a scoped population can be
|
|
196
|
+
* reconciled (`mergeTestDefs`) over just the wafers in scope. */
|
|
197
|
+
testDefs?: TestDef[];
|
|
198
|
+
/** The wafer's real identity (`waferIdentityLabel`) — never the positional
|
|
199
|
+
* "Wafer N (no ID)" that `label` falls back to, which in a chart title would
|
|
200
|
+
* read as an ID. */
|
|
201
|
+
identity?: string;
|
|
202
|
+
};
|
|
203
|
+
/** One wafer (or the lot's single stand-in) as the Insights views see it. */
|
|
204
|
+
export type InsightsItem = Item;
|
|
187
205
|
export declare function createInsightsTab(deps: InsightsTabDeps): InsightsTabHandle;
|
|
206
|
+
export {};
|
|
188
207
|
//# sourceMappingURL=insightsTab.d.ts.map
|