@wafertools/wafermap 0.21.1 → 0.22.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 +170 -0
- package/CHANGELOG.md +160 -1
- package/README.md +9 -1
- package/dist/packages/canvas-adapter/icons.js +1 -1
- package/dist/packages/canvas-adapter/index.d.ts +2 -0
- package/dist/packages/canvas-adapter/index.js +1 -1
- package/dist/packages/canvas-adapter/renderWaferGallery.d.ts +14 -0
- package/dist/packages/canvas-adapter/renderWaferGallery.js +6 -6
- package/dist/packages/canvas-adapter/renderWaferMap.d.ts +14 -0
- package/dist/packages/canvas-adapter/renderWaferMap.js +1 -1
- package/dist/packages/canvas-adapter/summaryPanel.d.ts +11 -1
- package/dist/packages/canvas-adapter/summaryPanel.js +3 -3
- package/dist/packages/canvas-adapter/toolbar.d.ts +4 -1
- package/dist/packages/canvas-adapter/toolbar.js +1 -1
- package/dist/packages/canvas-adapter/userGuideHtml.d.ts +1 -1
- package/dist/packages/canvas-adapter/userGuideHtml.js +59 -22
- package/dist/packages/canvas-adapter/version.d.ts +2 -2
- package/dist/packages/canvas-adapter/version.js +1 -1
- package/dist/packages/canvas-adapter/warnings.d.ts +52 -0
- package/dist/packages/canvas-adapter/warnings.js +1 -0
- package/dist/packages/renderer/buildWaferMap.d.ts +15 -1
- package/dist/packages/renderer/buildWaferMap.js +1 -1
- package/dist/packages/renderer/index.d.ts +1 -1
- package/dist/packages/renderer/index.js +1 -1
- package/dist/packages/stats/analyzeWaferLot.js +1 -1
- package/dist/packages/stats/analyzeWaferMap.js +1 -1
- package/dist/packages/stats/renderFindingsReport.js +15 -15
- package/dist/packages/stats/types.d.ts +34 -4
- package/llms.txt +39 -0
- package/package.json +12 -8
package/AGENTS.md
ADDED
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
# wafermap — rules for AI coding agents
|
|
2
|
+
|
|
3
|
+
Guidance for AI tools (Claude Code, Codex, Copilot, Cursor, …) writing code that
|
|
4
|
+
**uses** `@wafertools/wafermap`. Paste the rules below into your project's own
|
|
5
|
+
agent config so they are loaded whenever the agent works on wafer map code.
|
|
6
|
+
|
|
7
|
+
This library renders semiconductor wafer maps. Its output drives yield calls, lot
|
|
8
|
+
dispositions and process changes, so a plot that is *plausibly but silently wrong*
|
|
9
|
+
is the expensive failure — worse than one that throws. Most rules here exist
|
|
10
|
+
because the obvious-looking code produces exactly that.
|
|
11
|
+
|
|
12
|
+
<!-- RULES:START -->
|
|
13
|
+
|
|
14
|
+
## wafermap — usage rules
|
|
15
|
+
|
|
16
|
+
`@wafertools/wafermap` renders wafer maps from semiconductor die test data.
|
|
17
|
+
Wrong-but-plausible output drives real yield and lot decisions, so prefer failing
|
|
18
|
+
loudly over guessing.
|
|
19
|
+
|
|
20
|
+
### Entry points
|
|
21
|
+
|
|
22
|
+
- `@wafertools/wafermap` — `buildWaferMap()`, geometry, `registerColorScheme()`. Pure, no DOM, server-safe.
|
|
23
|
+
- `@wafertools/wafermap/render` — `renderWaferMap()`, `renderWaferGallery()`, `toCanvas()`. Needs the DOM.
|
|
24
|
+
- `@wafertools/wafermap/stats` — `analyzeWaferMap()`, `analyzeWaferLot()`. Pure analysis.
|
|
25
|
+
- `@wafertools/wafermap/worker` — `createWafermapWorker()` for off-main-thread builds.
|
|
26
|
+
|
|
27
|
+
Default path: `buildWaferMap()` once when data loads, then `renderWaferMap()` for a
|
|
28
|
+
single wafer or `renderWaferGallery()` for several. Reach for `toCanvas()`/`buildView()`
|
|
29
|
+
only when you need the low-level pipeline — they give up the toolbar and every UI
|
|
30
|
+
correctness guarantee that comes with it.
|
|
31
|
+
|
|
32
|
+
### Traps that produce silently wrong maps
|
|
33
|
+
|
|
34
|
+
- **Never `die.hbin ?? 0` or `die.sbin ?? 0`.** A missing bin is not bin 0 — it is
|
|
35
|
+
no-data, and must render grey. Defaulting to 0 invents a bin and changes the
|
|
36
|
+
yield number. Leave the field absent.
|
|
37
|
+
- **`x` and `y` are prober step positions (integers), not millimetres.** Pass them
|
|
38
|
+
through unchanged; `dieConfig.width`/`height` convert to physical units. Do not
|
|
39
|
+
pre-multiply. The geometry inputs are `waferConfig` (type `WaferConfig`) and
|
|
40
|
+
`dieConfig` (type `DieConfig`) — both optional, both inferred when omitted.
|
|
41
|
+
- **`passBins` decides both the yield number and the wording of its label.** Set it
|
|
42
|
+
from the actual test program. Do not assume `[1]`.
|
|
43
|
+
- **`testValues` is keyed by test number**, e.g. `{ 1050: 0.42 }` — not a positional
|
|
44
|
+
array. `activeTest` likewise takes a *test number* (`1050`), not an index.
|
|
45
|
+
- **Functional tests (`testType: 'F'`) have no measured value.** Read their verdicts
|
|
46
|
+
only via `getTestPassStatus(die, testNumber, def)`; never read `die.testPass`
|
|
47
|
+
directly and never interpret a 0/1 in `testValues`. A missing verdict is no-data,
|
|
48
|
+
never a fail.
|
|
49
|
+
- **Show users `die.x` / `die.y` only.** Never surface internal display or
|
|
50
|
+
transformed coordinates in tooltips, labels or reports, whatever the rotation or
|
|
51
|
+
flip state.
|
|
52
|
+
- **A die with test results is always fully on the wafer.** A prober only steps to
|
|
53
|
+
sites that fit. Never recompute a `partial` flag by testing die corners against
|
|
54
|
+
the wafer circle — that manufactures fake partial dies which are then greyed out
|
|
55
|
+
and dropped from yield. A die outside the wafer means the *geometry* is wrong.
|
|
56
|
+
- **Check `result.warnings` and `summary.stats.warnings`.** Both are
|
|
57
|
+
`WaferWarning[]` — `{ code, message, severity }`. Branch on `code`, never on the
|
|
58
|
+
prose. Geometry advisories are severity `'error'`: they mean dies may be drawn in
|
|
59
|
+
the wrong place. The renderers surface these themselves in a toolbar indicator, so
|
|
60
|
+
do NOT hand-roll a second display — pass
|
|
61
|
+
`warnings: { display: false, onWarning }` if the app has its own notification UI.
|
|
62
|
+
(`result.inference.warnings` is a deprecated string mirror; do not use it.)
|
|
63
|
+
- **Container needs a real height.** A zero-height parent renders nothing. This is
|
|
64
|
+
the most common "it didn't work" report.
|
|
65
|
+
|
|
66
|
+
### API facts that are easy to guess wrong
|
|
67
|
+
|
|
68
|
+
- `PlotMode` values are camelCase: `'hardBin'`, `'softBin'`, `'value'`, `'metadata'`,
|
|
69
|
+
`'stackedValues'`, `'stackedBins'`, `'stackedSoftBins'`. Never snake_case.
|
|
70
|
+
- `retestCount` is the total probe count — `2` means probed twice. Do not add 1.
|
|
71
|
+
- `retestPolicy: 'best'`/`'worst'` is pass/fail-aware via `passBins`; bin number only
|
|
72
|
+
breaks ties within a category.
|
|
73
|
+
- Hard bins (`hbin`) and soft bins (`sbin`) are independent number spaces. Never merge them.
|
|
74
|
+
- Build once, render many: `buildWaferMap()` handles data + geometry; re-render UI
|
|
75
|
+
changes through the controller's `setOptions()`, not by rebuilding.
|
|
76
|
+
- `result.view` is internal. Use the promoted fields: `result.plotMode`,
|
|
77
|
+
`result.metadata`, `result.isLotStack`, `result.hbinDefs`, `result.sbinDefs`,
|
|
78
|
+
`result.testDefs`.
|
|
79
|
+
- Die keys come from `getDieKey(die)`. A hand-rolled `` `${x},${y}` `` breaks
|
|
80
|
+
click-to-highlight silently, because findings carry `dieKeys` in that exact format.
|
|
81
|
+
- `stats.warnings` is `WaferWarning[]` (it was `string[]` before 0.22.0). Read
|
|
82
|
+
`w.message` to display, branch on `w.code`. Code that calls a string method on an
|
|
83
|
+
entry — `warnings[0].includes('…')` — is the old shape and will throw.
|
|
84
|
+
- **`summary.findings` is the complete list and contains restatements of the same
|
|
85
|
+
fact.** Building a list for a human to read? Exclude what other findings absorb:
|
|
86
|
+
`const absorbed = new Set(summary.findings.flatMap(f => f.absorbedIds ?? []))`.
|
|
87
|
+
Skip that and one edge failure is reported up to three times per region — a hard
|
|
88
|
+
bin row, its soft-bin twin, and the yield row that restates the pass bin. Do NOT
|
|
89
|
+
use `relatedIds` for this; it is a different relationship and some ids it names
|
|
90
|
+
no longer exist in `findings`.
|
|
91
|
+
|
|
92
|
+
### Scale: do not turn this into a data explorer
|
|
93
|
+
|
|
94
|
+
The most common performance mistake is treating the library as somewhere to dump
|
|
95
|
+
an entire test program and browse it. It is a *renderer* — it analyses everything
|
|
96
|
+
it is handed, because it has no way to know which tests anyone will look at.
|
|
97
|
+
|
|
98
|
+
- **Pass only the tests you will actually chart or analyse in `testDefs`.** A real
|
|
99
|
+
parametric program can carry hundreds of tests while the user ever looks at a
|
|
100
|
+
handful. Cost scales with test count, and test correlation scales
|
|
101
|
+
*quadratically*: on a ~1,000-die wafer, `enableTestValueAnalysis` costs ~25 ms at
|
|
102
|
+
6 tests and ~91 ms at 60, while the correlation matrix goes from 15 pairs to 1,770.
|
|
103
|
+
- **Do not "load everything, filter in the UI".** Filtering after the fact means you
|
|
104
|
+
already paid for the parse, the transfer and the analysis.
|
|
105
|
+
- **The right shape is pre-scan → select → load.** Scan the source for which test
|
|
106
|
+
numbers exist (and their names/limits if available), let the user choose, then
|
|
107
|
+
parse and build only the chosen tests. A scan that reads test identity without
|
|
108
|
+
reading every value is dramatically cheaper than a full load, and it is what makes
|
|
109
|
+
a large file feel instant.
|
|
110
|
+
- **Above 250 discovered tests, `analyzeWaferMap` gives up on test-value analysis
|
|
111
|
+
entirely** — it returns no test findings rather than a trimmed set, and records a
|
|
112
|
+
`WaferWarning` with code `'test-count-capped'` in `stats.warnings`. Silence is not
|
|
113
|
+
success here: an empty findings list is indistinguishable from "nothing to report"
|
|
114
|
+
unless you check. Pass `testNumbers: [...]` to scope the analysis explicitly.
|
|
115
|
+
- **Reach for options deliberately.** Plain `analyzeWaferMap()` is cheap;
|
|
116
|
+
`computePerTestStats` is modest; `enableTestValueAnalysis` is the expensive one
|
|
117
|
+
(roughly 10× the base analysis on a large wafer) and exists to find spatial
|
|
118
|
+
patterns automatically — do not enable it by default just because it sounds good.
|
|
119
|
+
- **A Web Worker buys responsiveness, not speed.** `createWafermapWorker` copies data
|
|
120
|
+
across `postMessage`, so total time goes *up*. Use it when a build would otherwise
|
|
121
|
+
visibly freeze the page, not for small datasets.
|
|
122
|
+
- **In a gallery, pass `perWaferSummaries` to `analyzeWaferLot`** so it reuses the
|
|
123
|
+
per-wafer analysis you already ran instead of redoing it.
|
|
124
|
+
|
|
125
|
+
### Removed — do not emit these
|
|
126
|
+
|
|
127
|
+
| Never | Use instead |
|
|
128
|
+
| --- | --- |
|
|
129
|
+
| `DieResult.values` / `Die.values` | `testValues` (keyed by test number) |
|
|
130
|
+
| `TestDef.index` | `TestDef.testNumber` (required) |
|
|
131
|
+
| `ViewOptions.colorBySpec` | `passFailDisplay: 'spec'` |
|
|
132
|
+
| `ViewOptions.testIndex` | `activeTest` |
|
|
133
|
+
| `mountWaferCanvas` | `renderWaferMap` |
|
|
134
|
+
| `HARD_BIN_COLORS` / `SOFT_BIN_COLORS` | `BIN_PALETTE` |
|
|
135
|
+
| `GalleryItem` | `WaferMapDisplayItem` |
|
|
136
|
+
| `MountOptions` | `RenderOptions` |
|
|
137
|
+
| `WaferCanvasController` | `WaferMapController` |
|
|
138
|
+
| `CanvasHitTarget` | `HitTarget` |
|
|
139
|
+
| `buildScene` / `BuildSceneOptions` / `SceneOptions` | `buildView` / `ViewOptions` |
|
|
140
|
+
| `WaferFlat`, field `flat` | `WaferNotch`, field `notch` |
|
|
141
|
+
| `isInsideWaferWithFlat` | `isInsideWafer` |
|
|
142
|
+
| `DieSample` / `WaferMapPoint` | `DieResult` |
|
|
143
|
+
| `colorScheme: 'color'` | `colorScheme: 'default'` |
|
|
144
|
+
| `plotMode: 'specLimit'` | `passFailDisplay: 'spec'` |
|
|
145
|
+
| standalone `getDieAtPoint` | `hitTarget.getDieAtPoint` from `toCanvas()` |
|
|
146
|
+
| `RenderOptions.tooltipTestLimit` | (was a no-op; nothing replaces it) |
|
|
147
|
+
|
|
148
|
+
Passing a removed option is a type error, and is ignored at runtime. Do not add
|
|
149
|
+
compatibility shims for them.
|
|
150
|
+
|
|
151
|
+
### Terminology in user-facing text
|
|
152
|
+
|
|
153
|
+
- Never write "channel" — use "index", "slot" or "test". "Channel" is tester
|
|
154
|
+
hardware jargon that confuses the engineers reading these maps.
|
|
155
|
+
- Label what is actually shown. Name the real pass bins rather than assuming bin 1;
|
|
156
|
+
say "Hard Bin Breakdown" not "Bin Breakdown"; identify an aggregated or filtered
|
|
157
|
+
population (`N=50`, "6 wafers · mean") so nobody mistakes a lot stack for one wafer.
|
|
158
|
+
|
|
159
|
+
<!-- RULES:END -->
|
|
160
|
+
|
|
161
|
+
## Where to look
|
|
162
|
+
|
|
163
|
+
- [API reference](https://wafertools.github.io/wafermap/api/) — every type, option and return value
|
|
164
|
+
- [Developer guide](https://wafertools.github.io/wafermap/guide/) — worked walkthroughs
|
|
165
|
+
- [Troubleshooting](https://wafertools.github.io/wafermap/troubleshooting/)
|
|
166
|
+
- [Examples](https://wafertools.github.io/wafermap/examples/) — 20 runnable pages, also
|
|
167
|
+
[downloadable](https://wafertools.github.io/wafermap/wafermap-examples.zip) to run offline
|
|
168
|
+
|
|
169
|
+
When a rule here and the API reference disagree, the API reference wins — tell the
|
|
170
|
+
user, so this file gets fixed.
|
package/CHANGELOG.md
CHANGED
|
@@ -19,15 +19,174 @@ under `### Breaking`.
|
|
|
19
19
|
|
|
20
20
|
---
|
|
21
21
|
|
|
22
|
-
## [
|
|
22
|
+
## [0.22.0] — 2026-08-04
|
|
23
|
+
|
|
24
|
+
### Breaking
|
|
25
|
+
|
|
26
|
+
- **`StatsSummary.stats.warnings` is now `WaferWarning[]`, not `string[]`.** The
|
|
27
|
+
library raised advisories in two incompatible shapes — structured
|
|
28
|
+
`{ code, message }` on `WaferMapResult.warnings`, raw prose strings on the stats
|
|
29
|
+
summary — so a host had two vocabularies to handle and no stable key to branch
|
|
30
|
+
on for half of them. Both are now `WaferWarning`.
|
|
31
|
+
|
|
32
|
+
Migration: read `w.message` where you previously read the string, and branch on
|
|
33
|
+
the new stable `w.code` (`'test-count-capped'` is the one raised today) instead
|
|
34
|
+
of matching prose. Code that did `warnings[0].includes('…')` is exactly what
|
|
35
|
+
this replaces — that string was never a contract.
|
|
36
|
+
|
|
37
|
+
- **`findTestDef`, `resolveTestNumber`, `getUniqueTestNumbers` and
|
|
38
|
+
`generateTextOverlay` are no longer exported from `/renderer` (or the root).**
|
|
39
|
+
They are internal helpers of the view pipeline with no documented contract, and
|
|
40
|
+
nothing outside the library used them. An export nobody can look up is still API
|
|
41
|
+
surface you cannot change later, so they were withdrawn rather than documented.
|
|
42
|
+
|
|
43
|
+
Migration: none expected. If you did depend on one, import it from
|
|
44
|
+
`@wafertools/wafermap/renderer/buildView.js` and open an issue saying what for —
|
|
45
|
+
the fix is to give you a supported entry point, not to re-widen the surface.
|
|
46
|
+
|
|
47
|
+
### Added
|
|
48
|
+
|
|
49
|
+
- **The library now surfaces its own data warnings.** A ⚠ indicator appears in the
|
|
50
|
+
toolbar only when there is something to say; clicking it lists each advisory with
|
|
51
|
+
its code and explanation. It also feeds the Summary panel's banner and a new
|
|
52
|
+
`onWarning` callback, all from one collected, de-duplicated, severity-ordered set.
|
|
53
|
+
|
|
54
|
+
This closes a real gap rather than adding a nicety: **geometry advisories were
|
|
55
|
+
rendered by no UI at all.** `'partial-coverage'` means the inferred diameter and
|
|
56
|
+
centre may be wrong and dies may be drawn in the wrong place, and nothing ever
|
|
57
|
+
told the person looking at the map. Analysis advisories fared little better — they
|
|
58
|
+
appeared only if the host both passed `statsSummary` and the user opened the
|
|
59
|
+
Summary panel. The library has the information to know the display may mislead,
|
|
60
|
+
so showing it is its responsibility, not the caller's.
|
|
61
|
+
|
|
62
|
+
Deliberately not a toast: these are persistent conditions about whether the map
|
|
63
|
+
can be trusted, and a message that dismisses itself leaves the map still wrong
|
|
64
|
+
with no way back to the explanation.
|
|
65
|
+
|
|
66
|
+
- **`WarningsOptions` on `renderWaferMap` and `renderWaferGallery`** —
|
|
67
|
+
`{ display?: boolean; onWarning?: (warnings: WaferWarning[]) => void }`. Hosts
|
|
68
|
+
with their own notification system pass `{ display: false, onWarning }`: the
|
|
69
|
+
library still collects, de-duplicates and severity-orders, and the host owns only
|
|
70
|
+
presentation. `collectWarnings` and `severityOf` are exported from
|
|
71
|
+
`@wafertools/wafermap/render` so such a host can reproduce exactly the set the
|
|
72
|
+
built-in UI would have shown rather than re-deriving it from two sources.
|
|
73
|
+
|
|
74
|
+
- **`WaferWarning.severity`** — `'error' | 'warning' | 'info'`, defaulting to
|
|
75
|
+
`'warning'`. Geometry advisories are `'error'` (the map may be positionally
|
|
76
|
+
wrong); the test-count cap is `'warning'` (a feature produced nothing, but what
|
|
77
|
+
is drawn is correct). Drives the indicator's colour and ordering — and the
|
|
78
|
+
severity is always in the accessible name too, never colour alone.
|
|
79
|
+
|
|
80
|
+
- New `--wmap-err-bg` / `--wmap-err-border` / `--wmap-err-text` theme tokens
|
|
81
|
+
(9.55:1 contrast on their background, clearing WCAG AA on all three surfaces
|
|
82
|
+
they appear on).
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
- **Downloadable examples package.** `site/wafermap-examples.zip`, built as part of
|
|
86
|
+
`npm run build:site` and published alongside the docs. Contains every example, the
|
|
87
|
+
bundled library, the sample datasets, and a `starter/` skeleton to copy as the seed
|
|
88
|
+
of an application. Unzip, run `sh serve.sh` (or `serve.cmd` on Windows), and it works
|
|
89
|
+
with no npm install and no network — the offline path matters for locked-down fab
|
|
90
|
+
networks. The bundled `serve.py` pins the MIME type for `.js` rather than trusting
|
|
91
|
+
the platform: Python's stdlib server reads MIME types from the Windows registry, and
|
|
92
|
+
where that mapping has been altered it serves JavaScript as `text/plain`, which
|
|
93
|
+
browsers refuse to execute as a module — failing every page on Windows while working
|
|
94
|
+
on Linux.
|
|
95
|
+
- **`AGENTS.md` — usage rules for AI coding agents.** Most consumers now write wafer
|
|
96
|
+
map code through Claude Code, Codex, Copilot or Cursor, and this library's inputs
|
|
97
|
+
invite confident wrong guesses: `die.hbin ?? 0` reads as ordinary defensive coding
|
|
98
|
+
but turns no-data dies into bin 0 and moves the yield number; `activeTest` reads
|
|
99
|
+
like an index but is a test number. The file's core is a copy-paste block for the
|
|
100
|
+
consumer's own agent config, surfaced at
|
|
101
|
+
[/agents/](https://wafertools.github.io/wafermap/agents/), shipped in the npm
|
|
102
|
+
package (`node_modules/@wafertools/wafermap/AGENTS.md`) and at the root of the
|
|
103
|
+
examples archive. `scripts/check-agents-guide.mjs` verifies it against
|
|
104
|
+
`dist/**/*.d.ts` on every `npm run check` and CI run — every recommended symbol
|
|
105
|
+
must exist, every symbol in the removal table (parsed from the table itself, not a
|
|
106
|
+
duplicate list) must be absent, and structural claims are checked rather than
|
|
107
|
+
trusted. An agent guide that names a removed API is worse than none.
|
|
108
|
+
- `llms.txt` now ships in the npm package and the examples archive, with its
|
|
109
|
+
repo-relative links replaced by absolute ones — they resolved to nothing in both
|
|
110
|
+
of those locations.
|
|
111
|
+
- `docs/examples/manifest.json` — single source for the examples list, consumed by
|
|
112
|
+
`demo-nav.js`, the `index.md` generator, the archive builder, and a nav consistency
|
|
113
|
+
check wired into `npm run check` and the test suite.
|
|
23
114
|
|
|
24
115
|
### Changed
|
|
25
116
|
|
|
117
|
+
- **Findings no longer restate the same fact several times.** One edge failure
|
|
118
|
+
could produce, per region: a hard-bin row, its soft-bin twin with an identical
|
|
119
|
+
delta, a pass-bin row, and a yield row saying the same thing as the pass-bin row
|
|
120
|
+
— up to seven rows for what an engineer would state in one sentence. Two exact
|
|
121
|
+
redundancies now collapse:
|
|
122
|
+
|
|
123
|
+
- A soft-bin finding whose hard-bin twin covers **provably the same dies** is
|
|
124
|
+
absorbed, and the surviving row says so (`Hard and soft bin 3 (same dies)`).
|
|
125
|
+
The test is die-set identity computed from the dies, never bin-number
|
|
126
|
+
equality — hard and soft bins are independent number spaces, and merging on
|
|
127
|
+
the number would report one population under the other's name.
|
|
128
|
+
- When exactly one pass bin is configured, that bin's row and the yield row are
|
|
129
|
+
the same statement by definition, so the bin row is absorbed into the yield
|
|
130
|
+
row. With several pass bins no single bin equals yield, and the rule correctly
|
|
131
|
+
does not fire.
|
|
132
|
+
|
|
133
|
+
The surviving row names what it absorbed — "Ring 4 (edge) has hard bin 3 and
|
|
134
|
+
soft bin 3 (same dies) occurrence 8.8 percentage points higher…" — rather than
|
|
135
|
+
quietly dropping the other half. "(same dies)" is load-bearing: without it the
|
|
136
|
+
wording could be read as two populations summed.
|
|
137
|
+
|
|
138
|
+
This applies at lot level too. `analyzeWaferLot` skips per-wafer findings that
|
|
139
|
+
were absorbed, so a twin does not reappear as its own lot row — which is where
|
|
140
|
+
the duplication was most misleading, since every lot row is annotated "seen on
|
|
141
|
+
N/M wafers" and one fact stated twice reads as two signals corroborating
|
|
142
|
+
each other.
|
|
143
|
+
|
|
144
|
+
Nothing is discarded: absorbed findings remain in `summary.findings` and are
|
|
145
|
+
still returned by `filterFindings`. Only the Summary panel and the findings
|
|
146
|
+
report hide them, via the new `StatsFinding.absorbedIds`.
|
|
147
|
+
|
|
148
|
+
- `StatsFinding.absorbedIds` — IDs of findings another finding restates. Kept
|
|
149
|
+
separate from `relatedIds`, which already meant two different things (a
|
|
150
|
+
run-merge's audit trail of constituents it *replaced*, which no longer exist,
|
|
151
|
+
and a spatial pattern's live supporting detail). Anything in `absorbedIds` is
|
|
152
|
+
guaranteed still present in `findings`.
|
|
153
|
+
|
|
154
|
+
- **Examples consolidated from 26 pages to 20.** Several demos differed only by which
|
|
155
|
+
option was enabled. `findings`, `summary-panel`, `lot-findings`, `gallery` and
|
|
156
|
+
`lot-stack-analysis` are now one `statistics.html` with a scope selector
|
|
157
|
+
(single wafer / lot gallery / lot stack) whose banner names the exact calls and
|
|
158
|
+
options in force; `color-schemes` folded into `display-control.html`, where the
|
|
159
|
+
existing scheme dropdown already did the same job; `partial-data` folded into
|
|
160
|
+
`geometry.html` as a second section. Every old URL keeps a redirect stub pointing at
|
|
161
|
+
the merged page and the anchor that reproduces what it used to show, and the Guide
|
|
162
|
+
cross-links now target those anchors, so per-topic granularity is unchanged.
|
|
163
|
+
|
|
26
164
|
- **Package renamed from `@paulrobins/wafermap` to `@wafertools/wafermap`.** The GitHub repo
|
|
27
165
|
moved from `telecasterer/wafermap` to `wafertools/wafermap` along with it — repository,
|
|
28
166
|
homepage, and issue-tracker URLs all point at the new org. `@paulrobins/wafermap` is
|
|
29
167
|
deprecated on npm in favour of this package; no functional changes.
|
|
30
168
|
|
|
169
|
+
### Fixed
|
|
170
|
+
|
|
171
|
+
- **The toolbar could overflow across neighbouring content.** It is pinned by its
|
|
172
|
+
right edge with no width bound, so once wider than its container the excess grew
|
|
173
|
+
leftward — out of the card and over whatever sat beside it (the next map in a
|
|
174
|
+
grid, an adjacent gallery card). It had always been wider than a ~400px
|
|
175
|
+
container. It now wraps within the container, breaking between control groups
|
|
176
|
+
rather than mid-group, with `wrap-reverse` so the trailing group keeps the top
|
|
177
|
+
row and Expand keeps its top-right corner.
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
- **Custom colour schemes did not work on the built documentation site.**
|
|
181
|
+
`scripts/bundle-docs.mjs` built each importmap entry point as an independent esbuild
|
|
182
|
+
bundle, so `wafermap` and `wafermap/render` each inlined a private copy of the
|
|
183
|
+
colour-scheme registry. `registerColorScheme` imported from `wafermap` wrote into a
|
|
184
|
+
registry the renderer never read, and a custom scheme rendered pixel-identical to the
|
|
185
|
+
default palette — silently, with no error. All entry points are now built in one
|
|
186
|
+
invocation with code splitting, so shared module state lives in a common chunk. Only
|
|
187
|
+
ever affected the bundled site build; `npm run dev` serves unbundled modules that
|
|
188
|
+
resolve to one shared file, which is why it went unnoticed.
|
|
189
|
+
|
|
31
190
|
## [0.21.1] — 2026-07-31
|
|
32
191
|
|
|
33
192
|
### Added
|
package/README.md
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# wafermap
|
|
2
2
|
|
|
3
|
+
<img src="docs/images/wafermap-readme-header-256.png" width="64" height="64" alt="wafermap icon">
|
|
4
|
+
|
|
3
5
|
[](https://github.com/wafertools/wafermap/actions/workflows/ci.yml)
|
|
4
6
|
[](https://www.npmjs.com/package/@wafertools/wafermap)
|
|
5
7
|

|
|
@@ -14,6 +16,11 @@ Browser-first wafer map visualization for semiconductor test data.
|
|
|
14
16
|
|
|
15
17
|
**[Project Portal: Docs & Interactive Demos →](https://wafertools.github.io/wafermap/)**
|
|
16
18
|
|
|
19
|
+
## Community
|
|
20
|
+
|
|
21
|
+
Questions, ideas, or want to show off a wafer map you built? Use [GitHub
|
|
22
|
+
Discussions](https://github.com/wafertools/.github/discussions).
|
|
23
|
+
|
|
17
24
|
## Overview
|
|
18
25
|
|
|
19
26
|
wafermap renders interactive wafer maps from semiconductor prober output. Hard bins, soft bins, test values, retest runs, edge exclusion, and spec limits are native inputs.
|
|
@@ -56,8 +63,9 @@ and stats layers.
|
|
|
56
63
|
- [Developer Guide](https://wafertools.github.io/wafermap/guide/)
|
|
57
64
|
- [SvelteKit](https://wafertools.github.io/wafermap/sveltekit/) · [React](https://wafertools.github.io/wafermap/react/) · [Vue 3](https://wafertools.github.io/wafermap/vue/) integration guides
|
|
58
65
|
- [API Reference](https://wafertools.github.io/wafermap/api/)
|
|
66
|
+
- [Using wafermap with an AI coding agent](https://wafertools.github.io/wafermap/agents/) — rules to paste into Claude Code / Codex / Copilot / Cursor, also shipped as `AGENTS.md` in this package
|
|
59
67
|
- [Architecture](https://wafertools.github.io/wafermap/architecture/) · [Performance](https://wafertools.github.io/wafermap/performance/) · [Troubleshooting](https://wafertools.github.io/wafermap/troubleshooting/)
|
|
60
|
-
- [Live examples](https://wafertools.github.io/wafermap/examples/)
|
|
68
|
+
- [Live examples](https://wafertools.github.io/wafermap/examples/) — or [download them](https://wafertools.github.io/wafermap/wafermap-examples.zip) to run and edit locally, offline, with the library bundled in
|
|
61
69
|
|
|
62
70
|
**Using an app built with wafermap** (test / device / yield engineers):
|
|
63
71
|
|
|
@@ -39,4 +39,4 @@ export const ICONS={aggr:'<svg viewBox="0 0 24 24" width="16" height="16" fill="
|
|
|
39
39
|
<path d="M4.2 12h15.6"/>
|
|
40
40
|
<path d="M5.8 16h12.4"/>
|
|
41
41
|
|
|
42
|
-
</svg>`,windowMinimize:'<svg viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M5 19h14"/></svg>',windowRestore:'<svg viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><rect x="4" y="5" width="16" height="14" rx="1.5"/></svg>',xyIndicator:'<svg viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M5 19 L5 7"/><path d="M5 7 L3 10"/><path d="M5 7 L7 10"/><path d="M5 19 L17 19"/><path d="M17 19 L14 17"/><path d="M17 19 L14 21"/><text x="18" y="8" font-size="6" fill="currentColor" stroke="none">X</text><text x="2" y="6" font-size="6" fill="currentColor" stroke="none">Y</text></svg>',zoomIn:'<svg viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="8"/><line x1="21" x2="16.65" y1="21" y2="16.65"/><line x1="11" x2="11" y1="8" y2="14"/><line x1="8" x2="14" y1="11" y2="11"/></svg>',zoomMode:'<svg viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="m13 13.5 2-2.5-2-2.5"/><path d="m21 21-4.3-4.3"/><path d="M9 8.5 7 11l2 2.5"/><circle cx="11" cy="11" r="8"/></svg>',zoomOut:'<svg viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="8"/><line x1="21" x2="16.65" y1="21" y2="16.65"/><line x1="8" x2="14" y1="11" y2="11"/></svg>'};
|
|
42
|
+
</svg>`,warning:'<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10.29 3.86 1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z"/><path d="M12 9v4"/><path d="M12 17h.01"/></svg>',windowMinimize:'<svg viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M5 19h14"/></svg>',windowRestore:'<svg viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><rect x="4" y="5" width="16" height="14" rx="1.5"/></svg>',xyIndicator:'<svg viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M5 19 L5 7"/><path d="M5 7 L3 10"/><path d="M5 7 L7 10"/><path d="M5 19 L17 19"/><path d="M17 19 L14 17"/><path d="M17 19 L14 21"/><text x="18" y="8" font-size="6" fill="currentColor" stroke="none">X</text><text x="2" y="6" font-size="6" fill="currentColor" stroke="none">Y</text></svg>',zoomIn:'<svg viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="8"/><line x1="21" x2="16.65" y1="21" y2="16.65"/><line x1="11" x2="11" y1="8" y2="14"/><line x1="8" x2="14" y1="11" y2="11"/></svg>',zoomMode:'<svg viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="m13 13.5 2-2.5-2-2.5"/><path d="m21 21-4.3-4.3"/><path d="M9 8.5 7 11l2 2.5"/><circle cx="11" cy="11" r="8"/></svg>',zoomOut:'<svg viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="8"/><line x1="21" x2="16.65" y1="21" y2="16.65"/><line x1="8" x2="14" y1="11" y2="11"/></svg>'};
|
|
@@ -5,6 +5,8 @@ export type { RenderOptions, WaferMapController, WaferViewOptions, WaferPreferen
|
|
|
5
5
|
export { renderWaferGallery } from './renderWaferGallery.js';
|
|
6
6
|
export type { WaferMapDisplayItem, WaferMapDisplayItemFactory, GalleryOptions, GalleryController } from './renderWaferGallery.js';
|
|
7
7
|
export type { SummaryPanelOptions } from './summaryPanel.js';
|
|
8
|
+
export { collectWarnings, severityOf } from './warnings.js';
|
|
9
|
+
export type { WarningsOptions } from './warnings.js';
|
|
8
10
|
export type { InsightsOptions, InsightsView } from './insightsTab.js';
|
|
9
11
|
export { setDetachWindowOpener } from './toolbar.js';
|
|
10
12
|
export type { DetachWindowOpener } from './toolbar.js';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export{toCanvas}from"./toCanvas.js";export{renderWaferMap}from"./renderWaferMap.js";export{renderWaferGallery}from"./renderWaferGallery.js";export{setDetachWindowOpener}from"./toolbar.js";
|
|
1
|
+
export{toCanvas}from"./toCanvas.js";export{renderWaferMap}from"./renderWaferMap.js";export{renderWaferGallery}from"./renderWaferGallery.js";export{collectWarnings,severityOf}from"./warnings.js";export{setDetachWindowOpener}from"./toolbar.js";
|
|
@@ -2,6 +2,7 @@ import { type SaveImageHandler, type SaveTextHandler, type UserGuideExtension }
|
|
|
2
2
|
import type { Die } from '../core/dies.js';
|
|
3
3
|
import type { WaferViewOptions } from './renderWaferMap.js';
|
|
4
4
|
import type { LotStatsSummary } from '../stats/types.js';
|
|
5
|
+
import { type WarningsOptions } from './warnings.js';
|
|
5
6
|
import type { SummaryPanelOptions } from './summaryPanel.js';
|
|
6
7
|
import { type InsightsOptions } from './insightsTab.js';
|
|
7
8
|
/**
|
|
@@ -25,6 +26,13 @@ export interface WaferMapDisplayItem {
|
|
|
25
26
|
testDefs?: import('../renderer/buildWaferMap.js').TestDef[];
|
|
26
27
|
metadataFields?: import('../renderer/buildWaferMap.js').MetadataFieldDef[];
|
|
27
28
|
reticles?: import('../core/reticle.js').Reticle[];
|
|
29
|
+
/**
|
|
30
|
+
* Geometry advisories carried from `buildWaferMap`. Present automatically when
|
|
31
|
+
* an item is spread from a `WaferMapResult` (the usual `{ ...result, label }`
|
|
32
|
+
* shape); the gallery collects them across cards and surfaces them once in the
|
|
33
|
+
* lot bar's warning indicator.
|
|
34
|
+
*/
|
|
35
|
+
warnings?: import('../renderer/buildWaferMap.js').WaferWarning[];
|
|
28
36
|
isLotStack?: boolean;
|
|
29
37
|
aggrMethod?: string;
|
|
30
38
|
lotSize?: number;
|
|
@@ -148,6 +156,12 @@ export interface GalleryOptions {
|
|
|
148
156
|
* recomputing it — no other host wiring beyond this option.
|
|
149
157
|
*/
|
|
150
158
|
insights?: InsightsOptions;
|
|
159
|
+
/**
|
|
160
|
+
* Built-in surfacing of the library's own data warnings (see `WarningsOptions`).
|
|
161
|
+
* Defaults on. The gallery collects across every item and de-duplicates, so a
|
|
162
|
+
* geometry advisory affecting the whole lot is stated once, not per card.
|
|
163
|
+
*/
|
|
164
|
+
warnings?: WarningsOptions;
|
|
151
165
|
}
|
|
152
166
|
export interface GalleryController {
|
|
153
167
|
/** Replace all items — destroys existing cards and rebuilds the grid. Accepts pre-built items, factory functions, or a mix. */
|