@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.
Files changed (30) hide show
  1. package/AGENTS.md +170 -0
  2. package/CHANGELOG.md +160 -1
  3. package/README.md +9 -1
  4. package/dist/packages/canvas-adapter/icons.js +1 -1
  5. package/dist/packages/canvas-adapter/index.d.ts +2 -0
  6. package/dist/packages/canvas-adapter/index.js +1 -1
  7. package/dist/packages/canvas-adapter/renderWaferGallery.d.ts +14 -0
  8. package/dist/packages/canvas-adapter/renderWaferGallery.js +6 -6
  9. package/dist/packages/canvas-adapter/renderWaferMap.d.ts +14 -0
  10. package/dist/packages/canvas-adapter/renderWaferMap.js +1 -1
  11. package/dist/packages/canvas-adapter/summaryPanel.d.ts +11 -1
  12. package/dist/packages/canvas-adapter/summaryPanel.js +3 -3
  13. package/dist/packages/canvas-adapter/toolbar.d.ts +4 -1
  14. package/dist/packages/canvas-adapter/toolbar.js +1 -1
  15. package/dist/packages/canvas-adapter/userGuideHtml.d.ts +1 -1
  16. package/dist/packages/canvas-adapter/userGuideHtml.js +59 -22
  17. package/dist/packages/canvas-adapter/version.d.ts +2 -2
  18. package/dist/packages/canvas-adapter/version.js +1 -1
  19. package/dist/packages/canvas-adapter/warnings.d.ts +52 -0
  20. package/dist/packages/canvas-adapter/warnings.js +1 -0
  21. package/dist/packages/renderer/buildWaferMap.d.ts +15 -1
  22. package/dist/packages/renderer/buildWaferMap.js +1 -1
  23. package/dist/packages/renderer/index.d.ts +1 -1
  24. package/dist/packages/renderer/index.js +1 -1
  25. package/dist/packages/stats/analyzeWaferLot.js +1 -1
  26. package/dist/packages/stats/analyzeWaferMap.js +1 -1
  27. package/dist/packages/stats/renderFindingsReport.js +15 -15
  28. package/dist/packages/stats/types.d.ts +34 -4
  29. package/llms.txt +39 -0
  30. 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
- ## [Unreleased]
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
  [![CI](https://github.com/wafertools/wafermap/actions/workflows/ci.yml/badge.svg)](https://github.com/wafertools/wafermap/actions/workflows/ci.yml)
4
6
  [![npm](https://img.shields.io/npm/v/@wafertools/wafermap.svg)](https://www.npmjs.com/package/@wafertools/wafermap)
5
7
  ![runtime deps](https://img.shields.io/badge/runtime%20deps-0-brightgreen)
@@ -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. */