@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.
- package/AGENTS.md +7 -7
- package/CHANGELOG.md +374 -1
- 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 +57 -15
- 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 +51 -2
- package/dist/packages/canvas-adapter/renderWaferMap.js +1 -1
- package/dist/packages/canvas-adapter/summaryPanel.d.ts +49 -11
- package/dist/packages/canvas-adapter/summaryPanel.js +2 -3
- 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/toCanvas.js +1 -1
- package/dist/packages/canvas-adapter/toolbar.d.ts +74 -93
- 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 +409 -51
- 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/aggregates.d.ts +6 -0
- package/dist/packages/core/aggregates.js +1 -1
- package/dist/packages/core/classify.d.ts +3 -2
- package/dist/packages/core/classify.js +1 -1
- package/dist/packages/core/compact.d.ts +205 -0
- package/dist/packages/core/compact.js +3 -0
- package/dist/packages/core/passBins.d.ts +23 -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/axisTicks.d.ts +14 -0
- package/dist/packages/renderer/axisTicks.js +1 -1
- package/dist/packages/renderer/binColors.d.ts +6 -6
- package/dist/packages/renderer/binColors.js +1 -1
- package/dist/packages/renderer/buildView.d.ts +27 -2
- package/dist/packages/renderer/buildView.js +1 -1
- package/dist/packages/renderer/buildWaferMap.js +1 -1
- package/dist/packages/renderer/compactView.d.ts +22 -0
- package/dist/packages/renderer/compactView.js +1 -0
- package/dist/packages/renderer/xyIndicator.d.ts +27 -0
- package/dist/packages/renderer/xyIndicator.js +1 -0
- package/dist/packages/stats/analyzeWaferLot.d.ts +28 -0
- package/dist/packages/stats/analyzeWaferLot.js +1 -1
- package/dist/packages/stats/analyzeWaferMap.d.ts +36 -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/binRows.d.ts +38 -0
- package/dist/packages/stats/binRows.js +1 -0
- package/dist/packages/stats/filterFindings.d.ts +25 -0
- package/dist/packages/stats/filterFindings.js +1 -1
- package/dist/packages/stats/findingText.d.ts +9 -0
- package/dist/packages/stats/findingText.js +2 -0
- 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.d.ts +16 -0
- package/dist/packages/stats/lotDrift.js +1 -0
- package/dist/packages/stats/lotPattern.d.ts +33 -0
- package/dist/packages/stats/lotPattern.js +1 -0
- package/dist/packages/stats/math.d.ts +60 -6
- package/dist/packages/stats/math.js +1 -1
- package/dist/packages/stats/patternClassification.d.ts +32 -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 +44 -0
- package/dist/packages/stats/presentation.js +1 -0
- package/dist/packages/stats/regions.d.ts +15 -0
- package/dist/packages/stats/regions.js +1 -1
- package/dist/packages/stats/renderSummaryReport.d.ts +23 -5
- package/dist/packages/stats/renderSummaryReport.js +32 -27
- package/dist/packages/stats/reportHtml.d.ts +54 -9
- package/dist/packages/stats/reportHtml.js +145 -55
- 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 +76 -0
- package/dist/packages/stats/synthesis.js +2 -0
- 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 +2 -2
- package/dist/packages/stats/yield.d.ts +15 -6
- package/dist/packages/stats/yield.js +1 -1
- package/dist/packages/stats/yieldRows.d.ts +31 -0
- package/dist/packages/stats/yieldRows.js +1 -0
- package/llms.txt +16 -4
- package/package.json +3 -3
- package/dist/packages/stats/findingsNarrative.d.ts +0 -3
- package/dist/packages/stats/findingsNarrative.js +0 -1
|
@@ -2,7 +2,7 @@ import type { Wafer } from '../core/wafer.js';
|
|
|
2
2
|
import type { Die } from '../core/dies.js';
|
|
3
3
|
import { type BinDef, type TestDef, type YieldSummary, type MetadataFieldDef } from '../renderer/buildWaferMap.js';
|
|
4
4
|
import type { StatsFinding, StatsSummary, LotStatsSummary } from '../stats/types.js';
|
|
5
|
-
import {
|
|
5
|
+
import type { ReportMap } from '../stats/renderSummaryReport.js';
|
|
6
6
|
import { type FindingsFilter } from '../stats/filterFindings.js';
|
|
7
7
|
import { type BinColors } from '../renderer/binColors.js';
|
|
8
8
|
import { buildWarningsBanner, type WaferWarning } from './warnings.js';
|
|
@@ -10,7 +10,7 @@ export { buildWarningsBanner };
|
|
|
10
10
|
import type { PlotMode } from '../renderer/buildView.js';
|
|
11
11
|
import { type CapabilityItem } from '../stats/capability.js';
|
|
12
12
|
import { type SaveTextHandler } from './toolbar.js';
|
|
13
|
-
import {
|
|
13
|
+
import type { DieListDisplayOptions } from './dieList.js';
|
|
14
14
|
export { csvField } from '../core/utils.js';
|
|
15
15
|
import { type Chunked } from '../core/utils.js';
|
|
16
16
|
import type { WaferMetadata } from '../core/metadata.js';
|
|
@@ -31,7 +31,9 @@ export declare function buildFacetSummaryChips(table: Array<{
|
|
|
31
31
|
values: Array<{
|
|
32
32
|
value: string;
|
|
33
33
|
}>;
|
|
34
|
-
}>,
|
|
34
|
+
}>,
|
|
35
|
+
/** How many values a field lists before it has been laid out (no width yet: first paint, a hidden parent). */
|
|
36
|
+
maxValuesPerField?: number): HTMLDivElement | null;
|
|
35
37
|
export interface MetadataStripStacked {
|
|
36
38
|
lotSize: number;
|
|
37
39
|
aggrMethod?: string;
|
|
@@ -89,7 +91,7 @@ export declare function buildYieldSection(yieldSummary: YieldSummary, dataCovera
|
|
|
89
91
|
totalDies: number;
|
|
90
92
|
edgeExcludedDies: number;
|
|
91
93
|
ratio: number;
|
|
92
|
-
}, passBins
|
|
94
|
+
}, passBins: number[]): HTMLDivElement;
|
|
93
95
|
export declare function buildBinSection(dies: Die[], binDefs: BinDef[] | undefined, mode: 'hard' | 'soft',
|
|
94
96
|
/**
|
|
95
97
|
* The map's resolved bin colours (`View.binColors`) — colours AND pass
|
|
@@ -129,6 +131,7 @@ export declare function buildBinBreakdownSection(params: {
|
|
|
129
131
|
softCounts?: Record<number, number>;
|
|
130
132
|
/** The map's active plot mode. `hardBin`/`softBin` pick the matching bin type. */
|
|
131
133
|
plotMode?: PlotMode;
|
|
134
|
+
/** The pass bins to resolve colours by, when `binColors` is omitted. Never defaulted: with neither the section is refused. */
|
|
132
135
|
passBins?: number[];
|
|
133
136
|
panel?: HTMLElement;
|
|
134
137
|
}): HTMLDivElement | null;
|
|
@@ -336,7 +339,24 @@ export declare function buildFindingsSection(findings: StatsFinding[], statsSumm
|
|
|
336
339
|
* thread a `panel` through and give it a `stateKey`, or it will be the one
|
|
337
340
|
* section that forgets whether the reader collapsed it.
|
|
338
341
|
*/
|
|
339
|
-
standalone?: boolean
|
|
342
|
+
standalone?: boolean,
|
|
343
|
+
/** The pass bins every wafer was judged by, for the synthesis the Detail modal opens with; omitted when they differ. */
|
|
344
|
+
passBins?: readonly number[]): HTMLDivElement | null;
|
|
345
|
+
/**
|
|
346
|
+
* The first section of both panels: what the wafer or lot did and what stands out, from the same
|
|
347
|
+
* `buildSynthesis` the HTML reports open with. A clean one says so ("Nothing stands out"), which a
|
|
348
|
+
* list of findings cannot, so this is its own section and not a part of the Findings one — that
|
|
349
|
+
* section is absent when there are no findings.
|
|
350
|
+
*
|
|
351
|
+
* Linked names (a region, a bin) select that finding, exactly as its row in the list below does.
|
|
352
|
+
*/
|
|
353
|
+
export declare function buildSynthesisSection(source: StatsSummary | LotStatsSummary,
|
|
354
|
+
/** The pass bins every wafer was judged by; omitted when they differ, and the items then rest on yield alone. */
|
|
355
|
+
passBins: readonly number[] | undefined, onFindingClick: ((finding: StatsFinding, row: HTMLButtonElement) => void) | undefined, activeFindingId: string | null,
|
|
356
|
+
/** Panel element owning the collapsed state. Omit for a stateless render. */
|
|
357
|
+
panel?: HTMLElement,
|
|
358
|
+
/** Opens the full report, which begins with this same section. Omitted ⇒ no link. */
|
|
359
|
+
onOpenReport?: () => void): HTMLDivElement;
|
|
340
360
|
/**
|
|
341
361
|
* Findings section with severity/kind/region filter controls — the
|
|
342
362
|
* panel-level entry point (wraps `buildFindingsSection(..., standalone:
|
|
@@ -371,7 +391,9 @@ export declare function buildFindingsSectionWithFilter(source: StatsSummary | Lo
|
|
|
371
391
|
/** Panel element owning the collapsed state. Omit for a stateless render. */
|
|
372
392
|
panel?: HTMLElement,
|
|
373
393
|
/** See {@link FindingsNotice}. Rendered above the filter row. */
|
|
374
|
-
notice?: FindingsNotice
|
|
394
|
+
notice?: FindingsNotice,
|
|
395
|
+
/** The pass bins every wafer was judged by; omitted when they differ. See {@link buildSynthesisSection}. */
|
|
396
|
+
passBins?: readonly number[]): HTMLDivElement | null;
|
|
375
397
|
/**
|
|
376
398
|
* Aggregate test value stats across all wafers in the lot. When every
|
|
377
399
|
* wafer's `StatsSummary.stats.perTestStats`/`.testSpecYield` is available
|
|
@@ -418,7 +440,15 @@ export declare function createSummaryPanelEl(placement: 'right' | 'left' | 'top'
|
|
|
418
440
|
* gutter on the gallery's panel where 12 belonged, and 12px inside a map card
|
|
419
441
|
* where 4 belonged.
|
|
420
442
|
*/
|
|
421
|
-
edgeInset: string, ownerDocument?: Document
|
|
443
|
+
edgeInset: string, ownerDocument?: Document,
|
|
444
|
+
/**
|
|
445
|
+
* Right-click on a finding (its row, a parent row or a finding named in the text): the host selects that finding's
|
|
446
|
+
* dies if they are not already and opens the drilldown menu on them. Without it the browser's menu is left alone.
|
|
447
|
+
*/
|
|
448
|
+
onFindingMenu?: (findingId: string, row: HTMLElement, at: {
|
|
449
|
+
x: number;
|
|
450
|
+
y: number;
|
|
451
|
+
}) => void): HTMLDivElement;
|
|
422
452
|
/** Wrap the canvas + panel in a flex container according to placement. */
|
|
423
453
|
export declare function wrapWithSummaryPanel(content: HTMLElement, panel: HTMLDivElement, placement: 'right' | 'left' | 'top' | 'bottom'): HTMLDivElement;
|
|
424
454
|
/**
|
|
@@ -457,8 +487,10 @@ export declare function renderWaferSummaryContentSteps(panel: HTMLDivElement, pa
|
|
|
457
487
|
* when omitted, which is the only set a bare panel could know about.
|
|
458
488
|
*/
|
|
459
489
|
warnings?: WaferWarning[];
|
|
460
|
-
passBins
|
|
461
|
-
|
|
490
|
+
/** The map's own pass bins (`WaferMapResult.passBins`): every yield and bin verdict in the panel judges by them. */
|
|
491
|
+
passBins: number[];
|
|
492
|
+
/** The map's own ring count (`WaferMapResult.ringCount`). Required, never defaulted. */
|
|
493
|
+
ringCount: number;
|
|
462
494
|
/** The map's resolved bin colours (`View.binColors`), so bars match die fills. */
|
|
463
495
|
binColors?: BinColors;
|
|
464
496
|
/**
|
|
@@ -480,6 +512,8 @@ export declare function renderWaferSummaryContentSteps(panel: HTMLDivElement, pa
|
|
|
480
512
|
metadataFields?: MetadataFieldDef[];
|
|
481
513
|
/** See `RenderOptions.dieList` — gates the "View die list" link below. */
|
|
482
514
|
dieListOptions?: DieListDisplayOptions;
|
|
515
|
+
/** A die row clicked in the Data tables: show that die on its map. */
|
|
516
|
+
onLocateDie?: (die: Die, waferIndex: number | undefined) => void;
|
|
483
517
|
/**
|
|
484
518
|
* Suppress the "Wafer Info" metadata section because the caller already
|
|
485
519
|
* renders this metadata elsewhere — `renderWaferMap` passes true whenever its
|
|
@@ -511,9 +545,11 @@ type ReportItem = {
|
|
|
511
545
|
* @internal Gallery items as report maps — the one conversion for the gallery's
|
|
512
546
|
* and the lot panel's report buttons. A slot not yet loaded is skipped, but each
|
|
513
547
|
* wafer keeps the label its position gives it, so a report names wafers exactly
|
|
514
|
-
* as the cards do. `
|
|
548
|
+
* as the cards do. `ringCount` fills in only for an item built without one; `passBins` only from the caller's own option, never a default.
|
|
515
549
|
*/
|
|
516
|
-
export declare function reportMapsFromItems(items: ReadonlyArray<ReportItem | null | undefined>,
|
|
550
|
+
export declare function reportMapsFromItems(items: ReadonlyArray<ReportItem | null | undefined>, ringCount: number | undefined,
|
|
551
|
+
/** For an item built without pass bins, from the caller's own `passBins` option. Never a built-in default: an item with neither is refused. */
|
|
552
|
+
passBins?: readonly number[]): ReportMap[];
|
|
517
553
|
/**
|
|
518
554
|
* {@link renderLotSummaryContentSteps}, run straight through — the entry for a
|
|
519
555
|
* caller that is not staging the render across tasks.
|
|
@@ -556,6 +592,8 @@ export declare function renderLotSummaryContentSteps(panel: HTMLDivElement, para
|
|
|
556
592
|
warnings?: WaferWarning[];
|
|
557
593
|
/** See `RenderOptions.dieList` — gates the "View die list" link below. */
|
|
558
594
|
dieListOptions?: DieListDisplayOptions;
|
|
595
|
+
/** A die row clicked in the Data tables: show that die on its map. */
|
|
596
|
+
onLocateDie?: (die: Die, waferIndex: number | undefined) => void;
|
|
559
597
|
/** Per-wafer findings tally badged onto the Wafer Yield rows — see
|
|
560
598
|
* `buildPerWaferYieldSection`. */
|
|
561
599
|
findingsFor?: (waferIndex: number) => {
|
|
@@ -1,3 +1,2 @@
|
|
|
1
|
-
import{testValue as Ne}from"../core/dieTable.js";import{waferDisplayLabel as ve}from"../core/waferLabel.js";import{itemPassBins as Se,passBinsLabel as dt}from"../core/passBins.js";import{isParametricTest as ct}from"../renderer/buildWaferMap.js";import{testLabel as pt,markedTestLabel as ut,unmarkedLabel as ft,derivedFields as we,derivedKeyText as mt,derivedCsvCell as Fe,isDerivedTest as Te,DERIVED_CSV_HEADER as De}from"../renderer/testLabel.js";import{buildRingRegions as gt,buildQuadrantRegions as ht,buildRegionYieldData as bt}from"../stats/regions.js";import{computeFunctionalYield as xt}from"../stats/analyzeWaferMap.js";import{renderWaferReportHtml as yt,renderLotReportHtml as Ct}from"../stats/renderSummaryReport.js";import{buildFindingsNarrative as vt}from"../stats/findingsNarrative.js";import{formatFindingTooltip as St}from"../stats/reportHtml.js";import{filterFindings as wt}from"../stats/filterFindings.js";import{buildFacetTable as $t,prettyKey as Ee}from"../stats/facets.js";import{commonMetadata as Re}from"../stats/facets.js";import{resolveMetadataColumns as kt}from"../stats/metadataColumns.js";import{resolveBinColors as Me,resolveBinColorsByWafer as Lt}from"../renderer/binColors.js";import{NO_DATA_FILL as Bt}from"../renderer/colorMap.js";import{classifySpec as Nt,isOutOfSpec as Ft}from"../renderer/spec.js";import{describeWaferPopulation as Ie,populationLabel as Tt}from"../stats/population.js";import{buildWarningsBanner as $e,collectWarnings as Ae}from"./warnings.js";export{$e as buildWarningsBanner};import{fmt as We,fmtAggregationMethod as Dt,plainBinTerms as de}from"../renderer/fmt.js";import{getUniqueTestNumbers as Et}from"../renderer/buildView.js";import{describeValues as Rt,quantile as Pe}from"../stats/math.js";import{pooledTestStatsSteps as Mt}from"../stats/capability.js";import{sortBinsForDisplay as It}from"../stats/binPareto.js";import{poolFunctionalYield as At}from"../stats/testPassRate.js";import{makeLabeledSelect as ze,makeSegmented as ke}from"./charts/chartShell.js";import{SHADOW as He,MOTION as Oe,LEADING as te,TRACKING as fe,wireControlHover as G,controlStyle as me,SPACE as x,RADIUS as q,FONT as F,CLR as $,sevColor as ge,openModal as Ye,openReportModal as _e,saveTextFile as je,wireTooltip as ce}from"./toolbar.js";import{buildDieListSection as Wt}from"./dieList.js";export{csvField}from"../core/utils.js";import{csvField as he,drain as be,maxOf as Pt,medianOfSorted as zt,minOf as Ht}from"../core/utils.js";import{metadataDisplayValue as Ot}from"../core/metadata.js";const Yt=$.panelBg,Ke=`1px solid ${$.menuBorder}`,_t="12px",Y=$.label,ne=$.value,Ve="10px";function l(e,t,i,n=document){const o=n.createElement(e);return t&&Object.assign(o.style,t),i!==void 0&&(o.textContent=i),o}function se(e){return l("div",{fontSize:Ve,fontWeight:"700",letterSpacing:fe,textTransform:"uppercase",color:Y,marginBottom:x.sm},e)}const qe=new WeakMap,Ge=["regionYield","testValues","functionalTests"];function xe(e){if(!e)return{collapsed:new Set(Ge),regionMode:"ring",waferSort:"slot"};let t=qe.get(e);return t||(t={collapsed:new Set(Ge),regionMode:"ring",waferSort:"slot"},qe.set(e,t)),t}function oe(e,t=!0,i,n){const o=l("div"),u=xe(n?.panel);n?.stateKey&&n.panel&&(t=!u.collapsed.has(n.stateKey));const s=document.createElement("button");s.type="button",Object.assign(s.style,{display:"flex",alignItems:"center",gap:x.xs,width:"100%",background:"none",border:"none",cursor:"pointer",padding:"0 0 6px",textAlign:"left"});const m=l("span",{fontSize:F.meta,color:Y,transition:`transform ${Oe.base}`,transform:t?"rotate(90deg)":"rotate(0deg)",display:"inline-block",lineHeight:te.none,marginRight:"1px"},"\u25B6"),p=l("span",{fontSize:Ve,fontWeight:"700",letterSpacing:fe,textTransform:"uppercase",color:Y,flex:"1"},e);if(G(s,"bare"),s.setAttribute("aria-expanded",t?"true":"false"),s.appendChild(m),s.appendChild(p),i){const b=l("span",{fontSize:F.meta,fontWeight:"700",background:$.warnBg,color:$.warnText,borderRadius:q.pill,padding:"1px 5px"},i);s.appendChild(b)}const r=l("div");r.style.display=t?"block":"none";let a=t;if(s.addEventListener("click",()=>{a=!a,r.style.display=a?"block":"none",m.style.transform=a?"rotate(90deg)":"rotate(0deg)",s.setAttribute("aria-expanded",a?"true":"false"),n?.stateKey&&n.panel&&(a?u.collapsed.delete(n.stateKey):u.collapsed.add(n.stateKey))}),n?.control){const b=l("div",{display:"flex",alignItems:"center",gap:x.sm});s.style.width="auto",s.style.flex="1",b.appendChild(s);const f=()=>{r.innerHTML="",n.render?.(r)},w=n.control(f);w&&(w.style.marginBottom="6px",w.addEventListener("click",k=>k.stopPropagation()),b.appendChild(w)),o.appendChild(b)}else o.appendChild(s);return o.appendChild(r),n?.render?.(r),{outer:o,content:r}}function Ue(){return l("div",{height:"1px",background:$.separator,margin:`${_t} 0`,flexShrink:"0"})}function Le(e,t,i=$.barFill,n,o){const u=l("div",{marginBottom:"5px"}),s=l("div",{display:"flex",justifyContent:"space-between",fontSize:F.body,color:ne,marginBottom:x.xxs}),m=l("span",{color:Y},e),p=l("span",{fontWeight:"600"},`${t.toFixed(1)}%`);s.appendChild(m),s.appendChild(p);const r=n!==void 0?n:Math.min(100,Math.max(0,t)),a=i,b=l("div",{position:"relative",height:"9px",background:$.bgActive,borderRadius:q.control,overflow:"hidden"}),f=l("div",{height:"100%",width:`${r}%`,background:a,borderRadius:q.control,transition:`width ${Oe.base}`});if(b.appendChild(f),o!==void 0){const w=l("div",{position:"absolute",top:"0",bottom:"0",left:`${o}%`,width:"2px",marginLeft:"-1px",background:$.value,opacity:"0.8"});b.appendChild(w)}return u.appendChild(s),u.appendChild(b),u}function Qe(){const e=l("div",{display:"flex",gap:x.sm,marginBottom:x.md});return e.finish=()=>{const t=e.lastElementChild;t&&Object.assign(t.style,{borderRight:"none",paddingRight:"0"})},e}function pe(e,t,i){const n=l("div",{padding:`${x.xxs} ${x.md} ${x.xxs} 0`,borderRight:Ke,textAlign:"center",flex:"1"}),o=l("div",{fontSize:F.heading,fontWeight:"700",color:ne,lineHeight:te.tight},e),u=l("div",{fontSize:F.body,color:Y,marginTop:x.xxs},t);return n.appendChild(o),n.appendChild(u),i&&n.appendChild(l("div",{fontSize:F.body,color:Y,opacity:"0.85"},i)),n}function Xe(e,t){const i=l("div",{display:"flex",justifyContent:"space-between",fontSize:F.body,gap:x.md,marginBottom:"3px"}),n=l("span",{color:Y,flexShrink:"0"},e),o=l("span",{color:ne,textAlign:"right",fontWeight:"500",wordBreak:"break-all"},t);return i.appendChild(n),i.appendChild(o),i}function jt(e,t){const i=l("div",{display:"flex",fontSize:F.body,gap:x.sm,marginBottom:"3px"}),n=l("span",{color:Y,flexShrink:"0"},`${e}:`),o=l("span",{color:ne,fontWeight:"500",wordBreak:"break-all"},t);return i.appendChild(n),i.appendChild(o),i}export function metadataEntries(e){const t=[];for(const[i,n]of Object.entries(e)){const o=Ot(n);o!==void 0&&t.push([i,o])}return t}const Kt=["lot","product","testprogram","split"],ye=e=>{const t=Kt.indexOf(e.toLowerCase().replace(/[^a-z]/g,""));return t===-1?Number.MAX_SAFE_INTEGER:t};export function buildFacetSummaryChips(e,t=3){if(!e.length)return null;const i=l("div",{display:"flex",flexWrap:"wrap",alignItems:"baseline",gap:`${x.xs} ${x.lg}`}),n=[...e].sort((m,p)=>ye(m.key)-ye(p.key)),o=n.filter(m=>ye(m.key)!==Number.MAX_SAFE_INTEGER),u=n.filter(m=>ye(m.key)===Number.MAX_SAFE_INTEGER),s=m=>{const p=l("span",{whiteSpace:"nowrap"}),r=l("span",{color:Y},`${Ee(m.key)}: `);p.appendChild(r);const a=m.values.slice(0,t),b=m.values.length-a.length;let f=a.map(w=>w.value).join(", ");return b>0&&(f+=` +${b} more`),p.appendChild(document.createTextNode(f)),p};for(const m of o)i.appendChild(s(m));if(u.length){const m=l("span",{display:"none",flexWrap:"wrap",alignItems:"baseline",gap:`${x.xs} ${x.lg}`});for(const a of u)m.appendChild(s(a));const p=l("button",{background:"none",border:"none",padding:"0",cursor:"pointer",color:$.iconActive,fontSize:F.body,whiteSpace:"nowrap"});p.type="button",G(p,"bare");const r=a=>{m.style.display=a?"contents":"none",p.textContent=a?"less":`+${u.length} more`,p.setAttribute("aria-expanded",String(a))};r(!1),p.addEventListener("click",()=>r(p.getAttribute("aria-expanded")!=="true")),i.appendChild(m),i.appendChild(p)}return i}export function buildMetadataStripRow(e,t,i){const n=$t(e,i),o=buildFacetSummaryChips(n);if(!o&&!t)return null;const u=l("div",{display:"flex",flexWrap:"wrap",alignItems:"baseline",gap:"4px 14px"});if(t){const s=l("span",{fontWeight:"500",whiteSpace:"nowrap"},t.aggrMethod?`${t.lotSize} wafers stacked \xB7 ${t.aggrMethod}`:`${t.lotSize} wafers stacked`);u.appendChild(s)}return o&&u.appendChild(o),u}const Vt={display:"flex",flexDirection:"column",gap:x.sm,background:$.menuBg,border:`1px solid ${$.menuBorder}`,borderRadius:q.container,padding:`${x.sm} ${x.lg}`,boxShadow:He.panel,fontSize:F.body,lineHeight:te.none,boxSizing:"border-box",width:"100%",minWidth:"0"};export function buildMetadataStripBox(e,t,i){const n=buildMetadataStripRow(e,t,i);if(!n)return null;const o=l("div",Vt);return o.appendChild(n),o}export function buildCompactMetadataRows(e){const t=metadataEntries(e);if(!t.length)return null;const i=l("div");for(const[n,o]of t)i.appendChild(jt(Ee(n),o));return i}export function buildMetadataInfoSection(e,t){const i=buildMetadataStripRow(e,t,{facetableOnly:e.length>1});if(!i)return null;const n=l("div");return n.appendChild(se("Wafer Info")),n.appendChild(i),n}export function buildYieldSection(e,t,i=[1]){const n=l("div");n.appendChild(se("Summary"));const o=Qe();if(o.appendChild(pe(String(e.totalDies),"Total dies")),e.partialDies>0&&o.appendChild(pe(String(e.partialDies),"Partial")),e.yieldPercent!==null){const u=dt([i]);o.appendChild(pe(`${e.yieldPercent.toFixed(1)}%`,"Yield",`pass: ${u}`))}return o.finish(),n.appendChild(o),e.edgeExcludedDies>0&&n.appendChild(Xe("Edge excluded (outer zone)",String(e.edgeExcludedDies))),n}export function buildBinSection(e,t,i,n,o,u){const s=new Map;if(o)for(const[r,a]of Object.entries(o))s.set(Number(r),a);else for(const r of e){if(r.partial||r.edgeExcluded)continue;const a=i==="hard"?r.hbin:r.sbin;a!=null&&s.set(a,(s.get(a)??0)+1)}if(!s.size)return null;const m=n??Me(e,{passBins:u,...i==="hard"?{hbinDefs:t}:{sbinDefs:t}}),p=l("div");p.appendChild(se(Ze(i,[...s.values()].reduce((r,a)=>r+a,0))));for(const r of Je(s,t,m[i],m.pass[i]))p.appendChild(r);return p}function Ze(e,t){return`${e==="hard"?"Hard":"Soft"} Bin Breakdown \u2014 % of dies (N=${t})`}function Je(e,t,i,n){const o=[...e.values()].reduce((m,p)=>m+p,0),u=t?new Map(t.map(m=>[m.bin,m])):null;return It(e.entries(),n).map(([m,p])=>{const r=u?.get(m),a=r?.name?`Bin ${m} \xB7 ${r.name}`:`Bin ${m}`,b=p/o*100,f=i.get(m)??Bt;return Le(`${a} (${p})`,b,f)})}export function buildBinBreakdownSection(e){const{dies:t,hbinDefs:i,sbinDefs:n,hardCounts:o,softCounts:u,plotMode:s,passBins:m=[1],panel:p}=e,r=e.binColors??Me(t,{passBins:m,hbinDefs:i,sbinDefs:n}),a=v=>{const g=v==="hard"?o:u,h=new Map;if(g){for(const[y,S]of Object.entries(g))h.set(Number(y),S);return h}for(const y of t){if(y.partial||y.edgeExcluded)continue;const S=v==="hard"?y.hbin:y.sbin;S!=null&&h.set(S,(h.get(S)??0)+1)}return h},b=a("hard"),f=a("soft"),w=b.size>0,k=f.size>0;if(!w&&!k)return null;const E=xe(p),H=s==="softBin"||s==="stackedSoftBins"?"soft":s==="hardBin"||s==="stackedBins"?"hard":void 0;let I=E.binMode??H??(w?"hard":"soft");I==="hard"&&!w&&(I="soft"),I==="soft"&&!k&&(I="hard");const O=I==="hard"?b:f,z=I==="hard"?i:n,C=[...O.values()].reduce((v,g)=>v+g,0),{outer:d}=oe(Ze(I,C),!0,void 0,{stateKey:"bins",panel:p,render:v=>{const g=I==="hard"?r.hard:r.soft;for(const h of Je(O,z,g,r.pass[I]))v.appendChild(h)},control:w&&k&&p?()=>ke([["hard","Hard"],["soft","Soft"]],I,v=>{E.binMode=v;const g=buildBinBreakdownSection(e);g&&d.replaceWith(g)},p.ownerDocument,!0):void 0});return d}export function buildLotBinSection(e,t,i,n){return buildBinSection(e,t,i,n)}export function buildRegionYieldPanelSection(e){const{diesByWafer:t,allWafers:i,ringCount:n,passBins:o,panel:u}=e,s=xe(u),m=a=>bt(t,i,n,o,a==="ring"?gt:ht);if(!m("ring").length&&!m("quadrant").length)return null;const p=s.regionMode,{outer:r}=oe(p==="ring"?"Ring Yield":"Quadrant Yield",!0,void 0,{stateKey:"regionYield",panel:u,render:a=>{const b=m(p);if(!b.length){a.appendChild(l("div",{fontSize:F.body,color:Y},"No positioned dies for this breakdown."));return}for(const{label:f,n:w,yieldPercent:k}of b)a.appendChild(Le(`${f} (N=${w})`,k))},control:u?()=>ke([["ring","Ring"],["quadrant","Quadrant"]],p,a=>{s.regionMode=a;const b=buildRegionYieldPanelSection(e);b&&r.replaceWith(b)},u.ownerDocument,!0):void 0});return r}export function buildLotOverviewSection(e,t=[]){const i=l("div"),n=Ie(e.perWafer.map(s=>s.summary.wafer));i.appendChild(se(n.lotId!==void 0?"Lot Summary":"Summary"));const o=Qe();o.appendChild(pe(String(e.stats.waferCount),"Wafers"));const u=e.perWafer.map(s=>s.summary.stats.yieldPercent).filter(s=>s!==null);if(u.length){const s=u.reduce((m,p)=>m+p,0)/u.length;o.appendChild(pe(`${s.toFixed(1)}%`,"Mean per-wafer yield"))}if(o.finish(),i.appendChild(o),t.length){const s=t.reduce((p,r)=>p+r.stats.analyzedDies,0),m=t.reduce((p,r)=>p+r.stats.excludedDies,0);(s||m)&&i.appendChild(Xe("Dies analysed",m?`${s.toLocaleString()} (${m.toLocaleString()} excluded)`:s.toLocaleString()))}return i}export function buildPerWaferYieldSection(e,t,i,n,o){const u=e.perWafer.map(C=>({waferIndex:C.waferIndex,label:ve(t[C.waferIndex],C.waferIndex).replace(/\s*·\s*\d+(\.\d+)?%$/,""),yieldPct:C.summary.stats.yieldPercent})).filter(C=>C.yieldPct!==null);if(!u.length)return null;const s=Ht(u.map(C=>C.yieldPct)),m=Pt(u.map(C=>C.yieldPct)),p=s===m?"":` (${s.toFixed(1)}\u2013${m.toFixed(1)}%)`,r=[...u.map(C=>C.yieldPct)].sort((C,d)=>C-d),a=zt(r),b=Pe(r,.25),f=Pe(r,.75),w=u.length>=5?b-1.5*(f-b):-1/0,k=xe(n),E=C=>{const d=k.waferSort==="yield"?[...u].sort((v,g)=>v.yieldPct-g.yieldPct):u;for(const{waferIndex:v,label:g,yieldPct:h}of d){const S=h<w?`${g} \xB7 low outlier`:g,L=Le(S,h,void 0,void 0,a),A=o?.(v);A?.total&&O(L,A),z(L,v,g,h,A),C.appendChild(L)}},H=` \xB7 median ${a.toFixed(1)}%`,{outer:I}=oe("Wafer Yield"+p+H,!0,void 0,{stateKey:"waferYield",panel:n,render:E,control:n?C=>ke([["slot","Slot"],["yield","Yield"]],k.waferSort,d=>{k.waferSort=d,C()},n.ownerDocument,!0):void 0});return I;function O(C,d){const v=d.unusual?"unusual":d.notable?"notable":"info",g=l("span",{marginLeft:x.sm,flexShrink:"0",background:ge(v),color:"#fff",borderRadius:q.container,padding:"0 5px",fontSize:F.body,fontWeight:"600"},`${d.total}`);g.setAttribute("aria-hidden","true"),ce(g,`${d.total} finding${d.total===1?"":"s"}`+(d.unusual?`, ${d.unusual} unusual`:"")+(d.notable?`, ${d.notable} notable`:"")),C.firstElementChild?.firstElementChild?.appendChild(g)}function z(C,d,v,g,h){i&&(C.style.cursor="pointer",C.style.borderRadius="4px",C.style.padding="2px 3px",C.style.marginLeft="-3px",C.style.marginRight="-3px",C.setAttribute("role","button"),C.setAttribute("aria-label",`${v}, ${g.toFixed(1)}% yield`+(h?.total?`, ${h.total} finding${h.total===1?"":"s"}${h.unusual?`, ${h.unusual} unusual`:""}`:"")+" \u2014 open wafer"),C.tabIndex=0,C.addEventListener("mouseenter",()=>{C.style.background=$.bgHover}),C.addEventListener("mouseleave",()=>{C.style.background=""}),C.addEventListener("focus",()=>{C.style.background=$.bgHover}),C.addEventListener("blur",()=>{C.style.background=""}),C.addEventListener("click",()=>i(d)),C.addEventListener("keydown",y=>{(y.key==="Enter"||y.key===" ")&&(y.preventDefault(),i(d))}))}}function qt(e){return Rt(Float64Array.from(e))}function et(e,t){if(!e)return[];const i=e.waferMetadata??(e.perWaferMetadata?Re(e.perWaferMetadata.map(n=>({metadata:n}))):void 0);return!i||!Object.keys(i).length?[]:kt({waferMetadata:i,waferKeys:"identity",waferPlacement:"csv",reservedLabels:t}).columns}function tt(e){return e?.populationLabel?[{key:"population",label:"Population",scope:"wafer",constant:e.populationLabel,get:()=>e.populationLabel,csvOnly:!0}]:[]}export function buildTestSection(...e){return be(buildTestSectionSteps(...e))}export function*buildTestSectionSteps(e,t,i,n,o,u,s,m,p="full"){const r=e.filter(c=>!c.partial&&!c.edgeExcluded),a=new Map((n?.perTestStats??[]).map(c=>[c.testNumber,c])),b=new Map((n?.testSpecYield??[]).map(c=>[c.testNumber,c]));let f;if(t?.length?f=t.filter(ct).map(c=>({testNumber:c.testNumber,name:c.name,unit:c.unit,limitLow:c.limitLow,limitHigh:c.limitHigh,limitLowInclusive:c.limitLowInclusive,limitHighInclusive:c.limitHighInclusive,...we(c)})):f=Et(r).map(W=>({testNumber:W,name:pt(void 0,W)})),!f.length)return null;const w=f.filter(c=>r.some(W=>{const j=Ne(W,c.testNumber);return j!==void 0&&isFinite(j)}));if(!w.length)return null;const k=w.some(c=>c.limitLow!==void 0||c.limitHigh!==void 0),E=[],H=c=>!!c&&c.stddev!==void 0&&c.median!==void 0&&c.q1!==void 0&&c.q3!==void 0,I=c=>c.limitLow!==void 0||c.limitHigh!==void 0,O=!!(s?.length&&t?.length),C=(O||w.some(c=>!H(a.get(c.testNumber))||I(c)&&!b.has(c.testNumber)))&&t?.length?yield*Mt(s??[{dies:e}],t):void 0;for(const c of w){yield;const W=a.get(c.testNumber),j=C?.specTally.get(c.testNumber),ee=C?.stats.get(c.testNumber),K=()=>r.map(D=>Ne(D,c.testNumber)).filter(D=>D!==void 0&&isFinite(D));let Q;if(H(W)){const D=W;Q={testNumber:c.testNumber,min:D.min,max:D.max,mean:D.mean,count:D.count,stddev:D.stddev,median:D.median,q1:D.q1,q3:D.q3}}else if(ee)Q={testNumber:c.testNumber,...ee};else{const D=K();if(!D.length)continue;Q={testNumber:c.testNumber,...qt(D)}}let M=null,B=0;if(I(c)){const D=b.get(c.testNumber);if(D)({yieldPercent:M,totalDies:B}=D);else if(j)B=j.n,M=B>0?(B-j.fail)/B*100:null;else{const X=K();let ue=0;for(const ie of X)Ft(Nt(ie,c))&&ue++;B=X.length,M=X.length>0?(X.length-ue)/X.length*100:null}}E.push({entry:c,stats:Q,specYieldPct:M,specN:B})}if(!E.length)return null;const d=new Map;for(const c of(O?C?.capability:void 0)??[])c.hasSpec&&d.set(c.testNumber,c.ppk);const v=d.size>0,h=new Set(E.map(c=>c.stats.count)).size===1?E[0].stats.count:null,y=l("div"),S=l("div",{display:"flex",alignItems:"center",justifyContent:"space-between",marginBottom:x.sm}),L=h!==null?` \xB7 N=${h.toLocaleString()}`:"",A=u?.populationLabel?`Test Values (${E.length}) \u2014 ${u.populationLabel}${L}`:`Test Values (${E.length})${L}`,T=se(A);T.style.marginBottom="0",S.appendChild(T);const R=document.createElement("button");R.type="button",R.textContent="Test values CSV",Object.assign(R.style,{...me("outlined")}),G(R),R.addEventListener("click",()=>{const c=["Test","Unit","N","Min","Q1","Median","Mean","Q3","Max","StdDev"];v&&c.push("Ppk");const W=E.some(B=>B.specYieldPct!==null&&B.specN!==B.stats.count);k&&(c.push("Lo limit","Hi limit","Limit Yield %"),W&&c.push("Limit Yield N"));const j=E.some(B=>Te(B.entry));j&&c.push(De);const ee=[...et(u,c),...tt(u)],Q=[[...ee.map(B=>B.label),...c].map(he).join(",")],M=B=>We(B,void 0,"engineering");for(const{entry:B,stats:D,specYieldPct:X,specN:ue}of E){const ie=[...ee.map(le=>le.constant??""),B.name,B.unit??"",String(D.count),M(D.min),M(D.q1),M(D.median),M(D.mean),M(D.q3),M(D.max),M(D.stddev)];if(v){const le=d.get(B.testNumber);ie.push(le==null?"":le.toFixed(3))}k&&(ie.push(B.limitLow!==void 0?M(B.limitLow):"",B.limitHigh!==void 0?M(B.limitHigh):"",X!==null?X.toFixed(1):""),W&&ie.push(B.limitLow!==void 0||B.limitHigh!==void 0?String(ue):"")),j&&ie.push(Fe(B)),Q.push(ie.map(he).join(","))}je(Q.join(`
|
|
2
|
-
`),"test-values.csv","text/csv",o)}),S.appendChild(R),y.appendChild(S);const P=l("table",{width:"100%",borderCollapse:"collapse",fontSize:F.body}),Z=document.createElement("thead"),J=document.createElement("tr"),_=["Test"];h===null&&_.push("N"),p==="full"&&_.push("Min","Q1","Median"),_.push("Mean"),p==="full"&&_.push("Q3","Max","StdDev"),v&&_.push("Ppk"),p==="full"&&k&&_.push("Lo limit","Hi limit"),k&&_.push("Limit yield");for(const c of _){const W=l("th",{textAlign:c==="Test"?"left":"right",fontWeight:"600",fontSize:F.meta,letterSpacing:fe,textTransform:"uppercase",color:Y,padding:"3px 8px 5px",borderBottom:`1px solid ${$.menuBorder}`,whiteSpace:"nowrap"},c);J.appendChild(W)}Z.appendChild(J),P.appendChild(Z);const U=document.createElement("tbody");for(const{entry:c,stats:W,specYieldPct:j,specN:ee}of E){const K=B=>We(B,c.unit,i),Q=document.createElement("tr"),M=(B,D="right")=>{const X=l("td",{textAlign:D,padding:`${x.xs} ${x.md}`,borderBottom:`1px solid ${$.menuBorder}`,color:$.text,whiteSpace:"nowrap"},B);Q.appendChild(X)};if(M(ut(c,c.testNumber),"left"),h===null&&M(`${W.count}`),p==="full"&&(M(K(W.min)),M(K(W.q1)),M(K(W.median))),M(K(W.mean)),p==="full"&&(M(K(W.q3)),M(K(W.max)),M(K(W.stddev))),v){const B=d.get(c.testNumber);M(B==null?"\u2014":B.toFixed(2))}p==="full"&&k&&(M(c.limitLow!==void 0?K(c.limitLow):"\u2014"),M(c.limitHigh!==void 0?K(c.limitHigh):"\u2014")),k&&M(j!==null?h!==null&&ee===h?`${j.toFixed(1)}%`:`${j.toFixed(1)}% (N=${ee})`:"\u2014"),U.appendChild(Q)}P.appendChild(U);const N=l("div",{overflowX:"auto"});N.appendChild(P);const V=st(N,E.map(c=>({label:c.entry.name,...we(c.entry)})));if(!m)return y.appendChild(V),y;S.remove();const{outer:re}=oe(A,!0,void 0,{stateKey:"testValues",panel:m,render:c=>c.appendChild(V),control:()=>R});return re}export function buildFunctionalTestSection(e,t,i,n,o,u){const s=i??xt(e,t);if(!s?.length)return null;const p=new Set(s.map(g=>g.totalDies)).size===1?s[0].totalDies:null,r=p!==null?` \xB7 N=${p.toLocaleString()}`:"",a=l("div"),b=l("div",{display:"flex",alignItems:"center",justifyContent:"space-between",marginBottom:x.sm}),f=o?.populationLabel?`Functional Tests (${s.length}) \u2014 ${o.populationLabel}${r}`:`Functional Tests (${s.length})${r}`,w=se(f);w.style.marginBottom="0",b.appendChild(w);const k=document.createElement("button");k.type="button",k.textContent="Functional CSV",Object.assign(k.style,{...me("outlined")}),G(k),k.addEventListener("click",()=>{const g=["Test","N","Pass","Fail","Pass Rate %"],h=s.some(L=>Te(L));h&&g.push(De);const y=[...et(o,g),...tt(o)],S=[[...y.map(L=>L.label),...g].map(he).join(",")];for(const L of s)S.push([...y.map(A=>A.constant??""),ft(L.label),String(L.totalDies),String(L.passDies),String(L.failDies),L.passRatePercent!==null?L.passRatePercent.toFixed(1):"",...h?[Fe(L)]:[]].map(he).join(","));je(S.join(`
|
|
3
|
-
`),"functional-tests.csv","text/csv",n)}),b.appendChild(k),a.appendChild(b);const E=l("table",{width:"100%",borderCollapse:"collapse",fontSize:F.body}),H=document.createElement("thead"),I=document.createElement("tr"),O=p===null?["Test","N","Pass","Fail","Pass rate"]:["Test","Pass","Fail","Pass rate"];for(const g of O)I.appendChild(l("th",{textAlign:g==="Test"?"left":"right",fontWeight:"700",fontSize:F.body,letterSpacing:fe,textTransform:"uppercase",color:Y,padding:"3px 8px 5px",borderBottom:`1px solid ${$.menuBorder}`,whiteSpace:"nowrap"},g));H.appendChild(I),E.appendChild(H);const z=document.createElement("tbody");for(const g of s){const h=document.createElement("tr"),y=(S,L="right")=>{h.appendChild(l("td",{textAlign:L,padding:`${x.xs} ${x.md}`,borderBottom:`1px solid ${$.menuBorder}`,color:ne,whiteSpace:"nowrap"},S))};y(g.label,"left"),p===null&&y(String(g.totalDies)),y(String(g.passDies)),y(String(g.failDies)),y(g.passRatePercent!==null?`${g.passRatePercent.toFixed(1)}%`:"\u2014"),z.appendChild(h)}E.appendChild(z);const C=l("div",{overflowX:"auto"});C.appendChild(E);const d=st(C,s);if(!u)return a.appendChild(d),a;b.remove();const{outer:v}=oe(f,!0,void 0,{stateKey:"functionalTests",panel:u,render:g=>g.appendChild(d),control:()=>k});return v}export function buildLotFunctionalSection(e,t,i,n,o){const u=i?.length?{perWaferMetadata:i.map(m=>m.wafer??{}),populationLabel:`${i.length} wafer${i.length===1?"":"s"} pooled`}:void 0,s=At(i);return s?buildFunctionalTestSection(e,t,s,n,u,o):buildFunctionalTestSection(e,t,void 0,n,u,o)}const ae={unusual:0,notable:1,info:2};function Be(e){return l("span",{display:"inline-block",width:"7px",height:"7px",borderRadius:q.pill,background:ge(e),flexShrink:"0"})}function nt(e,t){let i=e.summary;if(t){const n=[" has "," have "].find(o=>i.startsWith(`${t}${o}`));n?i=i.slice(t.length+n.length):i.startsWith(`${t} `)&&(i=i.slice(t.length+1))}return i=de(i),i.length?i[0].toUpperCase()+i.slice(1):i}function it(e){return ot(e.map(t=>({label:t.variable.label,...we(t.variable)})))}function ot(e){const t=mt(e);return t?l("div",{fontSize:F.body,color:Y,marginTop:x.sm,lineHeight:te.base},t):null}function st(e,t){const i=ot(t);if(!i)return e;const n=l("div",{});return n.append(e,i),n}export function buildFindingsSection(e,t,i,n,o=!1){if(!e.length)return null;const u=e.some(d=>d.severity==="unusual"||d.severity==="notable"),s=u?e.filter(d=>d.severity!=="info").length.toString():void 0,{outer:m,content:p}=o?(()=>{const d=l("div");return{outer:d,content:d}})():oe(`Findings (${e.length})`,u,s);function r(d){return d.reduce((v,g)=>ae[g.severity]<ae[v]?g.severity:v,"info")}function a(d){const v=new Map;for(const h of d){const y=`${h.comparison.family}\0${h.comparison.left}`,S=v.get(y)??[];S.push(h),v.set(y,S)}const g=[...v.entries()].map(([h,y])=>{const S=[...y].sort((L,A)=>ae[L.severity]-ae[A.severity]);return{key:h,findings:S,worst:r(S)}});return g.sort((h,y)=>ae[h.worst]-ae[y.worst]),g}function b(d,v){const h={ring:"Ring",quadrant:"Quadrant",sector:"Sector","reticle-position":"Reticle","test-site":"Test site",cluster:"Cluster","edge-arc":"Edge arc",wafer:"Wafer"}[d]??d;return v.toLowerCase().startsWith(h.toLowerCase())?v:`${h}: ${v}`}const f=e.filter(d=>d.comparison.family==="spatial-pattern"),w=new Set([...f.flatMap(d=>d.relatedIds??[]),...e.flatMap(d=>d.absorbedIds??[])]),k=e.filter(d=>d.comparison.family!=="spatial-pattern"&&!w.has(d.id)),E=a(k),H=()=>[...E.flatMap(d=>d.findings),...f.flatMap(d=>e.filter(v=>d.relatedIds?.includes(v.id)))];function I(d,v=!1,g){const h=n===d.id,y=document.createElement("button");return y.type="button",y.dataset.wmapFinding=d.id,y.textContent=nt(d,g),ce(y,St(d)),y.setAttribute("aria-current",h?"true":"false"),Object.assign(y.style,{border:`1px solid ${$.menuBorder}`,borderLeft:`3px solid ${ge(d.severity)}`,background:h?$.bgActive:$.menuBg,borderRadius:q.container,padding:v?"6px 10px":"8px 10px",textAlign:"left",fontSize:v?"10px":"11px",fontWeight:h?"600":"400",color:$.text,cursor:"pointer",width:"100%",marginBottom:x.xs}),G(y,"bare"),y.addEventListener("click",()=>i(d,y)),y}const O=de(vt(e)??"");if(O){const d=l("div",{background:$.bgActive,borderRadius:q.container,padding:`${x.md} ${x.lg}`,marginBottom:x.md,display:"flex",gap:x.md,alignItems:"flex-start"}),v=l("span",{flex:"1",fontSize:F.body,lineHeight:te.base,color:$.text},O);d.appendChild(v);const g=l("button",{flexShrink:"0",border:"none",background:"none",fontSize:F.body,color:$.icon,cursor:"pointer",padding:"0",lineHeight:te.base,whiteSpace:"nowrap"},"Detail \u25B8");g.type="button",G(g,"bare"),g.addEventListener("click",()=>{const h=Ye({title:"Findings Summary",onClose:()=>{},anchor:g}),y=l("p",{fontSize:"16px",lineHeight:te.base,color:$.text,padding:`${x.xxl} ${x.xxxl} ${x.xxl}`,margin:"0",borderBottom:`1px solid ${$.menuBorder}`,flexShrink:"0"},O);h.contentWrap.appendChild(y);const S=l("div",{overflowY:"auto",padding:`${x.xxl} ${x.xxxl}`,flex:"1"}),L=(T,R)=>{const P=l("div",{display:"flex",alignItems:"center",gap:"7px",fontSize:F.sub,fontWeight:"600",color:ne,marginTop:x.lg,marginBottom:x.xs});return P.appendChild(Be(T)),P.appendChild(l("span",{},R)),P};for(const T of f){S.appendChild(L(T.severity,T.comparison.left)),S.appendChild(l("div",{fontSize:F.body,color:$.text,padding:"4px 0 4px 15px",marginBottom:x.xxs},de(T.summary)));const R=e.filter(P=>T.relatedIds?.includes(P.id));for(const P of R)S.appendChild(l("div",{fontSize:F.body,color:Y,padding:"2px 0 2px 23px",marginBottom:x.xxs},de(P.summary)))}for(const T of E){const[R,P]=T.key.split("\0");S.appendChild(L(T.worst,b(R,P)));for(const Z of T.findings)S.appendChild(l("div",{fontSize:F.body,color:$.text,padding:"4px 0 4px 15px",marginBottom:x.xxs},nt(Z,P)))}const A=it(H());A&&S.appendChild(A),h.contentWrap.appendChild(S),Object.assign(h.contentWrap.style,{flexDirection:"column",overflow:"hidden"})}),d.appendChild(g),p.appendChild(d)}let z=!0;for(const d of f){z||p.appendChild(l("div",{height:"1px",background:$.separator,margin:"4px 0"})),z=!1;const v=e.filter(L=>d.relatedIds?.includes(L.id)),g=v.length>0,h=l("div",{position:"relative",marginBottom:g?"2px":"4px"}),y=n===d.id,S=document.createElement("button");if(S.type="button",S.dataset.wmapFinding=d.id,Object.assign(S.style,{border:`1px solid ${$.menuBorder}`,borderLeft:`3px solid ${ge(d.severity)}`,background:y?$.bgActive:$.menuBg,borderRadius:q.container,padding:"8px 32px 8px 10px",textAlign:"left",fontSize:F.body,fontWeight:y?"600":"500",color:$.text,cursor:"pointer",width:"100%"}),S.textContent=de(d.summary),ce(S,d.summary),S.setAttribute("aria-current",y?"true":"false"),G(S,"bare"),S.addEventListener("click",()=>i(d,S)),h.appendChild(S),g){const L=l("div",{display:"none",paddingLeft:"12px",marginBottom:x.xs});for(const R of v)L.appendChild(I(R,!0));h.appendChild(L);let A=!1;const T=l("button",{position:"absolute",top:"50%",right:"8px",transform:"translateY(-50%)",border:"none",background:"none",fontSize:F.body,color:$.icon,cursor:"pointer",padding:`${x.xxs} ${x.xs}`,lineHeight:te.none},"\u25B8");G(T,"bare"),T.type="button",ce(T),T.setAttribute("aria-label","Show supporting findings"),T.setAttribute("aria-expanded","false"),T.addEventListener("click",R=>{R.stopPropagation(),A=!A,L.style.display=A?"block":"none",T.textContent=A?"\u25BE":"\u25B8";const P=A?"Hide supporting findings":"Show supporting findings";T.setAttribute("aria-label",P),T.setAttribute("aria-expanded",String(A))}),h.appendChild(T)}p.appendChild(h)}for(const d of E){z||p.appendChild(l("div",{height:"1px",background:$.separator,margin:"4px 0"})),z=!1;const[v,g]=d.key.split("\0"),h=l("div",{display:"flex",alignItems:"center",gap:x.sm,fontSize:F.body,fontWeight:"600",color:ne,margin:"2px 0 4px"});h.appendChild(Be(d.worst)),h.appendChild(l("span",{},b(v,g))),p.appendChild(h);for(const y of d.findings)p.appendChild(I(y,!1,g))}const C=it(H());return C&&p.appendChild(C),m}const Gt=[{value:"",label:"All kinds"},{value:"yield",label:"Yield"},{value:"hardBin",label:"Hard bin"},{value:"softBin",label:"Soft bin"},{value:"test",label:"Test value"},{value:"functionalTest",label:"Functional test"},{value:"spatialPattern",label:"Spatial pattern"}],Ut=[{value:"",label:"All regions"},{value:"ring",label:"Ring"},{value:"quadrant",label:"Quadrant"},{value:"reticle-position",label:"Reticle position"},{value:"test-site",label:"Test site"},{value:"wafer",label:"Wafer"},{value:"sector",label:"Sector"},{value:"cluster",label:"Cluster"},{value:"edge-arc",label:"Edge arc"},{value:"spatial-pattern",label:"Spatial pattern"}],Qt=["unusual","notable","info"],Xt={unusual:"Unusual",notable:"Notable",info:"Info"};function Zt(e,t,i){const n=l("div",{display:"flex",flexWrap:"wrap",alignItems:"center",gap:x.lg,marginBottom:x.lg,paddingBottom:"10px",borderBottom:`1px solid ${$.separator}`}),o={unusual:0,notable:0,info:0};for(const r of e)o[r.severity]++;const u=Qt.filter(r=>o[r]>0),s=new Set(t.severity===void 0?u:Array.isArray(t.severity)?t.severity:[t.severity]),m=l("div",{display:"flex",gap:x.xs,flexWrap:"wrap"});for(const r of u){const a=document.createElement("button");a.type="button";const b=()=>{const f=s.has(r);a.setAttribute("aria-pressed",f?"true":"false"),Object.assign(a.style,{display:"inline-flex",alignItems:"center",gap:"5px",border:`1px solid ${$.menuBorder}`,borderRadius:q.pill,background:f?$.bgActive:"none",opacity:f?"1":"0.5",color:$.text,fontSize:F.body,padding:`${x.xxs} ${x.md}`,cursor:"pointer"}),a.dataset.on=f?"true":"false"};a.appendChild(Be(r)),a.appendChild(l("span",{},`${Xt[r]} ${o[r]}`)),ce(a,()=>s.has(r)?"Click to hide these findings":"Click to show these findings"),b(),G(a,"bare"),a.addEventListener("click",()=>{s.has(r)?s.delete(r):s.add(r),t.severity=s.size===u.length?void 0:[...s],i()}),m.appendChild(a)}n.appendChild(m);const p=t.kind!==void 0||t.family!==void 0;return(e.length>=Jt||p)&&(n.appendChild(ze("Kind:",Gt,t.kind??"",r=>{t.kind=r||void 0,i()},{maxWidth:"130px"})),n.appendChild(ze("Region:",Ut,t.family??"",r=>{t.family=r||void 0,i()},{maxWidth:"150px"}))),n}const Jt=8;function en(e,t){const i=l("div",{display:"flex",flexDirection:"column",gap:x.xs,padding:x.md,marginBottom:x.md,background:$.bgHover,borderRadius:q.control},void 0,t);if(i.appendChild(l("div",{fontSize:F.body,color:$.value},e.message,t)),e.detail&&i.appendChild(l("div",{fontSize:F.meta,color:Y},e.detail,t)),e.actionLabel&&e.onAction){const n=l("button",me("outlined"),e.actionLabel,t);n.type="button",n.style.alignSelf="flex-start",n.style.marginTop=x.xs,G(n),n.addEventListener("click",e.onAction),i.appendChild(n)}return i}export function buildFindingsSectionWithFilter(e,t,i,n,o,u,s){if(!e.findings.length&&!s)return null;const m=e.findings.some(k=>k.severity==="unusual"||k.severity==="notable"),p=m?e.findings.filter(k=>k.severity!=="info").length.toString():void 0,r=u?.ownerDocument??document,{outer:a,content:b}=oe(`Findings (${e.findings.length})`,m||!!s,p,{stateKey:"findings",panel:u});if(s&&b.appendChild(en(s,r)),!e.findings.length)return a;b.appendChild(Zt(e.findings,n,o));const f=wt(e,n);if(!f.length)return b.appendChild(l("div",{color:Y,fontSize:F.body,textAlign:"center",padding:`${x.xxl} ${x.md}`},"No findings match the current filter.")),a;const w=buildFindingsSection(f,e,t,i,!0);return w&&b.appendChild(w),a}export function buildLotTestSection(...e){return be(buildLotTestSectionSteps(...e))}export function*buildLotTestSectionSteps(e,t,i,n,o,u,s,m="full"){const p=n?.length?{perWaferMetadata:n.map(a=>a.wafer??{}),populationLabel:`${n.length} wafer${n.length===1?"":"s"} pooled`}:void 0;let r;if(n?.length&&n.every(a=>a.stats.perTestStats!==void 0)){const a=new Map;for(const b of n)for(const f of b.stats.perTestStats??[]){const w=a.get(f.testNumber);w?(w.n+=f.count,w.sum+=f.mean*f.count,w.min=Math.min(w.min,f.min),w.max=Math.max(w.max,f.max)):a.set(f.testNumber,{n:f.count,sum:f.mean*f.count,min:f.min,max:f.max})}r={perTestStats:[...a.entries()].map(([b,f])=>({testNumber:b,count:f.n,min:f.min,max:f.max,mean:f.sum/f.n}))}}if(n?.length&&n.every(a=>a.stats.testSpecYield!==void 0)){const a=new Map;for(const b of n)for(const f of b.stats.testSpecYield??[]){const w=a.get(f.testNumber);w?(w.passDies+=f.passDies,w.totalDies+=f.totalDies):a.set(f.testNumber,{passDies:f.passDies,totalDies:f.totalDies})}r={...r,testSpecYield:[...a.entries()].map(([b,f])=>({testNumber:b,totalDies:f.totalDies,yieldPercent:f.totalDies>0?f.passDies/f.totalDies*100:null}))}}return yield*buildTestSectionSteps(e,t,i,r,o,p,u,s,m)}export function createSummaryPanelEl(e,t,i=document){const n=e==="top"||e==="bottom",o=l("div",{background:Yt,color:$.text,fontVariantNumeric:"tabular-nums",border:Ke,borderRadius:q.container,padding:x.xl,overflowY:"auto",overflowX:n?"auto":"hidden",flexShrink:"0",boxSizing:"border-box",fontFamily:F.family,fontSize:F.sub,boxShadow:He.panel},void 0,i);return e==="right"&&(o.style.marginRight=t),e==="left"&&(o.style.marginLeft=t),e==="bottom"&&(o.style.marginBottom=t),n?(o.style.height="180px",o.style.width="100%"):(o.style.width="300px",o.style.maxHeight="100%"),o}export function wrapWithSummaryPanel(e,t,i){const n=i==="top"||i==="bottom",o=l("div",{display:"flex",flexDirection:n?"column":"row",gap:x.md,width:"100%",height:"100%",flex:"1 1 0",minHeight:"0",boxSizing:"border-box",alignItems:n?"flex-start":"stretch"});return e.style.flex="1 1 0",e.style.minWidth="0",e.style.minHeight="0",i==="right"||i==="bottom"?(o.appendChild(e),o.appendChild(t)):(o.appendChild(t),o.appendChild(e)),o}function at(e){return l("div",{fontSize:F.heading,fontWeight:"600",color:ne,marginBottom:x.lg},e)}function Ce(e,t){const i=l("button",me("outlined"),e);return i.type="button",G(i),i.addEventListener("click",t),i}function rt(...e){const t=e.filter(n=>!!n);if(!t.length)return null;const i=l("div",{display:"flex",flexWrap:"wrap",gap:x.sm,marginBottom:x.lg});for(const n of t)i.appendChild(n);return i}function lt(e,t,i,n,o,u,s,m,p,r,a){const b=e.ownerDocument,f=Ye({title:"Die list",onClose:()=>{},anchor:e,ownerDocument:b});f.contentWrap.style.padding="14px 16px";const w=Wt(t,i,{...s,title:n,onSaveText:m,waferMetadata:o,metadataFields:u,extraColumn:p,getWafer:r,ringCount:a,ownerDocument:b});w&&f.contentWrap.appendChild(w)}export function renderWaferSummaryContent(...e){be(renderWaferSummaryContentSteps(...e))}export function*renderWaferSummaryContentSteps(e,t){const i=e.scrollTop;e.innerHTML="";const{wafer:n,dies:o,yieldSummary:u,dataCoverage:s,hbinDefs:m,sbinDefs:p,testDefs:r,statsSummary:a,passBins:b=[1],ringCount:f=4,binColors:w,plotMode:k,fallbackFormat:E,onFindingClick:H,activeFindingId:I=null,findingsFilter:O,onFindingsFilterChange:z,findingsNotice:C,onSaveText:d,metadataFields:v,dieListOptions:g,metadataShownElsewhere:h}=t;e.appendChild(at("Wafer Summary"));const y=t.warnings??Ae({statsSummary:a});y.length&&e.appendChild($e(y,e.ownerDocument));const S=u&&s?Ce("Summary report",()=>{_e(yt({wafer:n,dies:o,yield:u,dataCoverage:s,hbinDefs:m,sbinDefs:p,testDefs:r,passBins:b,ringCount:f},a),{anchor:e})}):null,L=(g?.enabled??!0)&&o.length?Ce("View die list",()=>{lt(e,o,r,`Die list \u2014 ${o.length} dies`,n.metadata,v,g,d,void 0,()=>n,f)}):null,A=rt(S,L);A&&e.appendChild(A);let T=!0;const R=_=>{_&&(T||e.appendChild(Ue()),T=!1,e.appendChild(_))},P=a?.stats.isLotStack?a.stats:void 0,Z=P?.lotSize!==void 0?{lotSize:P.lotSize,aggrMethod:Dt(P.aggregationMethod)}:void 0;h||R(buildMetadataInfoSection([{metadata:n.metadata??void 0}],Z)),u&&s&&R(buildYieldSection(u,s,b)),yield,a&&H&&O&&z&&(R(buildFindingsSectionWithFilter(a,H,I,O,z,e,C)),yield),R(buildBinBreakdownSection({dies:o,hbinDefs:m,sbinDefs:p,binColors:w,hardCounts:a?.stats.hardBinCounts,softCounts:a?.stats.softBinCounts,plotMode:k,passBins:b,panel:e})),yield,R(buildRegionYieldPanelSection({diesByWafer:[o],allWafers:[n],ringCount:f,passBins:b,panel:e})),yield;const J=n.metadata?{waferMetadata:n.metadata}:void 0;R(yield*buildTestSectionSteps(o,r,E,a?.stats,d,J,[{dies:o}],e,"compact")),yield,R(buildFunctionalTestSection(o,r,a?.stats.functionalYield,d,J,e)),e.appendChild(l("div",{height:"12px",flexShrink:"0"})),e.scrollTop=i}export function reportMapsFromItems(e,t,i){const n=[];return e.forEach((o,u)=>{!o?.wafer||!o.dies||n.push({...o,wafer:o.wafer,dies:o.dies,label:ve(o,u),passBins:[...Se(o,t)],ringCount:o.ringCount??i})}),n}export function renderLotSummaryContent(...e){be(renderLotSummaryContentSteps(...e))}export function*renderLotSummaryContentSteps(e,t){const i=e.scrollTop;e.innerHTML="";const{lotSummary:n,items:o,hbinDefs:u,sbinDefs:s,testDefs:m,passBins:p=[1],ringCount:r=4,binColors:a,plotMode:b,fallbackFormat:f,onFindingClick:w,activeFindingId:k=null,onWaferClick:E,findingsFilter:H,onFindingsFilterChange:I,findingsNotice:O,onSaveText:z,dieListOptions:C,findingsFor:d}=t;e.appendChild(at(`Summary \u2014 ${Tt(Ie(n.perWafer.map(N=>N.summary.wafer)))}`));const v=t.warnings??Ae({lotStatsSummary:n});v.length&&e.appendChild($e(v,e.ownerDocument));const g=Ce("Summary report",()=>{_e(Ct(reportMapsFromItems(o,p,r)),{anchor:e})}),h=[],y=[],S=[],L=[],A=[],T=new WeakMap,R=new WeakMap;for(let N=0;N<o.length;N++){yield;const V=o[N];if(!V){y.push([]);continue}V.wafer&&(h.push(V.wafer),L.push(V.dies??[]),A.push(Se(V,p)));const re=V.dies??[],c=ve(V,N);for(const W of re)T.set(W,c),V.wafer&&R.set(W,V.wafer);y.push(re);for(const W of re)S.push(W)}const P=C?.enabled??!0?Ce("View die list",()=>{S.length&<(e,S,m,`Die list \u2014 ${S.length} dies across ${o.length} wafer${o.length===1?"":"s"}`,Re(o.filter(N=>!!N).map(N=>({metadata:N.wafer?.metadata}))),o.find(N=>N?.metadataFields?.length)?.metadataFields,C,z,{label:"Wafer",get:N=>T.get(N)},N=>R.get(N),r)}):null,Z=rt(g,P);Z&&e.appendChild(Z);const J=o.map(N=>N?.statsSummary).filter(N=>!!N);let _=!0;const U=N=>{N&&(_||e.appendChild(Ue()),_=!1,e.appendChild(N))};U(buildLotOverviewSection(n,J)),yield,w&&H&&I&&(U(buildFindingsSectionWithFilter(n,w,k,H,I,e,O)),yield),U(buildPerWaferYieldSection(n,o,E,e,d)),yield,U(buildBinBreakdownSection({dies:S,hbinDefs:u,sbinDefs:s,plotMode:b,passBins:p,panel:e,binColors:a??Lt(o.flatMap(N=>N?[{dies:N.dies??[],passBins:Se(N,p)}]:[]),{hbinDefs:u,sbinDefs:s}).colors})),yield,U(buildRegionYieldPanelSection({diesByWafer:L,allWafers:h,ringCount:r,passBins:N=>A[N],panel:e})),yield,m?.length&&(U(yield*buildLotTestSectionSteps(S,m,f,J,z,y.map(N=>({dies:N})),e,"compact")),yield,U(buildLotFunctionalSection(S,m,J,z,e))),e.appendChild(l("div",{height:"12px",flexShrink:"0"})),e.scrollTop=i}
|
|
1
|
+
import{testValue as Be}from"../core/dieTable.js";import{waferDisplayLabel as fe}from"../core/waferLabel.js";import{itemRingCount as $e}from"../core/ringCount.js";import{commonPassBins as ht,itemPassBins as pe,passBinsLabel as gt,requirePassBins as ke}from"../core/passBins.js";import{isParametricTest as bt}from"../renderer/buildWaferMap.js";import{testLabel as xt,markedTestLabel as yt,unmarkedLabel as Ct,derivedFields as xe,derivedKeyText as vt,derivedCsvCell as Ee,isDerivedTest as Re,DERIVED_CSV_HEADER as Te}from"../renderer/testLabel.js";import{buildRingRegions as wt,buildQuadrantRegions as St,buildRegionYieldData as Lt}from"../stats/regions.js";import{computeFunctionalYield as Bt}from"../stats/analyzeWaferMap.js";import{buildSynthesis as De,synthesisText as $t,synthesisRowShare as kt,SYNTHESIS_SMALLER_HEADING as Et}from"../stats/synthesis.js";import{MEAN_WAFER_YIELD_LABEL as Rt,METER_DOTS as Fe,SEVERITY_MARK as Tt,filledDots as Ne,impactWord as Dt,impactShortWord as Ft,formatPoints as Nt,shortfallColor as At}from"../stats/presentation.js";import{regionYieldRows as Wt,waferYieldRows as It}from"../stats/yieldRows.js";import{formatFindingTooltip as Ae}from"../stats/findingText.js";import{arrangeFindings as Mt,filterFindings as Pt}from"../stats/filterFindings.js";import{buildFacetTable as zt,prettyKey as We}from"../stats/facets.js";import{commonMetadata as Ot}from"../stats/facets.js";import{resolveMetadataColumns as Ht}from"../stats/metadataColumns.js";import{resolveBinColors as Ie,resolveBinColorsByWafer as Yt}from"../renderer/binColors.js";import{NO_DATA_FILL as _t}from"../renderer/colorMap.js";import{classifySpec as Vt,isOutOfSpec as Kt}from"../renderer/spec.js";import{describeWaferPopulation as Me,populationLabel as jt}from"../stats/population.js";import{buildWarningsBanner as ye,collectWarnings as Pe}from"./warnings.js";export{ye as buildWarningsBanner};import{fmt as qt,fmtAggregationMethod as Gt,plainBinTerms as oe}from"../renderer/fmt.js";import{getUniqueTestNumbers as Ut}from"../renderer/buildView.js";import{describeValues as Qt}from"../stats/math.js";import{pooledTestStatsSteps as Xt}from"../stats/capability.js";import{binBreakdownRows as Zt,binBreakdownTitle as ze,binCountsFrom as Oe,totalOf as He}from"../stats/binRows.js";import{poolFunctionalYield as Jt}from"../stats/testPassRate.js";import{makeLabeledSelect as Ye,makeSegmented as Ce}from"./charts/chartShell.js";import{SHADOW as _e,MOTION as Ve,LEADING as G,TRACKING as re,wireControlHover as U,controlStyle as me,SPACE as g,RADIUS as q,FONT as D,CLR as k,sevColor as le,openModal as Ke,wireTooltip as ae}from"./toolbar.js";import{openReportModal as je}from"./guideWindow.js";import{exportCsv as qe}from"./tableExport.js";export{csvField}from"../core/utils.js";import{drain as he,maxOf as en,minOf as tn}from"../core/utils.js";import{metadataDisplayValue as nn}from"../core/metadata.js";const on=k.panelBg,Ge=`1px solid ${k.menuBorder}`,an="12px",M=k.label,X=k.value,Ue="10px";function r(e,n,i,t=document){const o=t.createElement(e);return n&&Object.assign(o.style,n),i!==void 0&&(o.textContent=i),o}function de(e){return r("div",{fontSize:Ue,fontWeight:"700",letterSpacing:re,textTransform:"uppercase",color:M,marginBottom:g.sm},e)}const Qe=new WeakMap,Xe=["regionYield","testValues","functionalTests"];function ge(e){if(!e)return{collapsed:new Set(Xe),regionMode:"ring",waferSort:"slot"};let n=Qe.get(e);return n||(n={collapsed:new Set(Xe),regionMode:"ring",waferSort:"slot"},Qe.set(e,n)),n}function ee(e,n=!0,i,t){const o=r("div"),d=ge(t?.panel);t?.stateKey&&t.panel&&(n=!d.collapsed.has(t.stateKey));const a=document.createElement("button");a.type="button",Object.assign(a.style,{display:"flex",alignItems:"center",gap:g.xs,width:"100%",background:"none",border:"none",cursor:"pointer",padding:"0 0 6px",textAlign:"left"});const b=r("span",{fontSize:D.meta,color:M,transition:`transform ${Ve.base}`,transform:n?"rotate(90deg)":"rotate(0deg)",display:"inline-block",lineHeight:G.none,marginRight:"1px"},"\u25B6"),h=r("span",{fontSize:Ue,fontWeight:"700",letterSpacing:re,textTransform:"uppercase",color:M,flex:"1"},e);if(U(a,"bare"),a.setAttribute("aria-expanded",n?"true":"false"),a.appendChild(b),a.appendChild(h),i){const y=r("span",{fontSize:D.meta,fontWeight:"700",background:k.warnBg,color:k.warnText,borderRadius:q.pill,padding:"1px 5px"},i);a.appendChild(y)}const p=r("div");p.style.display=n?"block":"none";let c=n;if(a.addEventListener("click",()=>{c=!c,p.style.display=c?"block":"none",b.style.transform=c?"rotate(90deg)":"rotate(0deg)",a.setAttribute("aria-expanded",c?"true":"false"),t?.stateKey&&t.panel&&(c?d.collapsed.delete(t.stateKey):d.collapsed.add(t.stateKey))}),t?.control){const y=r("div",{display:"flex",alignItems:"center",gap:g.sm});a.style.width="auto",a.style.flex="1",y.appendChild(a);const u=()=>{p.innerHTML="",t.render?.(p)},f=t.control(u);f&&(f.style.marginBottom="6px",f.addEventListener("click",v=>v.stopPropagation()),y.appendChild(f)),o.appendChild(y)}else o.appendChild(a);return o.appendChild(p),t?.render?.(p),{outer:o,content:p}}function Ze(){return r("div",{height:"1px",background:k.separator,margin:`${an} 0`,flexShrink:"0"})}function ve(e,n,i=k.barFill,t,o,d){const a=r("div",{marginBottom:"5px"});d&&d.step>0&&Object.assign(a.style,{background:At(d.step),borderRadius:q.container,padding:"2px 4px",marginLeft:"-4px",marginRight:"-4px"});const b=r("div",{display:"flex",justifyContent:"space-between",fontSize:D.body,color:X,marginBottom:g.xxs}),h=r("span",{color:M},e),p=r("span",{fontWeight:"600",fontVariantNumeric:"tabular-nums"},`${n.toFixed(1)}%`);d&&(p.appendChild(r("span",{fontWeight:"400",marginLeft:g.sm,color:d.step>0?X:M},Nt(d.delta))),d.step>0&&(h.style.color=X)),b.appendChild(h),b.appendChild(p);const c=t!==void 0?t:Math.min(100,Math.max(0,n)),y=i,u=r("div",{position:"relative",height:"9px",background:k.bgActive,borderRadius:q.control,overflow:"hidden"}),f=r("div",{height:"100%",width:`${c}%`,background:y,borderRadius:q.control,transition:`width ${Ve.base}`});if(u.appendChild(f),o!==void 0){const v=r("div",{position:"absolute",top:"0",bottom:"0",left:`${o}%`,width:"2px",marginLeft:"-1px",background:k.value,opacity:"0.8"});u.appendChild(v)}return a.appendChild(b),a.appendChild(u),a}function Je(){const e=r("div",{display:"flex",gap:g.sm,marginBottom:g.md});return e.finish=()=>{const n=e.lastElementChild;n&&Object.assign(n.style,{borderRight:"none",paddingRight:"0"})},e}function ce(e,n,i){const t=r("div",{padding:`${g.xxs} ${g.md} ${g.xxs} 0`,borderRight:Ge,textAlign:"center",flex:"1"}),o=r("div",{fontSize:D.heading,fontWeight:"700",color:X,lineHeight:G.tight},e),d=r("div",{fontSize:D.body,color:M,marginTop:g.xxs},n);return t.appendChild(o),t.appendChild(d),i&&t.appendChild(r("div",{fontSize:D.body,color:M,opacity:"0.85"},i)),t}function et(e,n){const i=r("div",{display:"flex",justifyContent:"space-between",fontSize:D.body,gap:g.md,marginBottom:"3px"}),t=r("span",{color:M,flexShrink:"0"},e),o=r("span",{color:X,textAlign:"right",fontWeight:"500",wordBreak:"break-all"},n);return i.appendChild(t),i.appendChild(o),i}function sn(e,n){const i=r("div",{display:"flex",fontSize:D.body,gap:g.sm,marginBottom:"3px"}),t=r("span",{color:M,flexShrink:"0"},`${e}:`),o=r("span",{color:X,fontWeight:"500",wordBreak:"break-all"},n);return i.appendChild(t),i.appendChild(o),i}export function metadataEntries(e){const n=[];for(const[i,t]of Object.entries(e)){const o=nn(t);o!==void 0&&n.push([i,o])}return n}const rn=["lot","product","testprogram","split"],we=e=>{const n=rn.indexOf(e.toLowerCase().replace(/[^a-z]/g,""));return n===-1?Number.MAX_SAFE_INTEGER:n},ln=new WeakMap,dn=12;export function buildFacetSummaryChips(e,n=3){if(!e.length)return null;const i=g.lg,t=r("div",{display:"flex",flexWrap:"nowrap",alignItems:"baseline",gap:`${g.xs} ${i}`,flex:"1 1 0%",minWidth:"0",overflow:"hidden",position:"relative"}),o=[...e].sort((f,v)=>we(f.key)-we(v.key)),d=(f,v)=>`${We(f.key)}${v<f.values.length?` (${f.values.length})`:""}: `,a=(f,v)=>f.values.slice(0,v).map(x=>x.value).join(", ")+(v<f.values.length?", \u2026":""),b=o.map(f=>{const v=r("span",{whiteSpace:"nowrap",minWidth:"0"}),x=r("span",{color:M}),L=document.createTextNode("");return v.append(x,L),{field:f,chip:v,setValues:S=>{x.textContent=d(f,S),L.data=a(f,S)},max:Math.min(f.values.length,dn)}});for(const f of b)t.appendChild(f.chip);const h=r("button",{background:"none",border:"none",padding:"0",cursor:"pointer",color:k.iconActive,fontSize:D.body,whiteSpace:"nowrap",flexShrink:"0"});h.type="button",U(h,"bare"),t.appendChild(h);const p=r("span",{position:"absolute",visibility:"hidden",whiteSpace:"nowrap",pointerEvents:"none",left:"0",top:"0"});t.appendChild(p);const c=f=>(p.textContent=f,p.getBoundingClientRect().width);let y=!1;function u(){const f=t.clientWidth;if(h.setAttribute("aria-expanded",String(y)),y){Object.assign(t.style,{flexWrap:"wrap",overflow:"visible"});for(const L of b)L.setValues(L.max),L.chip.style.display="";h.textContent="fewer fields",h.style.display="";return}Object.assign(t.style,{flexWrap:"nowrap",overflow:"hidden"});let v=0;if(f){const L=parseFloat(getComputedStyle(t).columnGap)||0;h.textContent=`${b.length} more fields`;const m=h.getBoundingClientRect().width+L;let S=0,N=!1;b.forEach((l,$)=>{if(N){l.chip.style.display="none";return}const R=v>0?L:0,T=$<b.length-1?m:0,w=f-S-R-T;let F=l.max;for(;F>1&&c(d(l.field,F)+a(l.field,F))>w;)F--;const C=c(d(l.field,F)+a(l.field,F));if(C>w&&v>0){N=!0,l.chip.style.display="none";return}l.setValues(F),l.chip.style.display="",v===0&&Object.assign(l.chip.style,{overflow:"hidden",textOverflow:"ellipsis"}),S+=R+C,v++})}else for(const L of b){const m=we(L.field.key)!==Number.MAX_SAFE_INTEGER;L.chip.style.display=m?"":"none",m&&(L.setValues(Math.min(L.field.values.length,n)),v++)}const x=b.length-v;h.textContent=`${x} more field${x===1?"":"s"}`,h.style.display=x>0?"":"none"}if(h.addEventListener("click",()=>{y=!y,u()}),u(),typeof ResizeObserver<"u"){const f=new ResizeObserver(()=>u());f.observe(t),ln.set(t,f)}return t}export function buildMetadataStripRow(e,n,i){const t=zt(e,i),o=buildFacetSummaryChips(t);if(!o&&!n)return null;const d=r("div",{display:"flex",flexWrap:"wrap",alignItems:"baseline",gap:"4px 14px",width:"100%",minWidth:"0"});if(n){const a=r("span",{fontWeight:"500",whiteSpace:"nowrap"},n.aggrMethod?`${n.lotSize} wafers stacked \xB7 ${n.aggrMethod}`:`${n.lotSize} wafers stacked`);d.appendChild(a)}return o&&d.appendChild(o),d}const pn={display:"flex",flexDirection:"column",gap:g.sm,background:k.menuBg,border:`1px solid ${k.menuBorder}`,borderRadius:q.container,padding:`${g.sm} ${g.lg}`,boxShadow:_e.panel,fontSize:D.body,lineHeight:G.none,boxSizing:"border-box",width:"100%",minWidth:"0"};export function buildMetadataStripBox(e,n,i){const t=buildMetadataStripRow(e,n,i);if(!t)return null;const o=r("div",pn);return o.appendChild(t),o}export function buildCompactMetadataRows(e){const n=metadataEntries(e);if(!n.length)return null;const i=r("div");for(const[t,o]of n)i.appendChild(sn(We(t),o));return i}export function buildMetadataInfoSection(e,n){const i=buildMetadataStripRow(e,n,{facetableOnly:e.length>1});if(!i)return null;const t=r("div");return t.appendChild(de("Wafer Info")),t.appendChild(i),t}export function buildYieldSection(e,n,i){const t=r("div");t.appendChild(de("Summary"));const o=Je();if(o.appendChild(ce(String(e.totalDies),"Total dies")),e.partialDies>0&&o.appendChild(ce(String(e.partialDies),"Partial")),e.yieldPercent!==null){const d=gt([i]);o.appendChild(ce(`${e.yieldPercent.toFixed(1)}%`,"Yield",`pass: ${d}`))}return o.finish(),t.appendChild(o),e.edgeExcludedDies>0&&t.appendChild(et("Edge excluded (outer zone)",String(e.edgeExcludedDies))),t}export function buildBinSection(e,n,i,t,o,d){const a=Oe(e,i,o);if(!a.size)return null;const b=t??Ie(e,{passBins:ke({passBins:d},"buildBinSection"),...i==="hard"?{hbinDefs:n}:{sbinDefs:n}}),h=r("div");h.appendChild(de(ze(i,He(a))));for(const p of tt(a,n,b[i],b.pass[i]))h.appendChild(p);return h}function tt(e,n,i,t){return Zt(e,n,t).map(o=>ve(`${o.label} (${o.count})`,o.percent,i.get(o.bin)??_t))}export function buildBinBreakdownSection(e){const{dies:n,hbinDefs:i,sbinDefs:t,hardCounts:o,softCounts:d,plotMode:a,passBins:b,panel:h}=e,p=e.binColors??Ie(n,{passBins:ke({passBins:b},"buildBinBreakdownSection"),hbinDefs:i,sbinDefs:t}),c=R=>Oe(n,R,R==="hard"?o:d),y=c("hard"),u=c("soft"),f=y.size>0,v=u.size>0;if(!f&&!v)return null;const x=ge(h),L=a==="softBin"||a==="stackedSoftBins"?"soft":a==="hardBin"||a==="stackedBins"?"hard":void 0;let m=x.binMode??L??(f?"hard":"soft");m==="hard"&&!f&&(m="soft"),m==="soft"&&!v&&(m="hard");const S=m==="hard"?y:u,N=m==="hard"?i:t,l=He(S),{outer:$}=ee(ze(m,l),!0,void 0,{stateKey:"bins",panel:h,render:R=>{const T=m==="hard"?p.hard:p.soft;for(const w of tt(S,N,T,p.pass[m]))R.appendChild(w)},control:f&&v&&h?()=>Ce([["hard","Hard"],["soft","Soft"]],m,R=>{x.binMode=R;const T=buildBinBreakdownSection(e);T&&$.replaceWith(T)},h.ownerDocument,!0):void 0});return $}export function buildLotBinSection(e,n,i,t){return buildBinSection(e,n,i,t)}export function buildRegionYieldPanelSection(e){const{diesByWafer:n,allWafers:i,ringCount:t,passBins:o,panel:d}=e,a=ge(d),b=c=>Lt(n,i,t,o,c==="ring"?wt:St);if(!b("ring").length&&!b("quadrant").length)return null;const h=a.regionMode,{outer:p}=ee(h==="ring"?"Ring Yield":"Quadrant Yield",!0,void 0,{stateKey:"regionYield",panel:d,render:c=>{const y=b(h);if(!y.length){c.appendChild(r("div",{fontSize:D.body,color:M},"No positioned dies for this breakdown."));return}for(const{label:u,n:f,yieldPercent:v,delta:x,step:L}of Wt(y).rows)c.appendChild(ve(`${u} (N=${f})`,v,void 0,void 0,void 0,{delta:x,step:L}))},control:d?()=>Ce([["ring","Ring"],["quadrant","Quadrant"]],h,c=>{a.regionMode=c;const y=buildRegionYieldPanelSection(e);y&&p.replaceWith(y)},d.ownerDocument,!0):void 0});return p}export function buildLotOverviewSection(e,n=[]){const i=r("div"),t=Me(e.perWafer.map(a=>a.summary.wafer));i.appendChild(de(t.lotId!==void 0?"Lot Summary":"Summary"));const o=Je();o.appendChild(ce(String(e.stats.waferCount),"Wafers"));const d=e.perWafer.map(a=>a.summary.stats.yieldPercent).filter(a=>a!==null);if(d.length){const a=d.reduce((b,h)=>b+h,0)/d.length;o.appendChild(ce(`${a.toFixed(1)}%`,Rt))}if(o.finish(),i.appendChild(o),n.length){const a=n.reduce((h,p)=>h+p.stats.analyzedDies,0),b=n.reduce((h,p)=>h+p.stats.excludedDies,0);(a||b)&&i.appendChild(et("Dies analysed",b?`${a.toLocaleString()} (${b.toLocaleString()} excluded)`:a.toLocaleString()))}return i}export function buildPerWaferYieldSection(e,n,i,t,o){const d=e.perWafer.map(m=>({waferIndex:m.waferIndex,label:fe(n[m.waferIndex],m.waferIndex).replace(/\s*·\s*\d+(\.\d+)?%$/,""),yieldPct:m.summary.stats.yieldPercent})).filter(m=>m.yieldPct!==null);if(!d.length)return null;const a=tn(d.map(m=>m.yieldPct)),b=en(d.map(m=>m.yieldPct)),h=a===b?"":` (${a.toFixed(1)}\u2013${b.toFixed(1)}%)`,{rows:p,median:c}=It(d.map(m=>({...m,yieldPercent:m.yieldPct}))),y=ge(t),u=m=>{const S=y.waferSort==="yield"?[...p].sort((N,l)=>N.yieldPct-l.yieldPct):p;for(const N of S){const{waferIndex:l,label:$,yieldPct:R,delta:T,step:w}=N,F=N.outlier?`${$} \xB7 ${N.outlier} outlier`:$,C=ve(F,R,void 0,void 0,c,{delta:T,step:w}),W=o?.(l);W?.total&&x(C,W),L(C,l,$,R,W),m.appendChild(C)}},f=` \xB7 median ${c.toFixed(1)}%`,{outer:v}=ee("Wafer Yield"+h+f,!0,void 0,{stateKey:"waferYield",panel:t,render:u,control:t?m=>Ce([["slot","Slot"],["yield","Yield"]],y.waferSort,S=>{y.waferSort=S,m()},t.ownerDocument,!0):void 0});return v;function x(m,S){const N=S.unusual?"unusual":S.notable?"notable":"info",l=r("span",{marginLeft:g.sm,flexShrink:"0",background:le(N),color:"#fff",borderRadius:q.container,padding:"0 5px",fontSize:D.body,fontWeight:"600"},`${S.total}`);l.setAttribute("aria-hidden","true"),ae(l,`${S.total} finding${S.total===1?"":"s"}`+(S.unusual?`, ${S.unusual} unusual`:"")+(S.notable?`, ${S.notable} notable`:"")),m.firstElementChild?.firstElementChild?.appendChild(l)}function L(m,S,N,l,$){i&&(m.style.cursor="pointer",m.style.borderRadius="4px",m.style.padding="2px 3px",m.style.marginLeft="-3px",m.style.marginRight="-3px",m.setAttribute("role","button"),m.setAttribute("aria-label",`${N}, ${l.toFixed(1)}% yield`+($?.total?`, ${$.total} finding${$.total===1?"":"s"}${$.unusual?`, ${$.unusual} unusual`:""}`:"")+" \u2014 open wafer"),m.tabIndex=0,m.addEventListener("mouseenter",()=>{m.style.background=k.bgHover}),m.addEventListener("mouseleave",()=>{m.style.background=""}),m.addEventListener("focus",()=>{m.style.background=k.bgHover}),m.addEventListener("blur",()=>{m.style.background=""}),m.addEventListener("click",()=>i(S)),m.addEventListener("keydown",R=>{(R.key==="Enter"||R.key===" ")&&(R.preventDefault(),i(S))}))}}function cn(e){return Qt(Float64Array.from(e))}function nt(e,n){if(!e)return[];const i=e.waferMetadata??(e.perWaferMetadata?Ot(e.perWaferMetadata.map(t=>({metadata:t}))):void 0);return!i||!Object.keys(i).length?[]:Ht({waferMetadata:i,waferKeys:"identity",waferPlacement:"csv",reservedLabels:n}).columns}function it(e){return e?.populationLabel?[{key:"population",label:"Population",scope:"wafer",constant:e.populationLabel,get:()=>e.populationLabel,csvOnly:!0}]:[]}export function buildTestSection(...e){return he(buildTestSectionSteps(...e))}export function*buildTestSectionSteps(e,n,i,t,o,d,a,b,h="full"){const p=e.filter(s=>!s.partial&&!s.edgeExcluded),c=new Map((t?.perTestStats??[]).map(s=>[s.testNumber,s])),y=new Map((t?.testSpecYield??[]).map(s=>[s.testNumber,s]));let u;if(n?.length?u=n.filter(bt).map(s=>({testNumber:s.testNumber,name:s.name,unit:s.unit,limitLow:s.limitLow,limitHigh:s.limitHigh,limitLowInclusive:s.limitLowInclusive,limitHighInclusive:s.limitHighInclusive,...xe(s)})):u=Ut(p).map(E=>({testNumber:E,name:xt(void 0,E)})),!u.length)return null;const f=u.filter(s=>p.some(E=>{const I=Be(E,s.testNumber);return I!==void 0&&isFinite(I)}));if(!f.length)return null;const v=f.some(s=>s.limitLow!==void 0||s.limitHigh!==void 0),x=[],L=s=>!!s&&s.stddev!==void 0&&s.median!==void 0&&s.q1!==void 0&&s.q3!==void 0,m=s=>s.limitLow!==void 0||s.limitHigh!==void 0,S=!!(a?.length&&n?.length),l=(S||f.some(s=>!L(c.get(s.testNumber))||m(s)&&!y.has(s.testNumber)))&&n?.length?yield*Xt(a??[{dies:e}],n):void 0;for(const s of f){yield;const E=c.get(s.testNumber),I=l?.specTally.get(s.testNumber),B=l?.stats.get(s.testNumber),P=()=>p.map(z=>Be(z,s.testNumber)).filter(z=>z!==void 0&&isFinite(z));let ne;if(L(E)){const z=E;ne={testNumber:s.testNumber,min:z.min,max:z.max,mean:z.mean,count:z.count,stddev:z.stddev,median:z.median,q1:z.q1,q3:z.q3}}else if(B)ne={testNumber:s.testNumber,...B};else{const z=P();if(!z.length)continue;ne={testNumber:s.testNumber,...cn(z)}}let O=null,_=0;if(m(s)){const z=y.get(s.testNumber);if(z)({yieldPercent:O,totalDies:_}=z);else if(I)_=I.n,O=_>0?(_-I.fail)/_*100:null;else{const ie=P();let Le=0;for(const mt of ie)Kt(Vt(mt,s))&&Le++;_=ie.length,O=ie.length>0?(ie.length-Le)/ie.length*100:null}}x.push({entry:s,stats:ne,specYieldPct:O,specN:_})}if(!x.length)return null;const $=new Map;for(const s of(S?l?.capability:void 0)??[])s.hasSpec&&$.set(s.testNumber,s.ppk);const R=$.size>0,w=new Set(x.map(s=>s.stats.count)).size===1?x[0].stats.count:null,F=r("div"),C=r("div",{display:"flex",alignItems:"center",justifyContent:"space-between",marginBottom:g.sm}),W=w!==null?` \xB7 N=${w.toLocaleString()}`:"",A=d?.populationLabel?`Test Values (${x.length}) \u2014 ${d.populationLabel}${W}`:`Test Values (${x.length})${W}`,H=de(A);H.style.marginBottom="0",C.appendChild(H);const Y=document.createElement("button");Y.type="button",Y.textContent="Test values CSV",Object.assign(Y.style,{...me("outlined")}),U(Y),Y.addEventListener("click",()=>{const s=[{header:"Test",get:B=>B.entry.name},{header:"Unit",get:B=>B.entry.unit??""},{header:"N",get:B=>B.stats.count},{header:"Min",get:B=>B.stats.min},{header:"Q1",get:B=>B.stats.q1},{header:"Median",get:B=>B.stats.median},{header:"Mean",get:B=>B.stats.mean},{header:"Q3",get:B=>B.stats.q3},{header:"Max",get:B=>B.stats.max},{header:"StdDev",get:B=>B.stats.stddev}];R&&s.push({header:"Ppk",get:B=>{const P=$.get(B.entry.testNumber);return P==null?"":P.toFixed(3)}});const E=x.some(B=>B.specYieldPct!==null&&B.specN!==B.stats.count);v&&(s.push({header:"Lo limit",get:B=>B.entry.limitLow},{header:"Hi limit",get:B=>B.entry.limitHigh},{header:"Limit Yield %",get:B=>B.specYieldPct!==null?B.specYieldPct.toFixed(1):""}),E&&s.push({header:"Limit Yield N",get:B=>B.entry.limitLow!==void 0||B.entry.limitHigh!==void 0?B.specN:""})),x.some(B=>Re(B.entry))&&s.push({header:Te,get:B=>Ee(B.entry)});const I=[...nt(d,s.map(B=>B.header)),...it(d)];qe([...I.map(B=>({header:B.label,get:()=>B.constant??""})),...s],x,"test-values.csv",o)});const Z=b===void 0;Z&&C.appendChild(Y),F.appendChild(C);const V=r("table",{width:"100%",borderCollapse:"collapse",fontSize:D.body}),J=document.createElement("thead"),se=document.createElement("tr"),K=["Test"];w===null&&K.push("N"),h==="full"&&K.push("Min","Q1","Median"),K.push("Mean"),h==="full"&&K.push("Q3","Max","StdDev"),R&&K.push("Ppk"),h==="full"&&v&&K.push("Lo limit","Hi limit"),v&&K.push("Limit yield");for(const s of K){const E=r("th",{textAlign:s==="Test"?"left":"right",fontWeight:"600",fontSize:D.meta,letterSpacing:re,textTransform:"uppercase",color:M,padding:"3px 8px 5px",borderBottom:`1px solid ${k.menuBorder}`,whiteSpace:"nowrap"},s);se.appendChild(E)}J.appendChild(se),V.appendChild(J);const Q=document.createElement("tbody");for(const{entry:s,stats:E,specYieldPct:I,specN:B}of x){const P=_=>qt(_,s.unit,i),ne=document.createElement("tr"),O=(_,z="right")=>{const ie=r("td",{textAlign:z,padding:`${g.xs} ${g.md}`,borderBottom:`1px solid ${k.menuBorder}`,color:k.text,whiteSpace:"nowrap"},_);ne.appendChild(ie)};if(O(yt(s,s.testNumber),"left"),w===null&&O(`${E.count}`),h==="full"&&(O(P(E.min)),O(P(E.q1)),O(P(E.median))),O(P(E.mean)),h==="full"&&(O(P(E.q3)),O(P(E.max)),O(P(E.stddev))),R){const _=$.get(s.testNumber);O(_==null?"\u2014":_.toFixed(2))}h==="full"&&v&&(O(s.limitLow!==void 0?P(s.limitLow):"\u2014"),O(s.limitHigh!==void 0?P(s.limitHigh):"\u2014")),v&&O(I!==null?w!==null&&B===w?`${I.toFixed(1)}%`:`${I.toFixed(1)}% (N=${B})`:"\u2014"),Q.appendChild(ne)}V.appendChild(Q);const te=r("div",{overflowX:"auto"});te.appendChild(V);const j=rt(te,x.map(s=>({label:s.entry.name,...xe(s.entry)})));if(!b)return F.appendChild(j),F;C.remove();const{outer:ue}=ee(A,!0,void 0,{stateKey:"testValues",panel:b,render:s=>s.appendChild(j),control:Z?()=>Y:void 0});return ue}export function buildFunctionalTestSection(e,n,i,t,o,d){const a=i??Bt(e,n);if(!a?.length)return null;const h=new Set(a.map(w=>w.totalDies)).size===1?a[0].totalDies:null,p=h!==null?` \xB7 N=${h.toLocaleString()}`:"",c=r("div"),y=r("div",{display:"flex",alignItems:"center",justifyContent:"space-between",marginBottom:g.sm}),u=o?.populationLabel?`Functional Tests (${a.length}) \u2014 ${o.populationLabel}${p}`:`Functional Tests (${a.length})${p}`,f=de(u);f.style.marginBottom="0",y.appendChild(f);const v=document.createElement("button");v.type="button",v.textContent="Functional CSV",Object.assign(v.style,{...me("outlined")}),U(v),v.addEventListener("click",()=>{const w=[{header:"Test",get:C=>Ct(C.label)},{header:"N",get:C=>C.totalDies},{header:"Pass",get:C=>C.passDies},{header:"Fail",get:C=>C.failDies},{header:"Pass Rate %",get:C=>C.passRatePercent!==null?C.passRatePercent.toFixed(1):""}];a.some(C=>Re(C))&&w.push({header:Te,get:C=>Ee(C)});const F=[...nt(o,w.map(C=>C.header)),...it(o)];qe([...F.map(C=>({header:C.label,get:()=>C.constant??""})),...w],a,"functional-tests.csv",t)});const x=d===void 0;x&&y.appendChild(v),c.appendChild(y);const L=r("table",{width:"100%",borderCollapse:"collapse",fontSize:D.body}),m=document.createElement("thead"),S=document.createElement("tr"),N=h===null?["Test","N","Pass","Fail","Pass rate"]:["Test","Pass","Fail","Pass rate"];for(const w of N)S.appendChild(r("th",{textAlign:w==="Test"?"left":"right",fontWeight:"700",fontSize:D.body,letterSpacing:re,textTransform:"uppercase",color:M,padding:"3px 8px 5px",borderBottom:`1px solid ${k.menuBorder}`,whiteSpace:"nowrap"},w));m.appendChild(S),L.appendChild(m);const l=document.createElement("tbody");for(const w of a){const F=document.createElement("tr"),C=(W,A="right")=>{F.appendChild(r("td",{textAlign:A,padding:`${g.xs} ${g.md}`,borderBottom:`1px solid ${k.menuBorder}`,color:X,whiteSpace:"nowrap"},W))};C(w.label,"left"),h===null&&C(String(w.totalDies)),C(String(w.passDies)),C(String(w.failDies)),C(w.passRatePercent!==null?`${w.passRatePercent.toFixed(1)}%`:"\u2014"),l.appendChild(F)}L.appendChild(l);const $=r("div",{overflowX:"auto"});$.appendChild(L);const R=rt($,a);if(!d)return c.appendChild(R),c;y.remove();const{outer:T}=ee(u,!0,void 0,{stateKey:"functionalTests",panel:d,render:w=>w.appendChild(R),control:x?()=>v:void 0});return T}export function buildLotFunctionalSection(e,n,i,t,o){const d=i?.length?{perWaferMetadata:i.map(b=>b.wafer??{}),populationLabel:`${i.length} wafer${i.length===1?"":"s"} pooled`}:void 0,a=Jt(i);return a?buildFunctionalTestSection(e,n,a,t,d,o):buildFunctionalTestSection(e,n,void 0,t,d,o)}function Se(e){const{tier:n,word:i}=Tt[e],t=Ne(n),o=r("span",{flexShrink:"0",letterSpacing:"1px",lineHeight:G.none,color:le(e)},"\u25CF".repeat(t)+"\u25CB".repeat(Fe-t));return o.setAttribute("role","img"),o.setAttribute("aria-label",i),ae(o,i),o}function ot(e,n){let i=e.summary;if(n){const t=[" has "," have "].find(o=>i.startsWith(`${n}${o}`));t?i=i.slice(n.length+t.length):i.startsWith(`${n} `)&&(i=i.slice(n.length+1))}return i=oe(i),i.length?i[0].toUpperCase()+i.slice(1):i}function at(e){return st(e.map(n=>({label:n.variable.label,...xe(n.variable)})))}function st(e){const n=vt(e);return n?r("div",{fontSize:D.body,color:M,marginTop:g.sm,lineHeight:G.base},n):null}function rt(e,n){const i=st(n);if(!i)return e;const t=r("div",{});return t.append(e,i),t}export function buildFindingsSection(e,n,i,t,o=!1,d){if(!e.length)return null;const a=e.some(l=>l.severity==="unusual"||l.severity==="notable"),b=a?e.filter(l=>l.severity!=="info").length.toString():void 0,{outer:h,content:p}=o?(()=>{const l=r("div");return{outer:l,content:l}})():ee(`Findings (${e.length})`,a,b);function c(l,$){const T={ring:"Ring",quadrant:"Quadrant",sector:"Sector","reticle-position":"Reticle","test-site":"Test site",cluster:"Cluster","edge-arc":"Edge arc",wafer:"Wafer"}[l]??l;return $.toLowerCase().startsWith(T.toLowerCase())?$:`${T}: ${$}`}const y=Mt(e),u=y.patterns.map(l=>l.finding),f=y.groups.map(l=>({...l,key:`${l.family}\0${l.left}`})),v=()=>[...f.flatMap(l=>l.findings),...u.flatMap(l=>e.filter($=>l.relatedIds?.includes($.id)))];function x(l,$=!1,R){const T=t===l.id,w=document.createElement("button");return w.type="button",w.dataset.wmapFinding=l.id,w.textContent=ot(l,R),ae(w,Ae(l)),w.setAttribute("aria-current",T?"true":"false"),Object.assign(w.style,{border:`1px solid ${k.menuBorder}`,borderLeft:`3px solid ${le(l.severity)}`,background:T?k.bgActive:k.menuBg,borderRadius:q.container,padding:$?"6px 10px":"8px 10px",textAlign:"left",fontSize:$?"10px":"11px",fontWeight:T?"600":"400",color:k.text,cursor:"pointer",width:"100%",marginBottom:g.xs}),U(w,"bare"),w.addEventListener("click",()=>i(l,w)),w}const L=r("button",{flexShrink:"0",border:"none",background:"none",fontSize:D.body,color:k.icon,cursor:"pointer",padding:"0",lineHeight:G.base,whiteSpace:"nowrap"},"Detail \u25B8");L.type="button",U(L,"bare"),L.addEventListener("click",()=>{const l=Ke({title:"Findings Summary",onClose:()=>{},anchor:L}),$=De(n,{passBins:d}),R=r("div",{fontSize:"16px",lineHeight:G.base,color:k.text,padding:`${g.xxl} ${g.xxxl} ${g.xxl}`,margin:"0",borderBottom:`1px solid ${k.menuBorder}`,flexShrink:"0",display:"flex",flexDirection:"column",gap:g.md});for(const C of $t($).split(`
|
|
2
|
+
`))R.appendChild(r("p",{margin:"0"},oe(C)));l.contentWrap.appendChild(R);const T=r("div",{overflowY:"auto",padding:`${g.xxl} ${g.xxxl}`,flex:"1"}),w=(C,W)=>{const A=r("div",{display:"flex",alignItems:"center",gap:"7px",fontSize:D.sub,fontWeight:"600",color:X,marginTop:g.lg,marginBottom:g.xs});return A.appendChild(Se(C)),A.appendChild(r("span",{},W)),A};for(const C of u){T.appendChild(w(C.severity,C.comparison.left)),T.appendChild(r("div",{fontSize:D.body,color:k.text,padding:"4px 0 4px 15px",marginBottom:g.xxs},oe(C.summary)));const W=e.filter(A=>C.relatedIds?.includes(A.id));for(const A of W)T.appendChild(r("div",{fontSize:D.body,color:M,padding:"2px 0 2px 23px",marginBottom:g.xxs},oe(A.summary)))}for(const C of f){const[W,A]=C.key.split("\0");T.appendChild(w(C.worst,c(W,A)));for(const H of C.findings)T.appendChild(r("div",{fontSize:D.body,color:k.text,padding:"4px 0 4px 15px",marginBottom:g.xxs},ot(H,A)))}const F=at(v());F&&T.appendChild(F),l.contentWrap.appendChild(T),Object.assign(l.contentWrap.style,{flexDirection:"column",overflow:"hidden"})});const m=r("div",{display:"flex",justifyContent:"flex-end",marginBottom:g.xs});m.appendChild(L),p.appendChild(m);let S=!0;for(const l of u){S||p.appendChild(r("div",{height:"1px",background:k.separator,margin:"4px 0"})),S=!1;const $=e.filter(C=>l.relatedIds?.includes(C.id)),R=$.length>0,T=r("div",{position:"relative",marginBottom:R?"2px":"4px"}),w=t===l.id,F=document.createElement("button");if(F.type="button",F.dataset.wmapFinding=l.id,Object.assign(F.style,{border:`1px solid ${k.menuBorder}`,borderLeft:`3px solid ${le(l.severity)}`,background:w?k.bgActive:k.menuBg,borderRadius:q.container,padding:"8px 32px 8px 10px",textAlign:"left",fontSize:D.body,fontWeight:w?"600":"500",color:k.text,cursor:"pointer",width:"100%"}),F.textContent=oe(l.summary),ae(F,l.summary),F.setAttribute("aria-current",w?"true":"false"),U(F,"bare"),F.addEventListener("click",()=>i(l,F)),T.appendChild(F),R){const C=r("div",{display:"none",paddingLeft:"12px",marginBottom:g.xs});for(const H of $)C.appendChild(x(H,!0));T.appendChild(C);let W=!1;const A=r("button",{position:"absolute",top:"50%",right:"8px",transform:"translateY(-50%)",border:"none",background:"none",fontSize:D.body,color:k.icon,cursor:"pointer",padding:`${g.xxs} ${g.xs}`,lineHeight:G.none},"\u25B8");U(A,"bare"),A.type="button",ae(A),A.setAttribute("aria-label","Show supporting findings"),A.setAttribute("aria-expanded","false"),A.addEventListener("click",H=>{H.stopPropagation(),W=!W,C.style.display=W?"block":"none",A.textContent=W?"\u25BE":"\u25B8";const Y=W?"Hide supporting findings":"Show supporting findings";A.setAttribute("aria-label",Y),A.setAttribute("aria-expanded",String(W))}),T.appendChild(A)}p.appendChild(T)}for(const l of f){S||p.appendChild(r("div",{height:"1px",background:k.separator,margin:"4px 0"})),S=!1;const[$,R]=l.key.split("\0"),T=r("div",{display:"flex",alignItems:"center",gap:g.sm,fontSize:D.body,fontWeight:"600",color:X,margin:"2px 0 4px"});T.appendChild(Se(l.worst)),T.appendChild(r("span",{},c($,R))),p.appendChild(T);for(const w of l.findings)p.appendChild(x(w,!1,R))}const N=at(v());return N&&p.appendChild(N),h}const un={high:"rgba(168, 65, 18, 0.10)",medium:"rgba(138, 101, 0, 0.10)",low:""},lt={high:"unusual",medium:"notable",low:"info"};function dt(e,n){const i=Ne(e),t=r("span",{display:"inline-flex",alignItems:"baseline",gap:g.xs,whiteSpace:"nowrap"}),o=r("span",{letterSpacing:"1px",color:le(lt[e])},"\u25CF".repeat(i)+"\u25CB".repeat(Fe-i));return o.setAttribute("aria-hidden","true"),t.appendChild(o),t.appendChild(r("span",{fontSize:D.meta,fontWeight:"600",letterSpacing:re,textTransform:"uppercase",color:X},n)),t}export function buildSynthesisSection(e,n,i,t,o,d){const a=De(e,{passBins:n}),b=new Map(e.findings.map(x=>[x.id,x])),{outer:h,content:p}=ee("What stands out",!0,void 0,{stateKey:"synthesis",panel:o}),c=(x,L)=>{for(const m of L){const S=m.target?b.get(m.target.id):void 0,N=oe(m.text);if(!S||!i||!N){x.appendChild(document.createTextNode(N));continue}const l=document.createElement("button");l.type="button",l.textContent=N,l.dataset.wmapFinding=S.id,l.setAttribute("aria-current",t===S.id?"true":"false"),Object.assign(l.style,{border:"none",background:"none",padding:"0",margin:"0",font:"inherit",color:"inherit",textDecoration:"underline dotted",textUnderlineOffset:"2px",cursor:"pointer",textAlign:"inherit",fontWeight:t===S.id?"600":"inherit"}),ae(l,Ae(S));const $=R=>{l.style.textDecoration=R?"underline solid":"underline dotted",l.style.color=R?k.iconActive:"inherit"};l.addEventListener("mouseenter",()=>$(!0)),l.addEventListener("mouseleave",()=>$(!1)),l.addEventListener("focus",()=>$(!0)),l.addEventListener("blur",()=>$(!1)),l.addEventListener("click",()=>i(S,l)),x.appendChild(l)}},y=(x,L)=>{const m=r("div",{fontSize:D.body,lineHeight:G.base,color:k.text,...L});return c(m,x),m};p.appendChild(y(a.headline,{fontWeight:"600",marginBottom:g.sm})),a.nothing&&p.appendChild(r("div",{fontSize:D.body,lineHeight:G.base,color:k.text,marginBottom:g.sm},a.nothing));const[u,...f]=a.items;if(u){const x=r("div",{borderLeft:`3px solid ${le(lt[u.impact])}`,background:un[u.impact]||k.bgActive,borderRadius:`0 ${q.container} ${q.container} 0`,padding:`${g.sm} ${g.md}`,marginBottom:g.sm});x.appendChild(dt(u.impact,Dt(u.impact))),x.appendChild(y(u.parts,{marginTop:g.xxs})),p.appendChild(x)}const v=[...f,...a.also?.items??[]];if(v.length){p.appendChild(r("div",{fontSize:D.meta,color:M,marginBottom:g.xxs},Et));const x=r("div",{marginBottom:g.sm});x.setAttribute("role","list");for(const m of v){const S=r("div",{display:"flex",alignItems:"baseline",gap:g.sm,fontSize:D.body,lineHeight:G.base,color:k.text});S.setAttribute("role","listitem"),S.appendChild(dt(m.impact,Ft(m.impact)));const N=r("span",{flex:"1",minWidth:"0"});c(N,[m.brief]),S.appendChild(N),S.appendChild(r("span",{color:M,fontVariantNumeric:"tabular-nums",whiteSpace:"nowrap"},kt(m))),x.appendChild(S)}const L=a.also?.more??0;L>0&&x.appendChild(r("div",{fontSize:D.meta,color:M},`and ${L} more`)),p.appendChild(x)}for(const x of a.watch??[]){const L=y(x.parts,{marginBottom:g.xs});L.insertBefore(r("span",{fontSize:D.meta,fontWeight:"600",letterSpacing:re,textTransform:"uppercase",color:M,marginRight:g.sm},"Watch"),L.firstChild),p.appendChild(L)}if(p.appendChild(r("div",{fontSize:D.body,lineHeight:G.base,color:M},oe(a.checked))),d){const x=r("button",{border:"none",background:"none",padding:"0",marginTop:g.sm,fontSize:D.body,color:k.icon,cursor:"pointer",textDecoration:"underline",textUnderlineOffset:"2px"},"Full report \u25B8");x.type="button",U(x,"bare"),x.addEventListener("click",d),p.appendChild(x)}return h}const fn=[{value:"",label:"All kinds"},{value:"yield",label:"Yield"},{value:"hardBin",label:"Hard bin"},{value:"softBin",label:"Soft bin"},{value:"test",label:"Test value"},{value:"functionalTest",label:"Functional test"},{value:"spatialPattern",label:"Spatial pattern"}],mn=[{value:"",label:"All regions"},{value:"ring",label:"Ring"},{value:"quadrant",label:"Quadrant"},{value:"reticle-position",label:"Reticle position"},{value:"test-site",label:"Test site"},{value:"wafer",label:"Wafer"},{value:"sector",label:"Sector"},{value:"cluster",label:"Cluster"},{value:"edge-arc",label:"Edge arc"},{value:"spatial-pattern",label:"Spatial pattern"}],hn=["unusual","notable","info"],gn={unusual:"Unusual",notable:"Notable",info:"Minor"};function bn(e,n,i){const t=r("div",{display:"flex",flexWrap:"wrap",alignItems:"center",gap:g.lg,marginBottom:g.lg,paddingBottom:"10px",borderBottom:`1px solid ${k.separator}`}),o={unusual:0,notable:0,info:0};for(const p of e)o[p.severity]++;const d=hn.filter(p=>o[p]>0),a=new Set(n.severity===void 0?d:Array.isArray(n.severity)?n.severity:[n.severity]),b=r("div",{display:"flex",gap:g.xs,flexWrap:"wrap"});for(const p of d){const c=document.createElement("button");c.type="button";const y=()=>{const f=a.has(p);c.setAttribute("aria-pressed",f?"true":"false"),Object.assign(c.style,{display:"inline-flex",alignItems:"center",gap:"5px",border:`1px solid ${k.menuBorder}`,borderRadius:q.pill,background:f?k.bgActive:"none",opacity:f?"1":"0.5",color:k.text,fontSize:D.body,padding:`${g.xxs} ${g.md}`,cursor:"pointer"}),c.dataset.on=f?"true":"false"},u=Se(p);u.removeAttribute("role"),u.removeAttribute("aria-label"),u.setAttribute("aria-hidden","true"),c.appendChild(u),c.appendChild(r("span",{},`${gn[p]} ${o[p]}`)),ae(c,()=>a.has(p)?"Click to hide these findings":"Click to show these findings"),y(),U(c,"bare"),c.addEventListener("click",()=>{a.has(p)?a.delete(p):a.add(p),n.severity=a.size===d.length?void 0:[...a],i()}),b.appendChild(c)}t.appendChild(b);const h=n.kind!==void 0||n.family!==void 0;return(e.length>=xn||h)&&(t.appendChild(Ye("Kind:",fn,n.kind??"",p=>{n.kind=p||void 0,i()},{maxWidth:"130px"})),t.appendChild(Ye("Region:",mn,n.family??"",p=>{n.family=p||void 0,i()},{maxWidth:"150px"}))),t}const xn=8;function yn(e,n){const i=r("div",{display:"flex",flexDirection:"column",gap:g.xs,padding:g.md,marginBottom:g.md,background:k.bgHover,borderRadius:q.control},void 0,n);if(i.appendChild(r("div",{fontSize:D.body,color:k.value},e.message,n)),e.detail&&i.appendChild(r("div",{fontSize:D.meta,color:M},e.detail,n)),e.actionLabel&&e.onAction){const t=r("button",me("outlined"),e.actionLabel,n);t.type="button",t.style.alignSelf="flex-start",t.style.marginTop=g.xs,U(t),t.addEventListener("click",e.onAction),i.appendChild(t)}return i}export function buildFindingsSectionWithFilter(e,n,i,t,o,d,a,b){if(!e.findings.length&&!a)return null;const h=e.findings.some(x=>x.severity==="unusual"||x.severity==="notable"),p=h?e.findings.filter(x=>x.severity!=="info").length.toString():void 0,c=d?.ownerDocument??document,{outer:y,content:u}=ee(`Findings (${e.findings.length})`,h||!!a,p,{stateKey:"findings",panel:d});if(a&&u.appendChild(yn(a,c)),!e.findings.length)return y;u.appendChild(bn(e.findings,t,o));const f=Pt(e,t);if(!f.length)return u.appendChild(r("div",{color:M,fontSize:D.body,textAlign:"center",padding:`${g.xxl} ${g.md}`},"No findings match the current filter.")),y;const v=buildFindingsSection(f,e,n,i,!0,b);return v&&u.appendChild(v),y}export function buildLotTestSection(...e){return he(buildLotTestSectionSteps(...e))}export function*buildLotTestSectionSteps(e,n,i,t,o,d,a,b="full"){const h=t?.length?{perWaferMetadata:t.map(c=>c.wafer??{}),populationLabel:`${t.length} wafer${t.length===1?"":"s"} pooled`}:void 0;let p;if(t?.length&&t.every(c=>c.stats.perTestStats!==void 0)){const c=new Map;for(const y of t)for(const u of y.stats.perTestStats??[]){const f=c.get(u.testNumber);f?(f.n+=u.count,f.sum+=u.mean*u.count,f.min=Math.min(f.min,u.min),f.max=Math.max(f.max,u.max)):c.set(u.testNumber,{n:u.count,sum:u.mean*u.count,min:u.min,max:u.max})}p={perTestStats:[...c.entries()].map(([y,u])=>({testNumber:y,count:u.n,min:u.min,max:u.max,mean:u.sum/u.n}))}}if(t?.length&&t.every(c=>c.stats.testSpecYield!==void 0)){const c=new Map;for(const y of t)for(const u of y.stats.testSpecYield??[]){const f=c.get(u.testNumber);f?(f.passDies+=u.passDies,f.totalDies+=u.totalDies):c.set(u.testNumber,{passDies:u.passDies,totalDies:u.totalDies})}p={...p,testSpecYield:[...c.entries()].map(([y,u])=>({testNumber:y,totalDies:u.totalDies,yieldPercent:u.totalDies>0?u.passDies/u.totalDies*100:null}))}}return yield*buildTestSectionSteps(e,n,i,p,o,h,d,a,b)}export function createSummaryPanelEl(e,n,i=document,t){const o=e==="top"||e==="bottom",d=r("div",{background:on,color:k.text,fontVariantNumeric:"tabular-nums",border:Ge,borderRadius:q.container,padding:g.xl,overflowY:"auto",overflowX:o?"auto":"hidden",flexShrink:"0",boxSizing:"border-box",fontFamily:D.family,fontSize:D.sub,boxShadow:_e.panel},void 0,i);return e==="right"&&(d.style.marginRight=n),e==="left"&&(d.style.marginLeft=n),e==="bottom"&&(d.style.marginBottom=n),o?(d.style.height="180px",d.style.width="100%"):(d.style.width="300px",d.style.maxHeight="100%"),t&&d.addEventListener("contextmenu",a=>{const b=a.target?.closest("[data-wmap-finding]");b?.dataset.wmapFinding&&(a.preventDefault(),t(b.dataset.wmapFinding,b,{x:a.clientX,y:a.clientY}))}),d}export function wrapWithSummaryPanel(e,n,i){const t=i==="top"||i==="bottom",o=r("div",{display:"flex",flexDirection:t?"column":"row",gap:g.md,width:"100%",height:"100%",flex:"1 1 0",minHeight:"0",boxSizing:"border-box",alignItems:t?"flex-start":"stretch"});return e.style.flex="1 1 0",e.style.minWidth="0",e.style.minHeight="0",i==="right"||i==="bottom"?(o.appendChild(e),o.appendChild(n)):(o.appendChild(n),o.appendChild(e)),o}function pt(e,n,i,t,o){const d=n.find(b=>b.id===i);if(!d||!o)return;t.close();const a=[...e.querySelectorAll("button[data-wmap-finding]")].find(b=>b.dataset.wmapFinding===i)??e.ownerDocument.createElement("button");o(d,a)}function ct(e){return r("div",{fontSize:D.heading,fontWeight:"600",color:X,marginBottom:g.lg},e)}function be(e,n){const i=r("button",me("outlined"),e);return i.type="button",U(i),i.addEventListener("click",n),i}function ut(...e){const n=e.filter(t=>!!t);if(!n.length)return null;const i=r("div",{display:"flex",flexWrap:"wrap",gap:g.sm,marginBottom:g.lg});for(const t of n)i.appendChild(t);return i}function ft(e,n,i){const t=e.ownerDocument;import("./dataTab.js").then(({renderDataTables:o})=>{let d;const a=n.onLocateDie,b=o({...n,doc:t,view:"dies",onLocateDie:a?(p,c)=>{d?.(),a(p,c)}:void 0}),h=Ke({title:i,onClose:()=>b.destroy(),anchor:e,ownerDocument:t,boxSize:{width:"min(96vw, 1200px)",height:"min(92vh, 780px)"}});d=()=>h.close(),h.contentWrap.appendChild(b.el)})}export function renderWaferSummaryContent(...e){he(renderWaferSummaryContentSteps(...e))}export function*renderWaferSummaryContentSteps(e,n){const i=e.scrollTop;e.innerHTML="";const{wafer:t,dies:o,yieldSummary:d,dataCoverage:a,hbinDefs:b,sbinDefs:h,testDefs:p,statsSummary:c,passBins:y,ringCount:u,binColors:f,plotMode:v,fallbackFormat:x,onFindingClick:L,activeFindingId:m=null,findingsFilter:S,onFindingsFilterChange:N,findingsNotice:l,onSaveText:$,metadataFields:R,dieListOptions:T,onLocateDie:w,metadataShownElsewhere:F}=n;e.appendChild(ct("Wafer Summary"));const C=n.warnings??Pe({statsSummary:c});C.length&&e.appendChild(ye(C,e.ownerDocument));const W=d&&a?()=>{import("../stats/renderSummaryReport.js").then(({renderWaferReportHtml:Q})=>{je(Q({wafer:t,dies:o,yield:d,dataCoverage:a,hbinDefs:b,sbinDefs:h,testDefs:p,passBins:y,ringCount:u},c,{binColors:f,live:!!L}),{anchor:e,onFinding:(te,j)=>pt(e,c?.findings??[],te,j,L)})})}:null,A=W?be("Summary report",W):null,H=(T?.enabled??!0)&&o.length?be("Data tables",()=>{ft(e,{items:[{label:t.metadata?.waferId!==void 0?String(t.metadata.waferId):"this wafer",dies:o,wafer:t,passBins:y}],testDefs:p,ringCount:u,metadataFields:R,dieListOptions:T,onSaveText:$,onLocateDie:w},`Data tables \u2014 ${o.length.toLocaleString()} dies`)}):null,Y=ut(A,H);Y&&e.appendChild(Y);let Z=!0;const V=Q=>{Q&&(Z||e.appendChild(Ze()),Z=!1,e.appendChild(Q))},J=c?.stats.isLotStack?c.stats:void 0,se=J?.lotSize!==void 0?{lotSize:J.lotSize,aggrMethod:Gt(J.aggregationMethod)}:void 0;F||V(buildMetadataInfoSection([{metadata:t.metadata??void 0}],se)),d&&a&&V(buildYieldSection(d,a,y)),yield,c&&(V(buildSynthesisSection(c,y,L,m,e,W??void 0)),yield),c&&L&&S&&N&&(V(buildFindingsSectionWithFilter(c,L,m,S,N,e,l,y)),yield),V(buildBinBreakdownSection({dies:o,hbinDefs:b,sbinDefs:h,binColors:f,hardCounts:c?.stats.hardBinCounts,softCounts:c?.stats.softBinCounts,plotMode:v,passBins:y,panel:e})),yield,V(buildRegionYieldPanelSection({diesByWafer:[o],allWafers:[t],ringCount:u,passBins:y,panel:e})),yield;const K=t.metadata?{waferMetadata:t.metadata}:void 0;V(yield*buildTestSectionSteps(o,p,x,c?.stats,$,K,[{dies:o}],e,"compact")),yield,V(buildFunctionalTestSection(o,p,c?.stats.functionalYield,$,K,e)),e.appendChild(r("div",{height:"12px",flexShrink:"0"})),e.scrollTop=i}export function reportMapsFromItems(e,n,i){const t=[];return e.forEach((o,d)=>{!o?.wafer||!o.dies||t.push({...o,wafer:o.wafer,dies:o.dies,label:fe(o,d),passBins:[...pe(o,i)],ringCount:$e(o,n)})}),t}export function renderLotSummaryContent(...e){he(renderLotSummaryContentSteps(...e))}export function*renderLotSummaryContentSteps(e,n){const i=e.scrollTop;e.innerHTML="";const{lotSummary:t,items:o,hbinDefs:d,sbinDefs:a,testDefs:b,passBins:h,ringCount:p,binColors:c,plotMode:y,fallbackFormat:u,onFindingClick:f,activeFindingId:v=null,onWaferClick:x,findingsFilter:L,onFindingsFilterChange:m,findingsNotice:S,onSaveText:N,dieListOptions:l,onLocateDie:$,findingsFor:R}=n,T=()=>p??$e(o.find(s=>!!s));e.appendChild(ct(`Summary \u2014 ${jt(Me(t.perWafer.map(s=>s.summary.wafer)))}`));const w=n.warnings??Pe({lotStatsSummary:t});w.length&&e.appendChild(ye(w,e.ownerDocument));const F=()=>{import("../stats/renderSummaryReport.js").then(({renderLotReportHtml:s})=>{je(s(reportMapsFromItems(o,p,h),{binColors:c,live:!!f}),{anchor:e,onFinding:(E,I)=>pt(e,t.findings,E,I,f)})})},C=be("Summary report",F),W=[],A=[],H=[],Y=[],Z=[],V=new WeakMap,J=new WeakMap;for(let s=0;s<o.length;s++){yield;const E=o[s];if(!E){A.push([]);continue}E.wafer&&(W.push(E.wafer),Y.push(E.dies??[]),Z.push(pe(E,h)));const I=E.dies??[],B=fe(E,s);for(const P of I)V.set(P,B),E.wafer&&J.set(P,E.wafer);A.push(I);for(const P of I)H.push(P)}const se=l?.enabled??!0?be("Data tables",()=>{if(!H.length)return;const s=o.map((E,I)=>({it:E,i:I})).filter(E=>!!E.it);ft(e,{items:s.map(({it:E,i:I})=>({label:fe(E,I),dies:E.dies??[],waferIndex:I,wafer:E.wafer,passBins:pe(E,h),statsSummary:E.statsSummary})),testDefs:b,ringCount:T(),metadataFields:o.find(E=>E?.metadataFields?.length)?.metadataFields,dieListOptions:l,onSaveText:N,onLocateDie:$},`Data tables \u2014 ${H.length.toLocaleString()} dies across ${o.length} wafer${o.length===1?"":"s"}`)}):null,K=ut(C,se);K&&e.appendChild(K);const Q=o.map(s=>s?.statsSummary).filter(s=>!!s);let te=!0;const j=s=>{s&&(te||e.appendChild(Ze()),te=!1,e.appendChild(s))};j(buildLotOverviewSection(t,Q)),yield;const ue=ht(o.map(s=>s?pe(s,h):void 0).filter(s=>s!==void 0));j(buildSynthesisSection(t,ue,f,v,e,F)),yield,f&&L&&m&&(j(buildFindingsSectionWithFilter(t,f,v,L,m,e,S,ue)),yield),j(buildPerWaferYieldSection(t,o,x,e,R)),yield,j(buildBinBreakdownSection({dies:H,hbinDefs:d,sbinDefs:a,plotMode:y,passBins:h,panel:e,binColors:c??Yt(o.flatMap(s=>s?[{dies:s.dies??[],passBins:pe(s,h)}]:[]),{hbinDefs:d,sbinDefs:a}).colors})),yield,W.length>0&&j(buildRegionYieldPanelSection({diesByWafer:Y,allWafers:W,ringCount:T(),passBins:s=>Z[s],panel:e})),yield,b?.length&&(j(yield*buildLotTestSectionSteps(H,b,u,Q,N,A.map(s=>({dies:s})),e,"compact")),yield,j(buildLotFunctionalSection(H,b,Q,N,e))),e.appendChild(r("div",{height:"12px",flexShrink:"0"})),e.scrollTop=i}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export interface SummaryRail {
|
|
2
|
+
el: HTMLButtonElement;
|
|
3
|
+
/** Show while the panel is closed and a Summary exists; `notable` tints it like the toolbar icon. */
|
|
4
|
+
sync(visible: boolean, notable: boolean): void;
|
|
5
|
+
}
|
|
6
|
+
export declare function createSummaryRail(doc: Document, side: 'left' | 'right', onOpen: () => void): SummaryRail;
|
|
7
|
+
//# sourceMappingURL=summaryRail.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{CLR as e,FONT as s,RADIUS as d,SPACE as r,wireControlHover as c,wireTooltip as m}from"./toolbar.js";export function createSummaryRail(o,n,i){const t=o.createElement("button");return t.type="button",t.dataset.wmapSummaryRail="1",t.setAttribute("aria-expanded","false"),t.textContent=n==="right"?"\u25C2 Summary":"Summary \u25B8",Object.assign(t.style,{display:"none",flexShrink:"0",alignSelf:"flex-start",writingMode:"vertical-rl",cursor:"pointer",whiteSpace:"nowrap",fontSize:s.body,fontWeight:"600",color:e.icon,background:e.menuBg,border:`1px solid ${e.menuBorder}`,borderRadius:d.control,padding:`${r.md} ${r.sm}`,position:"sticky",top:r.md,order:n==="right"?"10":"-10"}),c(t,"bare"),m(t,"Open the Summary panel: what stands out, findings and statistics"),t.addEventListener("click",i),{el:t,sync(a,l){t.style.display=a?"block":"none",t.style.color=l?e.findingIndicator:e.icon}}}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { PlotSpec } from '../stats/plotSpec.js';
|
|
2
|
+
import type { TestDef } from '../renderer/buildWaferMap.js';
|
|
3
|
+
export interface SweepEditorOptions {
|
|
4
|
+
doc: Document;
|
|
5
|
+
plot: PlotSpec;
|
|
6
|
+
/** The open lot's tests: with them, a series read from test names shows those names, and what its pattern reads. */
|
|
7
|
+
testDefs?: readonly TestDef[];
|
|
8
|
+
/** Called with the new spec after every valid change. */
|
|
9
|
+
onChange: (next: PlotSpec) => void;
|
|
10
|
+
}
|
|
11
|
+
export interface SweepEditorHandle {
|
|
12
|
+
el: HTMLElement;
|
|
13
|
+
/** The plot changed elsewhere (undo, an import): show it. */
|
|
14
|
+
setPlot(plot: PlotSpec): void;
|
|
15
|
+
}
|
|
16
|
+
export declare function createSweepEditor(o: SweepEditorOptions): SweepEditorHandle;
|
|
17
|
+
//# sourceMappingURL=sweepEditor.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{formatNumberList as T,formatTestList as D,parseNumberList as N,parseTestList as R}from"../stats/sweepText.js";import{previewXFromName as V,recordSweepTestNames as X}from"../stats/sweep.js";import{NAME_PATTERN_EXAMPLES as P,NAME_PATTERN_LIMIT as k}from"../stats/sweepXFromName.js";import{CLR as h,FONT as S,RADIUS as O,SPACE as f,controlStyle as z,wireControlHover as $}from"./toolbar.js";import{makeSegmented as A,makeToggle as W}from"./charts/chartShell.js";import{fieldRow as b,inputStyle as _}from"./plotEditor.js";const B=x=>x.xFromName!==void 0?"name":x.xValues!==void 0?"values":"order";export function createSweepEditor(x){const{doc:r}=x;let y=x.plot;const w=r.createElement("div");w.dataset.wmapSweepEditor="1",Object.assign(w.style,{display:"flex",flexDirection:"column",gap:f.lg,minWidth:"0"});const u=a=>{const e=JSON.parse(JSON.stringify(y));e.sweep=e.sweep??{series:[]},a(e),e.sweep=X(e.sweep,x.testDefs),y=e,x.onChange(e)};function g(a,e,d,t,p={}){const m=r.createElement("div");Object.assign(m.style,{display:"flex",flexDirection:"column",gap:f.xxs,minWidth:"0"});const s=r.createElement("input");s.type="text",s.value=a,s.placeholder=d,s.setAttribute("aria-label",e),Object.assign(s.style,_(),{width:"100%",...p.mono?{fontVariantNumeric:"tabular-nums"}:{}});const i=r.createElement("div");return i.setAttribute("role","alert"),Object.assign(i.style,{display:"none",fontSize:S.sub,color:h.errText}),s.addEventListener("input",()=>{const n=t(s.value);i.textContent=n??"",i.style.display=n?"":"none",s.setAttribute("aria-invalid",String(!!n)),s.style.borderColor=n?h.errText:h.menuBorder}),s.addEventListener("keydown",n=>{n.key==="Escape"&&(n.stopPropagation(),s.blur())}),m.append(s,i),{el:m,input:s}}const E=(a,e,d)=>{const t=r.createElement("button");return t.type="button",t.textContent=a,t.dataset[e]="1",Object.assign(t.style,{...z("outlined"),fontSize:S.body}),$(t),t.addEventListener("click",d),t};function j(a,e){if(a.replaceChildren(),!e)return;const{rows:d,total:t,error:p}=V(e,x.testDefs),m=(s,i)=>{const n=r.createElement("div");n.textContent=s,i&&(n.style.color=i),a.appendChild(n)};if(d.length===0){m("This series has no tests yet, so there are no names to show.");return}m(p||!e.xFromName?"The names of these tests (write a pattern with {x} where the number is):":"What the pattern reads from the first names:");for(const s of d){const i=s.name===void 0?"(not in this data)":`\u201C${s.name}\u201D`;if(p||!e.xFromName){m(`${s.test} ${i}`);continue}const n=typeof s.x=="number"?`\u2192 ${s.x}`:s.x===null?"\u2192 no match":`\u2192 unclear: \u201C${s.x.ambiguous}\u201D could be milli or mega`;m(`${s.test} ${i} ${n}`,typeof s.x=="number"?void 0:h.errText)}t>d.length&&m(`\u2026 and ${t-d.length} more`)}function L(){const a=r.createElement("details");a.dataset.wmapSweepExamples="1",Object.assign(a.style,{fontSize:S.sub,color:h.label});const e=r.createElement("summary");e.textContent="Pattern examples",Object.assign(e.style,{cursor:"pointer"}),$(e,"bare"),a.appendChild(e);const d=r.createElement("div");Object.assign(d.style,{display:"flex",flexDirection:"column",gap:f.sm,paddingTop:f.sm});for(const p of P){const m=r.createElement("div");m.textContent=`\u201C${p.name}\u201D with ${p.pattern} reads ${p.reads}`+(p.note?` (${p.note})`:""),d.appendChild(m)}const t=r.createElement("div");return t.textContent=k,t.style.fontStyle="italic",d.appendChild(t),a.appendChild(d),a}function F(a,e,d){const t=r.createElement("div");t.dataset.wmapSweepSeries=String(e),Object.assign(t.style,{display:"flex",flexDirection:"column",gap:f.md,padding:f.lg,border:`1px solid ${h.menuBorder}`,borderRadius:O.control});const p=r.createElement("div");Object.assign(p.style,{display:"flex",gap:f.md,alignItems:"center"});const m=g(a.label,`Series ${e+1} name`,`Series ${e+1}`,l=>{u(o=>{o.sweep.series[e].label=l.trim()===""?`Series ${e+1}`:l})});m.el.style.flex="1",p.appendChild(m.el),d>1&&p.appendChild(E("Remove","wmapSweepRemoveSeries",()=>{u(l=>{l.sweep.series.splice(e,1)}),v()})),t.appendChild(p);let s;const i=g(D(a.tests),`Series ${e+1} tests`,"1010, 1011, 1020..1030",l=>{const o=R(l);if(!o.ok)return o.error;u(c=>{c.sweep.series[e].tests=o.value}),s?.()},{mono:!0});i.input.dataset.wmapSweepTests=String(e),t.appendChild(b(r,"Tests, in sweep order",i.el,"Test numbers, or a range such as 1010..1030 for the tests declared between them."));const n=B(a);if(t.appendChild(b(r,"X value of each test",A([["order","Test order"],["values","Values"],["name","From test name"]],n,l=>{u(o=>{const c=o.sweep.series[e];delete c.xValues,delete c.xFromName,l==="values"&&(c.xValues=[]),l==="name"&&(c.xFromName="")}),v()},r,!0))),n==="values"){const l=g(T(a.xValues??[]),`Series ${e+1} X values`,"0, 5, 10",o=>{const c=N(o);if(!c.ok)return c.error;u(C=>{C.sweep.series[e].xValues=c.value})},{mono:!0});t.appendChild(b(r,"X values",l.el,"One per test, in the same order. A different count is reported on the chart, which then uses test order."))}else if(n==="name"){const l=g(a.xFromName??"",`Series ${e+1} name pattern`,"LRS_STATS_{x}",o=>{u(c=>{c.sweep.series[e].xFromName=o}),s?.()});if(t.appendChild(b(r,"Name pattern",l.el,"{x} reads a number from each test\u2019s name, * matches any text and ? any one character. The pattern may match anywhere in the name: \u201C@ {x}\u201D reads 0.55 from \u201CFmax @ 0.55 V\u201D.")),t.appendChild(L()),x.testDefs){const o=r.createElement("div");o.dataset.wmapSweepNames=String(e),Object.assign(o.style,{display:"flex",flexDirection:"column",gap:f.xxs,fontSize:S.sub,color:h.label,fontVariantNumeric:"tabular-nums"}),s=()=>j(o,y.sweep?.series[e]),s(),t.appendChild(o)}}else{const l=r.createElement("div");l.textContent="The X axis is the position in the run, labelled by test number. No scale between the tests is assumed.",Object.assign(l.style,{fontSize:S.sub,color:h.label}),t.appendChild(l)}return t}function v(){w.replaceChildren();const a=y.sweep??{series:[]},e=g(y.title??"","Sweep title","Sweep",i=>{u(n=>{i.trim()===""?delete n.title:Object.assign(n,{title:i})})});e.input.dataset.wmapSweepTitle="1",w.appendChild(b(r,"Title",e.el));const d=r.createElement("div");Object.assign(d.style,{display:"flex",flexDirection:"column",gap:f.md}),a.series.forEach((i,n)=>d.appendChild(F(i,n,a.series.length))),w.appendChild(d),w.appendChild(E("+ Add series","wmapSweepAddSeries",()=>{u(i=>{i.sweep.series.push({label:`Series ${i.sweep.series.length+1}`,tests:[]})}),v()}));const t=r.createElement("div");Object.assign(t.style,{display:"flex",flexDirection:"column",gap:f.md,padding:f.lg,border:`1px solid ${h.menuBorder}`,borderRadius:O.control});const p=r.createElement("div");p.textContent="Axes and measurements",Object.assign(p.style,{fontSize:S.body,fontWeight:"600",color:h.value}),t.appendChild(p);const m=(i,n,l)=>{const o=g(a[i]??"",n,l,c=>{u(C=>{c.trim()===""?delete C.sweep[i]:C.sweep[i]=c})});return b(r,n,o.el)},s=r.createElement("div");if(Object.assign(s.style,{display:"grid",gridTemplateColumns:"1fr 1fr",gap:f.md}),s.append(m("xLabel","X title","automatic"),m("xUnit","X unit","e.g. V, Hz")),t.append(s,m("yLabel","Y title","automatic")),t.appendChild(b(r,"X scale",A([["linear","Linear"],["log","Log"]],a.xScale??"linear",i=>{u(n=>{i==="log"?n.sweep.xScale="log":delete n.sweep.xScale})},r),"Log needs every X value to be positive; the chart says so otherwise.")),a.series.length>=2){t.appendChild(W("Mark where the first two series cross",a.crossing??!0,n=>{u(l=>{l.sweep.crossing=n})},r));const i=g(T(a.separationAt??[]),"Widths at levels","1.2, 2.5",n=>{const l=N(n);if(!l.ok)return l.error;u(o=>{l.value.length?o.sweep.separationAt=l.value:delete o.sweep.separationAt})},{mono:!0});t.appendChild(b(r,"Width between the first two series at these Y levels",i.el,"Optional. In the tests\u2019 own unit."))}w.appendChild(t)}return v(),{el:w,setPlot(a){y=a,v()}}}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { type CsvColumn } from '../core/tableCsv.js';
|
|
2
|
+
import { type SaveTextHandler } from './toolbar.js';
|
|
3
|
+
export type { CsvCell, CsvColumn } from '../core/tableCsv.js';
|
|
4
|
+
/** Cells (rows × columns) from which a table is written in slices as a Blob. */
|
|
5
|
+
export declare const LARGE_EXPORT_CELLS = 1000000;
|
|
6
|
+
export interface ExportCsvOptions {
|
|
7
|
+
/** Row count, when `rows` is not an array — decides small vs large. Unknown → large. */
|
|
8
|
+
rowCount?: number;
|
|
9
|
+
/** Called after each slice of a large export with the rows written so far.
|
|
10
|
+
* Never called for a small export, which finishes before it could be shown. */
|
|
11
|
+
onProgress?: (rowsWritten: number, rowCount: number | undefined) => void;
|
|
12
|
+
/** Override {@link LARGE_EXPORT_CELLS}; for tests. */
|
|
13
|
+
largeCells?: number;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Write `rows` as CSV and save it as `filename` via `onSaveText` (or the browser
|
|
17
|
+
* download). Returns `void` for a small table, which is saved before it returns,
|
|
18
|
+
* and a promise for a large one, which resolves once the host has been handed the
|
|
19
|
+
* Blob (not once the user has finished a native dialog).
|
|
20
|
+
*/
|
|
21
|
+
export declare function exportCsv<R>(columns: readonly CsvColumn<R>[], rows: Iterable<R>, filename: string, onSaveText?: SaveTextHandler, options?: ExportCsvOptions): void | Promise<void>;
|
|
22
|
+
//# sourceMappingURL=tableExport.d.ts.map
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import{csvLines as x}from"../core/tableCsv.js";import{saveTextFile as L}from"./toolbar.js";export const LARGE_EXPORT_CELLS=1e6;const m=30,C=1<<20,_=()=>new Promise(e=>setTimeout(e,0));export function exportCsv(e,t,r,s,o={}){const n=Array.isArray(t)?t.length:o.rowCount;if(!(n===void 0||n*e.length>=(o.largeCells??LARGE_EXPORT_CELLS))){L(Array.from(x(e,t)).join(`
|
|
2
|
+
`),r,"text/csv",s);return}return v(e,t,n,r,s,o)}async function v(e,t,r,s,o,n){const c=[];let i=[],l=0;const p=()=>{i.length&&c.push(new Blob([i.join("")])),i=[],l=0};let g=performance.now(),f=0,a=!0;for(const u of x(e,t)){const h=a?u:`
|
|
3
|
+
`+u;i.push(h),l+=h.length,a||f++,a=!1,l>=C&&p(),performance.now()-g>=m&&(n.onProgress?.(f,r),await _(),g=performance.now())}p(),n.onProgress?.(f,r),L(new Blob(c,{type:"text/csv"}),s,"text/csv",o)}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{fontPx as Ft}from"./toolbar.js";import{findTestDef as Oe,buildMapTitle as Ne}from"../renderer/buildView.js";import{niceStep as Ot}from"../renderer/axisTicks.js";import{sortBinsForDisplay as co}from"../stats/binPareto.js";import{affineInvert as ro,affineVector as Ee}from"../core/transforms.js";import{asList as fo,compareNatural as ho,maxOf as Ut,minOf as He}from"../core/utils.js";import{resolveValueColorFn as go}from"../renderer/colorSchemes.js";import{NO_DATA_FILL as mo}from"../renderer/colorMap.js";import{SPEC_PASS_FILL as Ye,SPEC_FAIL_LOW as uo,SPEC_FAIL_HIGH as Xe,contrastTextColor as Ge}from"../renderer/colorMap.js";import{fmt as $e,fmtColorbarAxis as we}from"../renderer/fmt.js";import{resolveCanvasTheme as po}from"./canvasTheme.js";import{hitGridDims as xo}from"./hitGrid.js";function Ve(d,e,H,p,l){d.moveTo(e,H+l*p),d.lineTo(e-p*.9,H-l*p*.6),d.lineTo(e+p*.9,H-l*p*.6),d.closePath()}const bo=new Set(["value","stackedValues","stackedBins","stackedSoftBins"]),Mo=new Set(["hardBin","softBin","metadata"]),nt=()=>`${Ft(-2)}px system-ui, sans-serif`,Ke=3,Jt=2,yo=6,To=20,So=()=>`600 ${Ft()}px system-ui, sans-serif`,qe=36,pe=()=>`${Ft(-1)}px system-ui, sans-serif`,Lo=()=>`600 ${Ft(-1)}px system-ui, sans-serif`,jt=128,ko=()=>`${Ft(-2)}px system-ui, sans-serif`,ze=4,$=17,j=11;export const BIN_LEGEND_W=110,BIN_LEGEND_W_COMPACT=64,BIN_LEGEND_ADAPT_COMPACT=280,BIN_LEGEND_ADAPT_FLOATING=180,BIN_LEGEND_MIN_CANVAS_W=150,BIN_LEGEND_MIN_CANVAS_H=120;const Zt=28,Tt=5,gt=8,Ue=15,Je=12,je=6;export function toCanvas(d,e,H={}){return drawMapCanvas(d,e,H)}export function drawMapCanvas(d,e,H={}){const p=po(d.parentElement??d),{padding:l=16,showColorbar:w=!0,markFailingDies:K=!1,colorbarWidth:g=16,background:Y=p.background,legendPosition:B="default",legendOffset:q,showAxes:z=!1,diePitchMm:C,viewport:U,activeBin:F,hoverBin:Q,afterDies:Qt,fallbackFormat:St,minRightReserve:mt,hbinDefs:Lt,sbinDefs:ft,metadataFields:kt,showTitle:st=!0}=H,Nt=fo(F),O=Math.floor(d.clientWidth||d.width),J=Math.floor(d.clientHeight||d.height),Rt=O>=BIN_LEGEND_MIN_CANVAS_W&&J>=BIN_LEGEND_MIN_CANVAS_H,ut=w&&Rt&&bo.has(e.plotMode)&&e.passFailDisplay==="off",pt=w&&Rt&&Mo.has(e.plotMode),xt=w&&Rt&&e.plotMode==="value"&&(e.passFailDisplay==="spec"&&e.specCounts!==void 0||e.passFailDisplay==="test"&&e.passFailCounts!==void 0),bt=pt||xt,Et=pt&&e.plotMode!=="metadata"&&e.binCounts?co(e.binCounts.entries(),e.plotMode==="softBin"?e.binColors.pass.soft:e.binColors.pass.hard):[],Ht=pt&&e.plotMode==="metadata"&&e.metadataCounts?[...e.metadataColorMap?.keys()??[...e.metadataCounts.keys()].sort(ho)].map(o=>[o,e.metadataCounts.get(o)??0]):[],b=[];if(xt&&e.passFailDisplay==="test"){const o=e.passFailCounts;b.push({key:-1,color:Ye,label:"Pass",compactLabel:"Pass",count:o.pass,tooltip:`Pass \xB7 ${o.pass} dies`}),b.push({key:-2,color:Xe,label:"Fail",compactLabel:"Fail",count:o.fail,tooltip:`Fail \xB7 ${o.fail} dies`})}else if(xt){const o=e.specCounts,a=Oe(e.testDefs,e.activeTest);b.push({key:-1,color:Ye,label:"Pass",compactLabel:"Pass",count:o.pass,tooltip:`Pass \xB7 ${o.pass} dies`}),a?.limitHigh!==void 0&&b.push({key:-2,color:Xe,label:"Fail high",compactLabel:"Fail hi",count:o.failHigh,tooltip:`Fail high \xB7 ${o.failHigh} dies`}),a?.limitLow!==void 0&&b.push({key:-3,color:uo,label:"Fail low",compactLabel:"Fail lo",count:o.failLow,tooltip:`Fail low \xB7 ${o.failLow} dies`})}const Z=xt?b.length:e.plotMode==="metadata"?Ht.length:Et.length,tt=(d.ownerDocument.defaultView??window).devicePixelRatio??1;if(O<=0||J<=0)return{hitTarget:{getDieAtPoint:()=>null},viewport:{originX:0,originY:0,ppm:1,snapDist:1},binLegendRows:[]};const lt=B!=="default"?B:O<BIN_LEGEND_ADAPT_FLOATING?"floating":O<BIN_LEGEND_ADAPT_COMPACT?"compact":"default",xe=lt==="default"||lt==="compact",be=lt==="left",vt=lt==="bottom",_t=lt==="top",Bt=lt==="floating",Yt=lt==="compact"||lt==="floating"?BIN_LEGEND_W_COMPACT:BIN_LEGEND_W,Me=Math.floor((J-2*l)/$),ye=Z>Me?Me-1:Z,Te=bt&&vt?ye*$:0,te=bt&&_t?ye*$:0,ee=e.plotMode==="stackedBins"||e.plotMode==="stackedSoftBins",oe=Oe(e.testDefs,e.activeTest),Se=ee?"Count":oe?.name??(e.activeTest!=null?`Test ${e.activeTest}`:void 0),Le=ee?void 0:oe?.unit,[Pt,Dt]=e.valueRange,ne=o=>String(Math.round(o)),Qe=e.logScale||e.allIntegerValues?void 0:Ot(Math.max(0,Dt-Pt)*qe/Math.max(1,J))||void 0,se=e.allIntegerValues?ne:we(Dt,Se,Le,St,Qe).edgeFmt;let le=0;if(ut){const o=d.getContext("2d");o.font=nt();const a=[se(Pt),se(Dt)];Pt<0&&a.push(se(-Math.max(Math.abs(Pt),Math.abs(Dt))));for(const u of a)le=Math.max(le,o.measureText(u).width)}const ke=Math.max(To,Math.ceil(le)+Ke+Jt+yo-l),ie=ut?g+ke+8:bt&&xe?Yt:0,ae=bt&&be?Yt:0,ce=z?32:0,Xt=z?36:0,ve=mt!=null?Math.max(ie,mt):ie,to=O-2*l-ve-Xt-ae,Ae=J-2*l-ce-Te-te;d.width=Math.round(O*tt),d.height=Math.round(J*tt);const t=d.getContext("2d");t.scale(tt,tt),t.fillStyle=Y,t.fillRect(0,0,O,J);const v=e.hoverPoints;if(!v.length)return{hitTarget:{getDieAtPoint:()=>null},viewport:{originX:0,originY:0,ppm:1,snapDist:1},binLegendRows:[]};const Gt=e.rectangles[0],re=Gt?Gt.width/2:0,fe=Gt?Gt.height/2:0,$t=e.dieBounds,Ce=($t?.minX??0)-re,eo=($t?.maxX??0)+re,oo=($t?.minY??0)-fe,Re=($t?.maxY??0)+fe,_e=eo-Ce,Be=Re-oo;let it,at,R;if(U)({originX:it,originY:at,ppm:R}=U);else{const a=e.notchDir,u=a&&a.y>.1?13:0,m=a&&a.y<-.1?13:0,h=a&&a.x<-.1?13:0,c=a&&a.x>.1?13:0,f=to-h-c,s=Ae-u-m;R=Math.min(f/_e,s/Be),it=l+Xt+ae+h+(f-_e*R)/2-Ce*R,at=l+te+u+(s-Be*R)/2+Re*R}const wt=U?.snapDist??Math.max(re,fe,1)*1.5,Vt=it+e.waferCenter.x*R,no=at-e.waferCenter.y*R,he=e.waferRadius*R,ht=o=>{const a=Math.abs(o-no);return a>=he?0:Math.sqrt(he*he-a*a)},Mt=(o,a,u,m,h,c,f=So(),s=p.text)=>{if(!o)return;t.save(),t.fillStyle=s,t.font=f,t.textAlign=m,t.textBaseline=h;const i=m==="right"?a-c:c-a;let n=o;if(i>8&&t.measureText(n).width>i){for(;n.length>1&&t.measureText(n+"\u2026").width>i;)n=n.slice(0,-1);n=n+"\u2026"}i>8&&t.fillText(n,a,u),t.restore()};t.save(),t.setTransform(R*tt,0,0,-R*tt,it*tt,at*tt);const yt=1/R,so=1/(R*tt);if(e.rectangles.length>0){const o=new Map;for(const h of e.rectangles){const c=String(h.fill);let f=o.get(c);f||(f=[],o.set(c,f)),f.push(h)}for(const[h,c]of o){t.fillStyle=h;for(let f=0;f<c.length;f+=PATH_SHAPES){t.beginPath();const s=Math.min(c.length,f+PATH_SHAPES);for(let i=f;i<s;i++){const n=c[i];t.rect(n.x-n.width/2,n.y-n.height/2,n.width,n.height)}t.fill()}}t.strokeStyle="rgba(0,0,0,0.18)",t.lineWidth=.5*so,t.lineCap="square";const a=Ze.get(e.rectangles)??outlineSegments(e.rectangles);Ze.set(e.rectangles,a);const u=PATH_SHAPES*4;for(let h=0;h<a.length;h+=u){t.beginPath();const c=Math.min(a.length,h+u);for(let f=h;f<c;f+=4)t.moveTo(a[f],a[f+1]),t.lineTo(a[f+2],a[f+3]);t.stroke()}if(t.lineCap="butt",K){const h=e.rectangles.filter(c=>c.binFail);if(h.length>0){const c=new Map;for(const f of h){const s=Ge(String(f.fill));let i=c.get(s);i||(i=[],c.set(s,i)),i.push(f)}t.save(),t.lineCap="butt";for(const[f,s]of c){t.strokeStyle=f,t.globalAlpha=.55,t.lineWidth=Math.max(.6*yt,Math.min(s[0].width,s[0].height)*.1);for(const i of s){const n=i.width/2,M=i.height/2,N=Math.max(1.6*yt,Math.min(i.width,i.height)/3);t.save(),t.beginPath(),t.rect(i.x-n,i.y-M,i.width,i.height),t.clip(),t.beginPath();for(let A=-(n+M);A<=n+M;A+=N)t.moveTo(i.x-n+A,i.y-M),t.lineTo(i.x-n+A+i.height,i.y+M);t.stroke(),t.restore()}}t.restore()}}const m=e.rectangles.filter(h=>h.specMark);if(m.length>0){const h=new Map;for(const f of m){const s=Ge(String(f.fill)),i=`${f.specMark}|${s}`;let n=h.get(i);n||(n=[],h.set(i,n)),n.push(f)}const c=2.5*yt;t.lineJoin="round";for(const[f,s]of h){const[i,n]=f.split("|"),M=n==="#000000"?"#ffffff":"#000000",N=i==="failHigh"?1:-1,A=Math.min(s[0].width,s[0].height),L=Math.max(2.5*yt,A*.3);t.beginPath();for(const V of s)Ve(t,V.x,V.y,L,N);t.strokeStyle=M,t.lineWidth=c,t.stroke(),t.fillStyle=n,t.fill()}}}for(const o of e.overlays){t.beginPath();for(const a of o.points){for(let u=0;u<a.length;u++)u===0?t.moveTo(a[u].x,a[u].y):t.lineTo(a[u].x,a[u].y);o.closed&&t.closePath()}o.fill&&!o.fill.startsWith("rgba(0,0,0,0)")&&(t.fillStyle=o.fill,t.fill()),o.kind==="ring-boundary"||o.kind==="quadrant-boundary"||o.kind==="reticle"?(t.strokeStyle="rgba(0,0,0,0.45)",t.lineWidth=3*yt,t.stroke(),t.strokeStyle="rgba(255,255,255,0.8)",t.lineWidth=1*yt,t.stroke()):(t.strokeStyle=o.lineColor,t.lineWidth=o.lineWidth*yt,t.stroke())}if(t.restore(),e.notchDir){const o=it+e.waferCenter.x*R,a=at-e.waferCenter.y*R,u=e.notchDir.x,m=-e.notchDir.y,h=e.waferRadius*R,c=4,f=9,s=5,i=o+u*(h+c),n=a+m*(h+c),M=i+u*f,N=n+m*f,A={x:-m,y:u},L=l+Xt+ae,V=O-l-ie,et=l+te,rt=J-l-Te-ce;t.save(),t.beginPath(),t.rect(L,et,V-L,rt-et),t.clip(),t.beginPath(),t.moveTo(i,n),t.lineTo(M+A.x*s,N+A.y*s),t.lineTo(M-A.x*s,N-A.y*s),t.closePath(),t.fillStyle=p.text,t.fill(),t.restore()}t.save(),t.textAlign="center",t.textBaseline="middle";let Pe=0;if(e.rectangles.length>0){let o=1;for(const c of e.texts)c.role!=="indicator"&&c.text.length>o&&(o=c.text.length);const a=e.rectangles[0].width*R,u=e.rectangles[0].height*R,m=Math.min(a,u),h=a/o;Pe=Math.max(8,Math.min(64,Math.round(Math.min(m*.55,h*1.8))))}for(const o of e.texts){const a=it+o.x*R,u=at-o.y*R;t.font=`${o.role==="indicator"?o.fontSize:Pe}px system-ui, sans-serif`,t.fillStyle=o.color,t.fillText(o.text,a,u)}if(t.restore(),Qt?.(t,{originX:it,originY:at,ppm:R},{background:Y,text:p.text}),z&&Ao(t,O,J,it,at,R,l,ce,Xt,C,e.gridToScreen,e.rotation,p),ut){const o=go(e.valueColorScheme,e.reverseValueScheme),a=ke,u=Ae,m=Math.round(u*.75),h=l+Math.round((u-m)/2),c=O-l-g-a,f=Pt,s=Dt,i=s-f,n=e.plotMode==="stackedSoftBins"?ft:Lt,{primary:M,secondary:N,note:A}=st?Ne(e,St,n):{primary:"",secondary:"",note:void 0};if(st){const r=Math.max(h-6,l+11),W=Vt+Math.max(ht(r),ht(r-12))+8;Mt(M,O-l,r,"right","bottom",W)}let L=h+m+4;const V=r=>Vt+Math.max(ht(r),ht(r+12))+8,et=e.logScale&&f>0?Math.log10(f):0,rt=e.logScale&&s>0?Math.log10(s)-et:1,P=r=>e.logScale?(1-(Math.log10(r)-et)/rt)*m:(1-(r-f)/i)*m;for(let r=0;r<jt;r++){const W=1-r/(jt-1),E=h+r/jt*m,x=m/jt+1;t.fillStyle=o(W),t.fillRect(c,E,g,x)}t.strokeStyle="rgba(0,0,0,0.25)",t.lineWidth=.5,t.strokeRect(c,h,g,m),t.fillStyle=p.text,t.font=nt(),t.textAlign="left",t.strokeStyle="rgba(0,0,0,0.35)",t.lineWidth=.5;const D=Ke,Wt=qe,ct=14;let X;const I=e.logScale&&rt>0?Co(f,s,m,Wt,ct,et,rt):e.allIntegerValues?(()=>{const r=Math.max(1,Ot(i*Wt/m)),W=[],E=Math.ceil(f/r)*r;for(let x=E;x<=s+r*1e-6;x+=r){const _=(1-(x-f)/i)*m;_>ct&&_<m-ct&&W.push(Math.round(x))}return W})():(()=>{const r=i>0?Ot(i*Wt/m):0,W=[];if(r>0){X=r;const E=Math.ceil(f/r)*r;for(let x=E;x<=s+r*1e-6;x+=r){const _=(1-(x-f)/i)*m;_>ct&&_<m-ct&&W.push(x)}}return W})(),y=oe,k=ee,T=we(s,Se,Le,St,X),G=e.allIntegerValues?ne:T.tickFmt,S=e.allIntegerValues?ne:T.edgeFmt;t.textBaseline="middle";for(const r of I){const W=h+P(r);t.beginPath(),t.moveTo(c+g,W),t.lineTo(c+g+D,W),t.stroke(),t.fillText(G(r),c+g+D+Jt,W)}if(t.beginPath(),t.moveTo(c+g,h),t.lineTo(c+g+D,h),t.stroke(),t.textBaseline="top",t.fillText(S(s),c+g+D+Jt,h),t.beginPath(),t.moveTo(c+g,h+m),t.lineTo(c+g+D,h+m),t.stroke(),t.textBaseline="bottom",t.fillText(S(f),c+g+D+Jt,h+m),!k&&y&&(y.limitLow!==void 0||y.limitHigh!==void 0)){const r=[],W=e.colorbarRangeMode==="spec";for(const[E,x]of[[y.limitLow,"Lo limit"],[y.limitHigh,"Hi limit"]]){if(E===void 0||e.logScale&&E<=0)continue;const _=P(E);!W&&(_<-ct||_>m+ct)||r.push({value:E,label:x,py:Math.max(0,Math.min(m,_))})}if(r.length>0){t.save(),t.font=nt(),t.textAlign="right",t.textBaseline="middle";for(const{py:E,label:x}of r){const _=h+E;W||(t.strokeStyle="rgba(255,255,255,0.85)",t.lineWidth=3,t.beginPath(),t.moveTo(c,_),t.lineTo(c+g,_),t.stroke(),t.strokeStyle="rgba(0,0,0,0.7)",t.lineWidth=1,t.beginPath(),t.moveTo(c,_),t.lineTo(c+g,_),t.stroke()),t.fillStyle=p.text,t.fillText(x,c-3,_);const At=x==="Hi limit"?-1:1,Ct=4,zt=c-3-t.measureText(x).width-Ct-4;t.beginPath(),Ve(t,zt,_,Ct,At),t.strokeStyle="#fff",t.lineWidth=2,t.lineJoin="round",t.stroke(),t.fillStyle=p.text,t.fill()}t.restore()}}if(!k){const r=e.logScale?"log\u2081\u2080":e.logScaleRequested?"linear \u2014 log n/a":null;r&&(t.save(),t.fillStyle=p.text,t.font=Lo(),t.textAlign="right",t.textBaseline="top",t.fillText(r,O-l,L),t.restore(),L+=15)}st&&N&&(Mt(N,O-l,L,"right","top",V(L),pe(),p.text),L+=15),st&&A&&Mt(A,O-l,L,"right","top",V(L),pe(),p.text)}const de=[];let ot;if(bt){const o=lt==="compact",a=e.plotMode==="softBin"?ft:Lt,u=a?new Map(a.map(n=>[n.bin,n])):null,m=e.plotMode==="softBin"?e.binColors.soft:e.binColors.hard,h=n=>m.get(n)??mo,c=(n,M)=>{const N=t.measureText(n).width,A=o?0:t.measureText(String(M)).width,L=j+Tt+N+(A?Zt:0);return{labelWidth:N,countWidth:A,totalWidth:L}},f=kt?.find(n=>n.key===e.activeMetadataKey),s=xt?b.map(n=>{const M=o?n.compactLabel:n.label;return{key:n.key,color:n.color,count:n.count,label:M,tooltipLabel:n.tooltip,...c(M,n.count)}}):e.plotMode==="metadata"?Ht.map(([n,M])=>{const A=f?.values?.find(et=>et.value===n)?.label??n,L=o?n:A,V=e.metadataColorMap?.get(n)??"#d6d9dd";return{key:n,color:V,count:M,label:L,tooltipLabel:`${A} \xB7 ${M} dies`,...c(L,M)}}):Et.map(([n,M])=>{const N=u?.get(n),A=N?.name?`${n} \xB7 ${N.name}`:`Bin ${n}`,L=o?String(n):A;return{key:n,color:h(n),count:M,label:L,tooltipLabel:`${A} \xB7 ${M} dies`,...c(L,M)}}),i=O-l*2;if(s.length>0){const n=Bt?gt*2+(st?Ue:0):st&&!_t?Je+je:0,M=Math.max(1,Math.floor((J-2*l-n)/$));let N=s.length,A=1,L=N*$,V=[],et=0;const rt=vt||_t;if(rt){const X=Ut(s.map(y=>y.totalWidth)),I=Math.max(1,Math.min(s.length,Math.floor((i+8)/(X+8))));for(let y=I;y>=1;y--){const k=Math.ceil(s.length/y);if(k>M)continue;const T=new Array(y).fill(0);for(let S=0;S<s.length;S++){const r=S%y;T[r]=Math.max(T[r],s[S].totalWidth)}if(T.reduce((S,r)=>S+r,0)+(y-1)*8<=i||y===1){A=y,V=T,N=k,L=k*$;break}}}else N=Math.min(s.length,M),L=N*$,s.length>M&&(et=s.length-(M-1)),V=[Yt];let P,D;if(Bt){const X=Ut(s.map(r=>j+Tt+t.measureText(r.label).width+Zt)),I=Math.ceil(X);V=[I];const y=st?Ue:0,k=I+gt*2,T=L+y+gt*2,G=q?.x??0,S=q?.y??0;P=Math.min(Math.max(O-l-k+G,l),O-l-k),D=Math.min(Math.max(J-l-T+S,l),J-l-T),ot={x:P,y:D,w:k,h:T},t.save(),t.globalAlpha=.92,t.fillStyle=p.surface,t.fillRect(P,D,k,T),t.globalAlpha=1,t.strokeStyle=p.axisLine,t.lineWidth=1,t.strokeRect(P,D,k,T),t.restore(),P+=gt,D+=gt+y}else vt?(P=l,D=J-l-L):_t?(P=l,D=l):be?(P=l+4,D=Math.max(l+Math.round((J-2*l-L)/2),l+n)):(P=O-l-Yt+4,D=Math.max(l+Math.round((J-2*l-L)/2),l+n));if(st){const X=e.plotMode==="softBin"?ft:Lt,{primary:I,secondary:y,note:k}=Ne(e,St,X),T=je,G=[y,k].filter(x=>!!x),S=(x,_,At)=>G.forEach((Ct,zt)=>Mt(Ct,x,_+zt*16,"left","top",At(_+zt*16),pe(),p.text)),r=x=>Vt-Math.max(ht(x),ht(x-12))-8,W=x=>xe?O-l:r(x),E=Bt?ot.y+ot.h:D+L;if(_t){const x=E+T;Mt(I,P,x,"left","top",Vt-Math.max(ht(x),ht(x+12))-8),S(P,x+16,r)}else if(Bt)Mt(I,ot.x+gt,ot.y+gt,"left","top",ot.x+ot.w-gt),S(ot.x,E+T,()=>ot.x+ot.w);else{const x=Math.max(D-T,l+Je);Mt(I,P,x,"left","bottom",W(x)),S(P,E+T,W)}}t.save(),t.font=nt();const Wt=rt?8:0,ct=(X,I)=>{if(t.measureText(X).width<=I)return X;let y=0,k=X.length;const T="\u2026",G=t.measureText(T).width;for(;y<k;){const S=y+k+1>>1;t.measureText(X.slice(0,S)).width+G<=I?y=S:k=S-1}return y===0?T:X.slice(0,y)+T};if(rt){let X=0;for(let I=0;I<N;I++){let y=P;for(let k=0;k<A;k++){const T=s[X++];if(!T)break;const G=Nt.includes(T.key),S=!G&&T.key===Q,r=y,W=y+j+Tt,E=D+I*$+$/2,x=D+I*$+Math.round(($-j)/2),_=V[k]-j-Tt-Zt;S&&(t.fillStyle=p.hoverRow,t.fillRect(y,D+I*$,V[k],$)),t.fillStyle=T.color,t.fillRect(r,x,j,j),t.strokeStyle=G?p.accent:"rgba(0,0,0,0.25)",t.lineWidth=G?2:.75,t.strokeRect(r,x,j,j),t.fillStyle=G?p.accent:p.text,t.font=G?`bold ${nt()}`:nt(),t.textAlign="left",t.textBaseline="middle",t.fillText(ct(T.label,_),W,E),t.fillStyle=p.textMuted,t.font=nt(),t.textAlign="right",t.fillText(String(T.count),y+V[k]-2,E),de.push({bin:T.key,x:y,y:D+I*$,w:V[k],h:$,label:T.tooltipLabel}),y+=V[k]+Wt}}}else{const X=V[0],I=lt!=="compact",y=X-j-Tt-(I?Zt:0),k=P+X-2;let T=s;et>0&&(T=s.slice(0,M-1));let G=D;for(const S of T){const r=Nt.includes(S.key),W=!r&&S.key===Q,E=P,x=P+j+Tt,_=G+$/2,At=G+Math.round(($-j)/2),Ct=S.label;W&&(t.fillStyle=p.hoverRow,t.fillRect(P-3,G,X+3,$)),t.fillStyle=S.color,t.fillRect(E,At,j,j),t.strokeStyle=r?p.accent:"rgba(0,0,0,0.25)",t.lineWidth=r?2:.75,t.strokeRect(E,At,j,j),t.fillStyle=r?p.accent:p.text,t.font=r?`bold ${nt()}`:nt(),t.textAlign="left",t.textBaseline="middle",t.fillText(ct(Ct,y),x,_),(I||Bt)&&(t.fillStyle=p.textMuted,t.font=nt(),t.textAlign="right",t.fillText(String(S.count),k,_)),de.push({bin:S.key,x:P,y:G,w:X,h:$,label:S.tooltipLabel}),G+=$}et>0&&(t.fillStyle=p.textMuted,t.font=nt(),t.textAlign="left",t.textBaseline="middle",t.fillText(`+ ${et} more`,P+j+Tt,G+$/2))}}s.length>0&&t.restore()}const lo={originX:it,originY:at,ppm:R,snapDist:wt},De=e.rectangles[0]?.width??1,Ie=e.rectangles[0]?.height??1,dt=e.dieBounds,Kt=dt?dt.minX:v.length?He(v.map(o=>o.x)):0,qt=dt?dt.minY:v.length?He(v.map(o=>o.y)):0,io=dt?dt.maxX:v.length?Ut(v.map(o=>o.x)):1,ao=dt?dt.maxY:v.length?Ut(v.map(o=>o.y)):1,{cellW:ge,cellH:me,nCols:It,nRows:ue}=xo(io-Kt,ao-qt,De*1.5,Ie*1.5,v.length),We=Array.from({length:It*ue},()=>[]);for(let o=0;o<v.length;o++){const a=Math.floor((v[o].x-Kt)/ge),u=Math.floor((v[o].y-qt)/me),m=Number.isFinite(a)?Math.max(0,Math.min(It-1,a)):0,h=Number.isFinite(u)?Math.max(0,Math.min(ue-1,u)):0;We[h*It+m].push(o)}function Fe(o,a,u){const m=Math.max(0,Math.floor((o-u-Kt)/ge)),h=Math.min(It-1,Math.floor((o+u-Kt)/ge)),c=Math.max(0,Math.floor((a-u-qt)/me)),f=Math.min(ue-1,Math.floor((a+u-qt)/me)),s=[];for(let i=c;i<=f;i++)for(let n=m;n<=h;n++)for(const M of We[i*It+n])s.push(M);return s}return{hitTarget:{getDieAtPoint(o,a){const u=(o-it)/R,m=(at-a)/R,h=Math.max(De,Ie);for(const s of Fe(u,m,h)){const i=e.rectangles[s];if(i&&Math.abs(u-i.x)<=i.width/2&&Math.abs(m-i.y)<=i.height/2)return e.dies[s]??null}let c=null,f=wt*wt;for(const s of Fe(u,m,wt)){const i=v[s].x-u,n=v[s].y-m,M=i*i+n*n;M<f&&(f=M,c=e.dies[s]??null)}return c}},viewport:lo,binLegendRows:de,legendBox:ot}}function Ao(d,e,H,p,l,w,K,g,Y,B,q,z,C){d.save(),d.font=ko(),d.fillStyle=C.text,d.strokeStyle=C.axisLine,d.lineWidth=.5;const U=H-g+4,F=K+Y-4,Q=ro(q),Qt=Ee(Q,1,0),St=Ee(Q,0,1),mt=1e-9;function Lt(b){return B?Math.abs(b.y)<mt&&Math.abs(b.x)>mt?{pitch:B.x,sign:Math.sign(b.x)}:Math.abs(b.x)<mt&&Math.abs(b.y)>mt?{pitch:B.y,sign:Math.sign(b.y)}:null:null}const ft=Lt(Qt),kt=Lt(St);function st(b){return ft?Math.round(b*ft.sign/ft.pitch):b}function Nt(b){return kt?Math.round(b*kt.sign/kt.pitch):b}function O(b){if(!b)return Ot(50/w);const Z=50/(b*w);return Math.max(1,Math.round(Z))*b}const J=ft?.pitch,Rt=kt?.pitch,ut=O(J),pt=O(Rt);d.textAlign="center",d.textBaseline="top";const xt=Math.ceil((K-p)/w/ut)*ut,bt=(e-K-p)/w;for(let b=xt;b<=bt;b+=ut){const Z=p+b*w;if(Z<K||Z>e-K)continue;d.beginPath(),d.moveTo(Z,U-ze),d.lineTo(Z,U),d.stroke();const tt=B?String(st(b)):$e(b);d.fillText(tt,Z,U+2)}d.textAlign="right",d.textBaseline="middle";const Et=Math.ceil((l-(H-K))/w/pt)*pt,Ht=(l-K)/w;for(let b=Et;b<=Ht;b+=pt){const Z=l-b*w;if(Z<K||Z>H-K)continue;d.beginPath(),d.moveTo(F,Z),d.lineTo(F+ze,Z),d.stroke();const tt=B?String(Nt(b)):$e(b);d.fillText(tt,F-2,Z)}d.restore()}function Co(d,e,H,p,l,w,K){const g=[],Y=K;if(Y<1){const C=Ot((e-d)*p/H);if(C>0){const U=Math.ceil(d/C)*C;for(let F=U;F<=e+C*1e-6;F+=C){if(F<=d||F>=e)continue;const Q=(1-(Math.log10(F)-w)/K)*H;Q>l&&Q<H-l&&g.push(F)}}return g}const B=Y<3?[1,2,5]:[1],q=Math.floor(Math.log10(d)),z=Math.ceil(Math.log10(e));for(let C=q;C<=z;C++)for(const U of B){const F=U*Math.pow(10,C);if(F<=d||F>=e)continue;const Q=(1-(Math.log10(F)-w)/K)*H;Q>l&&Q<H-l&&g.push(F)}return g}export{fmt,fmtColorbarAxis}from"../renderer/fmt.js";export const PATH_SHAPES=256;const Ze=new WeakMap;export function outlineSegments(d){const e=g=>Math.round(g*1e6),H=new Map,p=new Map,l=(g,Y,B,q)=>{const z=e(Y);let C=g.get(z);C||g.set(z,C={at:Y,spans:[]}),C.spans.push([B,q])};for(const g of d){const Y=g.x-g.width/2,B=g.x+g.width/2,q=g.y-g.height/2,z=g.y+g.height/2;l(H,q,Y,B),l(H,z,Y,B),l(p,Y,q,z),l(p,B,q,z)}const w=[],K=(g,Y)=>{for(const{at:B,spans:q}of g.values()){q.sort((U,F)=>U[0]-F[0]);let[z,C]=q[0];for(let U=1;U<q.length;U++){const[F,Q]=q[U];e(F)<=e(C)?Q>C&&(C=Q):(Y(B,z,C),z=F,C=Q)}Y(B,z,C)}};return K(H,(g,Y,B)=>w.push(Y,g,B,g)),K(p,(g,Y,B)=>w.push(g,Y,g,B)),Float64Array.from(w)}
|
|
1
|
+
import{fontPx as Nt}from"./toolbar.js";import{findTestDef as Oe,buildMapTitle as Ne}from"../renderer/buildView.js";import{niceStep as Et,compactTickIndices as ro}from"../renderer/axisTicks.js";import{sortBinsForDisplay as fo}from"../stats/binPareto.js";import{affineInvert as ho,affineVector as Ee}from"../core/transforms.js";import{asList as go,compareNatural as mo,maxOf as Qt,minOf as He}from"../core/utils.js";import{resolveValueColorFn as uo}from"../renderer/colorSchemes.js";import{NO_DATA_FILL as po}from"../renderer/colorMap.js";import{SPEC_PASS_FILL as Ye,SPEC_FAIL_LOW as xo,SPEC_FAIL_HIGH as Xe,contrastTextColor as Ge}from"../renderer/colorMap.js";import{fmt as $e,fmtColorbarAxis as we}from"../renderer/fmt.js";import{resolveCanvasTheme as bo}from"./canvasTheme.js";import{hitGridDims as Mo}from"./hitGrid.js";function Ve(h,e,P,u,s){h.moveTo(e,P+s*u),h.lineTo(e-u*.9,P-s*u*.6),h.lineTo(e+u*.9,P-s*u*.6),h.closePath()}const yo=new Set(["value","stackedValues","stackedBins","stackedSoftBins"]),To=new Set(["hardBin","softBin","metadata"]),lt=()=>`${Nt(-2)}px system-ui, sans-serif`,Ke=3,vt=2,So=6,ko=20,Lo=()=>`600 ${Nt()}px system-ui, sans-serif`,qe=36,be=()=>`${Nt(-1)}px system-ui, sans-serif`,Ao=()=>`600 ${Nt(-1)}px system-ui, sans-serif`,te=128,Co=()=>`${Nt(-2)}px system-ui, sans-serif`,ze=4,V=17,Z=11;export const BIN_LEGEND_W=110,BIN_LEGEND_W_COMPACT=64,BIN_LEGEND_ADAPT_COMPACT=280,BIN_LEGEND_ADAPT_FLOATING=180,BIN_LEGEND_MIN_CANVAS_W=150,BIN_LEGEND_MIN_CANVAS_H=120;const ee=28,Lt=5,bt=8,Ue=15,Je=12,je=6;export function toCanvas(h,e,P={}){return drawMapCanvas(h,e,P)}export function drawMapCanvas(h,e,P={}){const u=bo(h.parentElement??h),{padding:s=16,showColorbar:C=!0,markFailingDies:z=!1,colorbarWidth:g=16,background:Y=u.background,legendPosition:D="default",legendOffset:U,showAxes:J=!1,diePitchMm:R,viewport:X,activeBin:N,hoverBin:Q,afterDies:Ht,fallbackFormat:At,minRightReserve:Yt,hbinDefs:mt,sbinDefs:Ct,metadataFields:Xt,showTitle:tt=!0}=P,ut=go(N),G=Math.floor(h.clientWidth||h.width),j=Math.floor(h.clientHeight||h.height),Rt=G>=BIN_LEGEND_MIN_CANVAS_W&&j>=BIN_LEGEND_MIN_CANVAS_H,Pt=C&&Rt&&yo.has(e.plotMode)&&e.passFailDisplay==="off",Dt=C&&Rt&&To.has(e.plotMode),ft=C&&Rt&&e.plotMode==="value"&&(e.passFailDisplay==="spec"&&e.specCounts!==void 0||e.passFailDisplay==="test"&&e.passFailCounts!==void 0),ht=Dt||ft,Gt=Dt&&e.plotMode!=="metadata"&&e.binCounts?fo(e.binCounts.entries(),e.plotMode==="softBin"?e.binColors.pass.soft:e.binColors.pass.hard):[],$t=Dt&&e.plotMode==="metadata"&&e.metadataCounts?[...e.metadataColorMap?.keys()??[...e.metadataCounts.keys()].sort(mo)].map(o=>[o,e.metadataCounts.get(o)??0]):[],dt=[];if(ft&&e.passFailDisplay==="test"){const o=e.passFailCounts;dt.push({key:-1,color:Ye,label:"Pass",compactLabel:"Pass",count:o.pass,tooltip:`Pass \xB7 ${o.pass} dies`}),dt.push({key:-2,color:Xe,label:"Fail",compactLabel:"Fail",count:o.fail,tooltip:`Fail \xB7 ${o.fail} dies`})}else if(ft){const o=e.specCounts,a=Oe(e.testDefs,e.activeTest);dt.push({key:-1,color:Ye,label:"Pass",compactLabel:"Pass",count:o.pass,tooltip:`Pass \xB7 ${o.pass} dies`}),a?.limitHigh!==void 0&&dt.push({key:-2,color:Xe,label:"Fail high",compactLabel:"Fail hi",count:o.failHigh,tooltip:`Fail high \xB7 ${o.failHigh} dies`}),a?.limitLow!==void 0&&dt.push({key:-3,color:xo,label:"Fail low",compactLabel:"Fail lo",count:o.failLow,tooltip:`Fail low \xB7 ${o.failLow} dies`})}const wt=ft?dt.length:e.plotMode==="metadata"?$t.length:Gt.length,nt=(h.ownerDocument.defaultView??window).devicePixelRatio??1;if(G<=0||j<=0)return{hitTarget:{getDieAtPoint:()=>null},viewport:{originX:0,originY:0,ppm:1,snapDist:1},binLegendRows:[]};const ot=D!=="default"?D:G<BIN_LEGEND_ADAPT_FLOATING?"floating":G<BIN_LEGEND_ADAPT_COMPACT?"compact":"default",b=ot==="default"||ot==="compact",it=ot==="left",K=ot==="bottom",Mt=ot==="top",yt=ot==="floating",Tt=ot==="compact"||ot==="floating"?BIN_LEGEND_W_COMPACT:BIN_LEGEND_W,Me=Math.floor((j-2*s)/V),ye=wt>Me?Me-1:wt,Te=ht&&K?ye*V:0,oe=ht&&Mt?ye*V:0,ne=e.plotMode==="stackedBins"||e.plotMode==="stackedSoftBins",se=Oe(e.testDefs,e.activeTest),Se=ne?"Count":se?.name??(e.activeTest!=null?`Test ${e.activeTest}`:void 0),ke=ne?void 0:se?.unit,[It,Wt]=e.valueRange,le=o=>String(Math.round(o)),ve=e.logScale||e.allIntegerValues?void 0:Et(Math.max(0,Wt-It)*qe/Math.max(1,j))||void 0,ie=e.allIntegerValues?le:we(Wt,Se,ke,At,ve).edgeFmt;let ae=0;if(Pt){const o=h.getContext("2d");o.font=lt();const a=[ie(It),ie(Wt)];It<0&&a.push(ie(-Math.max(Math.abs(It),Math.abs(Wt))));for(const p of a)ae=Math.max(ae,o.measureText(p).width)}const Le=Math.max(ko,Math.ceil(ae)+Ke+vt+So-s),ce=Pt?g+Le+8:ht&&b?Tt:0,re=ht&&it?Tt:0,fe=J?32:0,Vt=J?36:0,to=Yt!=null?Math.max(ce,Yt):ce,eo=G-2*s-to-Vt-re,Ae=j-2*s-fe-Te-oe;h.width=Math.round(G*nt),h.height=Math.round(j*nt);const t=h.getContext("2d");t.scale(nt,nt),t.fillStyle=Y,t.fillRect(0,0,G,j);const v=e.hoverPoints;if(!v.length)return{hitTarget:{getDieAtPoint:()=>null},viewport:{originX:0,originY:0,ppm:1,snapDist:1},binLegendRows:[]};const Kt=e.rectangles[0],he=Kt?Kt.width/2:0,de=Kt?Kt.height/2:0,qt=e.dieBounds,Ce=(qt?.minX??0)-he,oo=(qt?.maxX??0)+he,no=(qt?.minY??0)-de,Re=(qt?.maxY??0)+de,_e=oo-Ce,Be=Re-no;let at,ct,_;if(X)({originX:at,originY:ct,ppm:_}=X);else{const a=e.notchDir,p=a&&a.y>.1?13:0,m=a&&a.y<-.1?13:0,d=a&&a.x<-.1?13:0,c=a&&a.x>.1?13:0,f=eo-d-c,l=Ae-p-m;_=Math.min(f/_e,l/Be),at=s+Vt+re+d+(f-_e*_)/2-Ce*_,ct=s+oe+p+(l-Be*_)/2+Re*_}const zt=X?.snapDist??Math.max(he,de,1)*1.5,Ut=at+e.waferCenter.x*_,so=ct-e.waferCenter.y*_,ge=e.waferRadius*_,pt=o=>{const a=Math.abs(o-so);return a>=ge?0:Math.sqrt(ge*ge-a*a)},St=(o,a,p,m,d,c,f=Lo(),l=u.text)=>{if(!o)return;t.save(),t.fillStyle=l,t.font=f,t.textAlign=m,t.textBaseline=d;const i=m==="right"?a-c:c-a;let n=o;if(i>8&&t.measureText(n).width>i){for(;n.length>1&&t.measureText(n+"\u2026").width>i;)n=n.slice(0,-1);n=n+"\u2026"}i>8&&t.fillText(n,a,p),t.restore()};t.save(),t.setTransform(_*nt,0,0,-_*nt,at*nt,ct*nt);const kt=1/_,lo=1/(_*nt);if(e.rectangles.length>0){const o=new Map;for(const d of e.rectangles){const c=String(d.fill);let f=o.get(c);f||(f=[],o.set(c,f)),f.push(d)}for(const[d,c]of o){t.fillStyle=d;for(let f=0;f<c.length;f+=PATH_SHAPES){t.beginPath();const l=Math.min(c.length,f+PATH_SHAPES);for(let i=f;i<l;i++){const n=c[i];t.rect(n.x-n.width/2,n.y-n.height/2,n.width,n.height)}t.fill()}}t.strokeStyle="rgba(0,0,0,0.18)",t.lineWidth=.5*lo,t.lineCap="square";const a=Qe.get(e.rectangles)??outlineSegments(e.rectangles);Qe.set(e.rectangles,a);const p=PATH_SHAPES*4;for(let d=0;d<a.length;d+=p){t.beginPath();const c=Math.min(a.length,d+p);for(let f=d;f<c;f+=4)t.moveTo(a[f],a[f+1]),t.lineTo(a[f+2],a[f+3]);t.stroke()}if(t.lineCap="butt",z){const d=e.rectangles.filter(c=>c.binFail);if(d.length>0){const c=new Map;for(const f of d){const l=Ge(String(f.fill));let i=c.get(l);i||(i=[],c.set(l,i)),i.push(f)}t.save(),t.lineCap="butt";for(const[f,l]of c){t.strokeStyle=f,t.globalAlpha=.55,t.lineWidth=Math.max(.6*kt,Math.min(l[0].width,l[0].height)*.1);for(const i of l){const n=i.width/2,M=i.height/2,E=Math.max(1.6*kt,Math.min(i.width,i.height)/3);t.save(),t.beginPath(),t.rect(i.x-n,i.y-M,i.width,i.height),t.clip(),t.beginPath();for(let A=-(n+M);A<=n+M;A+=E)t.moveTo(i.x-n+A,i.y-M),t.lineTo(i.x-n+A+i.height,i.y+M);t.stroke(),t.restore()}}t.restore()}}const m=e.rectangles.filter(d=>d.specMark);if(m.length>0){const d=new Map;for(const f of m){const l=Ge(String(f.fill)),i=`${f.specMark}|${l}`;let n=d.get(i);n||(n=[],d.set(i,n)),n.push(f)}const c=2.5*kt;t.lineJoin="round";for(const[f,l]of d){const[i,n]=f.split("|"),M=n==="#000000"?"#ffffff":"#000000",E=i==="failHigh"?1:-1,A=Math.min(l[0].width,l[0].height),k=Math.max(2.5*kt,A*.3);t.beginPath();for(const q of l)Ve(t,q.x,q.y,k,E);t.strokeStyle=M,t.lineWidth=c,t.stroke(),t.fillStyle=n,t.fill()}}}for(const o of e.overlays){t.beginPath();for(const a of o.points){for(let p=0;p<a.length;p++)p===0?t.moveTo(a[p].x,a[p].y):t.lineTo(a[p].x,a[p].y);o.closed&&t.closePath()}o.fill&&!o.fill.startsWith("rgba(0,0,0,0)")&&(t.fillStyle=o.fill,t.fill()),o.kind==="ring-boundary"||o.kind==="quadrant-boundary"||o.kind==="reticle"?(t.strokeStyle="rgba(0,0,0,0.45)",t.lineWidth=3*kt,t.stroke(),t.strokeStyle="rgba(255,255,255,0.8)",t.lineWidth=1*kt,t.stroke()):(t.strokeStyle=o.lineColor,t.lineWidth=o.lineWidth*kt,t.stroke())}if(t.restore(),e.notchDir){const o=at+e.waferCenter.x*_,a=ct-e.waferCenter.y*_,p=e.notchDir.x,m=-e.notchDir.y,d=e.waferRadius*_,c=4,f=9,l=5,i=o+p*(d+c),n=a+m*(d+c),M=i+p*f,E=n+m*f,A={x:-m,y:p},k=s+Vt+re,q=G-s-ce,et=s+oe,gt=j-s-Te-fe;t.save(),t.beginPath(),t.rect(k,et,q-k,gt-et),t.clip(),t.beginPath(),t.moveTo(i,n),t.lineTo(M+A.x*l,E+A.y*l),t.lineTo(M-A.x*l,E-A.y*l),t.closePath(),t.fillStyle=u.text,t.fill(),t.restore()}t.save(),t.textAlign="center",t.textBaseline="middle";let Pe=0;if(e.rectangles.length>0){let o=1;for(const c of e.texts)c.role!=="indicator"&&c.text.length>o&&(o=c.text.length);const a=e.rectangles[0].width*_,p=e.rectangles[0].height*_,m=Math.min(a,p),d=a/o;Pe=Math.max(8,Math.min(64,Math.round(Math.min(m*.55,d*1.8))))}for(const o of e.texts){const a=at+o.x*_,p=ct-o.y*_;t.font=`${o.role==="indicator"?o.fontSize:Pe}px system-ui, sans-serif`,t.fillStyle=o.color,t.fillText(o.text,a,p)}if(t.restore(),Ht?.(t,{originX:at,originY:ct,ppm:_},{background:Y,text:u.text}),J&&Ro(t,G,j,at,ct,_,s,fe,Vt,R,e.gridToScreen,e.rotation,u,e.compact),Pt){const o=uo(e.valueColorScheme,e.reverseValueScheme),a=Le,p=Ae,m=Math.round(p*.75),d=s+Math.round((p-m)/2),c=G-s-g-a,f=It,l=Wt,i=l-f,n=e.plotMode==="stackedSoftBins"?Ct:mt,{primary:M,secondary:E,note:A}=tt?Ne(e,At,n):{primary:"",secondary:"",note:void 0};if(tt){const r=Math.max(d-6,s+11),O=Ut+Math.max(pt(r),pt(r-12))+8;St(M,G-s,r,"right","bottom",O)}let k=d+m+4;const q=r=>Ut+Math.max(pt(r),pt(r+12))+8,et=e.logScale&&f>0?Math.log10(f):0,gt=e.logScale&&l>0?Math.log10(l)-et:1,I=r=>e.logScale?(1-(Math.log10(r)-et)/gt)*m:(1-(r-f)/i)*m;for(let r=0;r<te;r++){const O=1-r/(te-1),H=d+r/te*m,x=m/te+1;t.fillStyle=o(O),t.fillRect(c,H,g,x)}t.strokeStyle="rgba(0,0,0,0.25)",t.lineWidth=.5,t.strokeRect(c,d,g,m),t.fillStyle=u.text,t.font=lt(),t.textAlign="left",t.strokeStyle="rgba(0,0,0,0.35)",t.lineWidth=.5;const W=Ke,Ot=qe,rt=14;let $;const F=e.logScale&>>0?_o(f,l,m,Ot,rt,et,gt):e.allIntegerValues?(()=>{const r=Math.max(1,Et(i*Ot/m)),O=[],H=Math.ceil(f/r)*r;for(let x=H;x<=l+r*1e-6;x+=r){const B=(1-(x-f)/i)*m;B>rt&&B<m-rt&&O.push(Math.round(x))}return O})():(()=>{const r=i>0?Et(i*Ot/m):0,O=[];if(r>0){$=r;const H=Math.ceil(f/r)*r;for(let x=H;x<=l+r*1e-6;x+=r){const B=(1-(x-f)/i)*m;B>rt&&B<m-rt&&O.push(x)}}return O})(),y=se,L=ne,T=we(l,Se,ke,At,$),w=e.allIntegerValues?le:T.tickFmt,S=e.allIntegerValues?le:T.edgeFmt;t.textBaseline="middle";for(const r of F){const O=d+I(r);t.beginPath(),t.moveTo(c+g,O),t.lineTo(c+g+W,O),t.stroke(),t.fillText(w(r),c+g+W+vt,O)}if(t.beginPath(),t.moveTo(c+g,d),t.lineTo(c+g+W,d),t.stroke(),t.textBaseline="top",t.fillText(S(l),c+g+W+vt,d),t.beginPath(),t.moveTo(c+g,d+m),t.lineTo(c+g+W,d+m),t.stroke(),t.textBaseline="bottom",t.fillText(S(f),c+g+W+vt,d+m),!L&&y&&(y.limitLow!==void 0||y.limitHigh!==void 0)){const r=[],O=e.colorbarRangeMode==="spec";for(const[H,x]of[[y.limitLow,"Lo limit"],[y.limitHigh,"Hi limit"]]){if(H===void 0||e.logScale&&H<=0)continue;const B=I(H);!O&&(B<-rt||B>m+rt)||r.push({value:H,label:x,py:Math.max(0,Math.min(m,B))})}if(r.length>0){t.save(),t.font=lt(),t.textAlign="right",t.textBaseline="middle";for(const{py:H,label:x}of r){const B=d+H;O||(t.strokeStyle="rgba(255,255,255,0.85)",t.lineWidth=3,t.beginPath(),t.moveTo(c,B),t.lineTo(c+g,B),t.stroke(),t.strokeStyle="rgba(0,0,0,0.7)",t.lineWidth=1,t.beginPath(),t.moveTo(c,B),t.lineTo(c+g,B),t.stroke()),t.fillStyle=u.text,t.fillText(x,c-3,B);const _t=x==="Hi limit"?-1:1,Bt=4,Zt=c-3-t.measureText(x).width-Bt-4;t.beginPath(),Ve(t,Zt,B,Bt,_t),t.strokeStyle="#fff",t.lineWidth=2,t.lineJoin="round",t.stroke(),t.fillStyle=u.text,t.fill()}t.restore()}}if(!L){const r=e.logScale?"log\u2081\u2080":e.logScaleRequested?"linear \u2014 log n/a":null;r&&(t.save(),t.fillStyle=u.text,t.font=Ao(),t.textAlign="right",t.textBaseline="top",t.fillText(r,G-s,k),t.restore(),k+=15)}tt&&E&&(St(E,G-s,k,"right","top",q(k),be(),u.text),k+=15),tt&&A&&St(A,G-s,k,"right","top",q(k),be(),u.text)}const me=[];let st;if(ht){const o=ot==="compact",a=e.plotMode==="softBin"?Ct:mt,p=a?new Map(a.map(n=>[n.bin,n])):null,m=e.plotMode==="softBin"?e.binColors.soft:e.binColors.hard,d=n=>m.get(n)??po,c=(n,M)=>{const E=t.measureText(n).width,A=o?0:t.measureText(String(M)).width,k=Z+Lt+E+(A?ee:0);return{labelWidth:E,countWidth:A,totalWidth:k}},f=Xt?.find(n=>n.key===e.activeMetadataKey),l=ft?dt.map(n=>{const M=o?n.compactLabel:n.label;return{key:n.key,color:n.color,count:n.count,label:M,tooltipLabel:n.tooltip,...c(M,n.count)}}):e.plotMode==="metadata"?$t.map(([n,M])=>{const A=f?.values?.find(et=>et.value===n)?.label??n,k=o?n:A,q=e.metadataColorMap?.get(n)??"#d6d9dd";return{key:n,color:q,count:M,label:k,tooltipLabel:`${A} \xB7 ${M} dies`,...c(k,M)}}):Gt.map(([n,M])=>{const E=p?.get(n),A=E?.name?`${n} \xB7 ${E.name}`:`Bin ${n}`,k=o?String(n):A;return{key:n,color:d(n),count:M,label:k,tooltipLabel:`${A} \xB7 ${M} dies`,...c(k,M)}}),i=G-s*2;if(l.length>0){const n=yt?bt*2+(tt?Ue:0):tt&&!Mt?Je+je:0,M=Math.max(1,Math.floor((j-2*s-n)/V));let E=l.length,A=1,k=E*V,q=[],et=0;const gt=K||Mt;if(gt){const $=Qt(l.map(y=>y.totalWidth)),F=Math.max(1,Math.min(l.length,Math.floor((i+8)/($+8))));for(let y=F;y>=1;y--){const L=Math.ceil(l.length/y);if(L>M)continue;const T=new Array(y).fill(0);for(let S=0;S<l.length;S++){const r=S%y;T[r]=Math.max(T[r],l[S].totalWidth)}if(T.reduce((S,r)=>S+r,0)+(y-1)*8<=i||y===1){A=y,q=T,E=L,k=L*V;break}}}else E=Math.min(l.length,M),k=E*V,l.length>M&&(et=l.length-(M-1)),q=[Tt];let I,W;if(yt){const $=Qt(l.map(r=>Z+Lt+t.measureText(r.label).width+ee)),F=Math.ceil($);q=[F];const y=tt?Ue:0,L=F+bt*2,T=k+y+bt*2,w=U?.x??0,S=U?.y??0;I=Math.min(Math.max(G-s-L+w,s),G-s-L),W=Math.min(Math.max(j-s-T+S,s),j-s-T),st={x:I,y:W,w:L,h:T},t.save(),t.globalAlpha=.92,t.fillStyle=u.surface,t.fillRect(I,W,L,T),t.globalAlpha=1,t.strokeStyle=u.axisLine,t.lineWidth=1,t.strokeRect(I,W,L,T),t.restore(),I+=bt,W+=bt+y}else K?(I=s,W=j-s-k):Mt?(I=s,W=s):it?(I=s+4,W=Math.max(s+Math.round((j-2*s-k)/2),s+n)):(I=G-s-Tt+4,W=Math.max(s+Math.round((j-2*s-k)/2),s+n));if(tt){const $=e.plotMode==="softBin"?Ct:mt,{primary:F,secondary:y,note:L}=Ne(e,At,$),T=je,w=[y,L].filter(x=>!!x),S=(x,B,_t)=>w.forEach((Bt,Zt)=>St(Bt,x,B+Zt*16,"left","top",_t(B+Zt*16),be(),u.text)),r=x=>Ut-Math.max(pt(x),pt(x-12))-8,O=x=>b?G-s:r(x),H=yt?st.y+st.h:W+k;if(Mt){const x=H+T;St(F,I,x,"left","top",Ut-Math.max(pt(x),pt(x+12))-8),S(I,x+16,r)}else if(yt)St(F,st.x+bt,st.y+bt,"left","top",st.x+st.w-bt),S(st.x,H+T,()=>st.x+st.w);else{const x=Math.max(W-T,s+Je);St(F,I,x,"left","bottom",O(x)),S(I,H+T,O)}}t.save(),t.font=lt();const Ot=gt?8:0,rt=($,F)=>{if(t.measureText($).width<=F)return $;let y=0,L=$.length;const T="\u2026",w=t.measureText(T).width;for(;y<L;){const S=y+L+1>>1;t.measureText($.slice(0,S)).width+w<=F?y=S:L=S-1}return y===0?T:$.slice(0,y)+T};if(gt){let $=0;for(let F=0;F<E;F++){let y=I;for(let L=0;L<A;L++){const T=l[$++];if(!T)break;const w=ut.includes(T.key),S=!w&&T.key===Q,r=y,O=y+Z+Lt,H=W+F*V+V/2,x=W+F*V+Math.round((V-Z)/2),B=q[L]-Z-Lt-ee;S&&(t.fillStyle=u.hoverRow,t.fillRect(y,W+F*V,q[L],V)),t.fillStyle=T.color,t.fillRect(r,x,Z,Z),t.strokeStyle=w?u.accent:"rgba(0,0,0,0.25)",t.lineWidth=w?2:.75,t.strokeRect(r,x,Z,Z),t.fillStyle=w?u.accent:u.text,t.font=w?`bold ${lt()}`:lt(),t.textAlign="left",t.textBaseline="middle",t.fillText(rt(T.label,B),O,H),t.fillStyle=u.textMuted,t.font=lt(),t.textAlign="right",t.fillText(String(T.count),y+q[L]-2,H),me.push({bin:T.key,x:y,y:W+F*V,w:q[L],h:V,label:T.tooltipLabel}),y+=q[L]+Ot}}}else{const $=q[0],F=ot!=="compact",y=$-Z-Lt-(F?ee:0),L=I+$-2;let T=l;et>0&&(T=l.slice(0,M-1));let w=W;for(const S of T){const r=ut.includes(S.key),O=!r&&S.key===Q,H=I,x=I+Z+Lt,B=w+V/2,_t=w+Math.round((V-Z)/2),Bt=S.label;O&&(t.fillStyle=u.hoverRow,t.fillRect(I-3,w,$+3,V)),t.fillStyle=S.color,t.fillRect(H,_t,Z,Z),t.strokeStyle=r?u.accent:"rgba(0,0,0,0.25)",t.lineWidth=r?2:.75,t.strokeRect(H,_t,Z,Z),t.fillStyle=r?u.accent:u.text,t.font=r?`bold ${lt()}`:lt(),t.textAlign="left",t.textBaseline="middle",t.fillText(rt(Bt,y),x,B),(F||yt)&&(t.fillStyle=u.textMuted,t.font=lt(),t.textAlign="right",t.fillText(String(S.count),L,B)),me.push({bin:S.key,x:I,y:w,w:$,h:V,label:S.tooltipLabel}),w+=V}et>0&&(t.fillStyle=u.textMuted,t.font=lt(),t.textAlign="left",t.textBaseline="middle",t.fillText(`+ ${et} more`,I+Z+Lt,w+V/2))}}l.length>0&&t.restore()}const io={originX:at,originY:ct,ppm:_,snapDist:zt},De=e.rectangles[0]?.width??1,Ie=e.rectangles[0]?.height??1,xt=e.dieBounds,Jt=xt?xt.minX:v.length?He(v.map(o=>o.x)):0,jt=xt?xt.minY:v.length?He(v.map(o=>o.y)):0,ao=xt?xt.maxX:v.length?Qt(v.map(o=>o.x)):1,co=xt?xt.maxY:v.length?Qt(v.map(o=>o.y)):1,{cellW:ue,cellH:pe,nCols:Ft,nRows:xe}=Mo(ao-Jt,co-jt,De*1.5,Ie*1.5,v.length),We=Array.from({length:Ft*xe},()=>[]);for(let o=0;o<v.length;o++){const a=Math.floor((v[o].x-Jt)/ue),p=Math.floor((v[o].y-jt)/pe),m=Number.isFinite(a)?Math.max(0,Math.min(Ft-1,a)):0,d=Number.isFinite(p)?Math.max(0,Math.min(xe-1,p)):0;We[d*Ft+m].push(o)}function Fe(o,a,p){const m=Math.max(0,Math.floor((o-p-Jt)/ue)),d=Math.min(Ft-1,Math.floor((o+p-Jt)/ue)),c=Math.max(0,Math.floor((a-p-jt)/pe)),f=Math.min(xe-1,Math.floor((a+p-jt)/pe)),l=[];for(let i=c;i<=f;i++)for(let n=m;n<=d;n++)for(const M of We[i*Ft+n])l.push(M);return l}return{hitTarget:{getDieAtPoint(o,a){const p=(o-at)/_,m=(ct-a)/_,d=Math.max(De,Ie);for(const l of Fe(p,m,d)){const i=e.rectangles[l];if(i&&Math.abs(p-i.x)<=i.width/2&&Math.abs(m-i.y)<=i.height/2)return e.dies[l]??null}let c=null,f=zt*zt;for(const l of Fe(p,m,zt)){const i=v[l].x-p,n=v[l].y-m,M=i*i+n*n;M<f&&(f=M,c=e.dies[l]??null)}return c}},viewport:io,binLegendRows:me,legendBox:st}}function Ro(h,e,P,u,s,C,z,g,Y,D,U,J,R,X){h.save(),h.font=Co(),h.fillStyle=R.text,h.strokeStyle=R.axisLine,h.lineWidth=.5;const N=P-g+4,Q=z+Y-4,Ht=ho(U),At=Ee(Ht,1,0),Yt=Ee(Ht,0,1),mt=1e-9;function Ct(b){return D?Math.abs(b.y)<mt&&Math.abs(b.x)>mt?{pitch:D.x,sign:Math.sign(b.x),axis:"x"}:Math.abs(b.x)<mt&&Math.abs(b.y)>mt?{pitch:D.y,sign:Math.sign(b.y),axis:"y"}:null:null}function Xt(b,it){if(!X||!b)return null;const K=b.axis==="x"?X.columns:X.rows,Mt=b.axis==="x"?X.columnBreaks:X.rowBreaks,yt=b.axis==="x"?X.centreColumn:X.centreRow;return ro(K.length,Mt,b.pitch*C,it).map(Tt=>({mm:b.sign*(Tt-yt)*b.pitch,label:String(K[Tt])}))}const tt=Ct(At),ut=Ct(Yt);function G(b){return tt?Math.round(b*tt.sign/tt.pitch):b}function j(b){return ut?Math.round(b*ut.sign/ut.pitch):b}function Rt(b){if(!b)return Et(50/C);const it=50/(b*C);return Math.max(1,Math.round(it))*b}const Pt=tt?.pitch,Dt=ut?.pitch,ft=Rt(Pt),ht=Rt(Dt);h.textAlign="center",h.textBaseline="top";const Gt=Math.ceil((z-u)/C/ft)*ft,$t=(e-z-u)/C,dt=Xt(tt,36)??Ze(Gt,$t,ft,b=>D?String(G(b)):$e(b));for(const{mm:b,label:it}of dt){const K=u+b*C;K<z||K>e-z||(h.beginPath(),h.moveTo(K,N-ze),h.lineTo(K,N),h.stroke(),h.fillText(it,K,N+2))}h.textAlign="right",h.textBaseline="middle";const wt=Math.ceil((s-(P-z))/C/ht)*ht,nt=(s-z)/C,ot=Xt(ut,16)??Ze(wt,nt,ht,b=>D?String(j(b)):$e(b));for(const{mm:b,label:it}of ot){const K=s-b*C;K<z||K>P-z||(h.beginPath(),h.moveTo(Q,K),h.lineTo(Q+ze,K),h.stroke(),h.fillText(it,Q-2,K))}h.restore()}function Ze(h,e,P,u){const s=[];for(let C=h;C<=e;C+=P)s.push({mm:C,label:u(C)});return s}function _o(h,e,P,u,s,C,z){const g=[],Y=z;if(Y<1){const R=Et((e-h)*u/P);if(R>0){const X=Math.ceil(h/R)*R;for(let N=X;N<=e+R*1e-6;N+=R){if(N<=h||N>=e)continue;const Q=(1-(Math.log10(N)-C)/z)*P;Q>s&&Q<P-s&&g.push(N)}}return g}const D=Y<3?[1,2,5]:[1],U=Math.floor(Math.log10(h)),J=Math.ceil(Math.log10(e));for(let R=U;R<=J;R++)for(const X of D){const N=X*Math.pow(10,R);if(N<=h||N>=e)continue;const Q=(1-(Math.log10(N)-C)/z)*P;Q>s&&Q<P-s&&g.push(N)}return g}export{fmt,fmtColorbarAxis}from"../renderer/fmt.js";export const PATH_SHAPES=256;const Qe=new WeakMap;export function outlineSegments(h){const e=g=>Math.round(g*1e6),P=new Map,u=new Map,s=(g,Y,D,U)=>{const J=e(Y);let R=g.get(J);R||g.set(J,R={at:Y,spans:[]}),R.spans.push([D,U])};for(const g of h){const Y=g.x-g.width/2,D=g.x+g.width/2,U=g.y-g.height/2,J=g.y+g.height/2;s(P,U,Y,D),s(P,J,Y,D),s(u,Y,U,J),s(u,D,U,J)}const C=[],z=(g,Y)=>{for(const{at:D,spans:U}of g.values()){U.sort((X,N)=>X[0]-N[0]);let[J,R]=U[0];for(let X=1;X<U.length;X++){const[N,Q]=U[X];e(N)<=e(R)?Q>R&&(R=Q):(Y(D,J,R),J=N,R=Q)}Y(D,J,R)}};return z(P,(g,Y,D)=>C.push(Y,g,D,g)),z(u,(g,Y,D)=>C.push(g,Y,g,D)),Float64Array.from(C)}
|