gds-lens 0.1.1 → 1.0.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/CHANGELOG.md +98 -4
- package/README.md +46 -212
- package/dist/esm/gds-lens.js +1851 -1599
- package/dist/inline-wasm/gds-lens-engine.js +0 -0
- package/dist/inline-wasm/gds-lens-host.js +36 -5
- package/dist/inline-wasm/gds-lens.js +1738 -1598
- package/dist/web/gds-lens-engine.js +1 -1
- package/dist/web/gds-lens-engine.wasm +0 -0
- package/dist/web/gds-lens-host.js +36 -5
- package/dist/web/gds-lens.js +1738 -1598
- package/docs/embedding.md +238 -0
- package/docs/react.md +194 -0
- package/package.json +7 -1
- package/src/esm-entry.js +18 -0
- package/src/gds-lens.js +189 -53
- package/src/hosts/browser.js +92 -12
- package/src/viewer-shell.html +6 -2
- package/src/viewer.css +3 -0
- package/src/viewer.js +2663 -2458
- package/types/gds-lens.d.ts +36 -2
- package/src/mount-target.js +0 -23
package/CHANGELOG.md
CHANGED
|
@@ -4,10 +4,103 @@ Notable changes to this project. The format follows
|
|
|
4
4
|
[Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project
|
|
5
5
|
follows [semantic versioning](https://semver.org/spec/v2.0.0.html).
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
7
|
+
From 1.0.0 on, a breaking change to the element's API waits for a major
|
|
8
|
+
version. Before that, `0.x` releases changed it freely.
|
|
9
9
|
|
|
10
|
-
## [
|
|
10
|
+
## [1.0.0] - 2026-08-25
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- Several `<gds-lens>` elements can now be live on one page. Each drives its
|
|
15
|
+
own viewer, with its own WebAssembly instance and WebGL2 context, so a `.lyp`
|
|
16
|
+
or marker database applied to one leaves the others alone. A second element
|
|
17
|
+
used to refuse visibly rather than contend for the renderer's state.
|
|
18
|
+
- `destroy()` on the element, releasing a viewer's WebAssembly instance and GL
|
|
19
|
+
context for good. Rarely needed -- an ordinary unmount parks the viewer
|
|
20
|
+
instead, which is what makes a framework remount free.
|
|
21
|
+
- `docs/react.md`: a wrapper component, the JSX type declaration for React 18
|
|
22
|
+
and 19, server rendering, and what remounting does. The examples are covered
|
|
23
|
+
by `test/react.test.js`, and the type declaration by
|
|
24
|
+
`test/types/jsx-smoke.tsx`.
|
|
25
|
+
- `showLoading(label?)` on the viewer surface, for the wait before `load()`:
|
|
26
|
+
a host that is fetching bytes can say so instead of leaving the viewer
|
|
27
|
+
looking idle. The element calls it itself when `load()` is given a URL.
|
|
28
|
+
- `examples/multi-view.html`: six viewers on one page -- three loading a layout
|
|
29
|
+
from `src`, three waiting for a button -- including applying a `.lyp` and a
|
|
30
|
+
marker database to one viewer alone, and creating and releasing a viewer to
|
|
31
|
+
keep a WebGL context free.
|
|
32
|
+
- `docs/embedding.md`, which is where the `ViewerHost` interface, the three
|
|
33
|
+
builds, the subpath exports and the WebAssembly limits moved to. The README
|
|
34
|
+
keeps the quick start and the element's own API and is a third of its previous
|
|
35
|
+
length.
|
|
36
|
+
|
|
37
|
+
### Fixed
|
|
38
|
+
|
|
39
|
+
- Saved views were shared by every viewer on the page. Each read the whole set
|
|
40
|
+
at mount and wrote the whole set back on save, so two viewers that both saved
|
|
41
|
+
a view overwrote each other and the last one won. The default host now keeps a
|
|
42
|
+
bucket per viewer, keyed by the element's `id`, else its `src`, else -- for a
|
|
43
|
+
page with a single viewer and neither -- the key it has always used. `loadViews`
|
|
44
|
+
and `saveViews` are handed the viewer asking, which is what a host serving
|
|
45
|
+
several needs to tell them apart. A viewer with no `id` and no `src` on a page
|
|
46
|
+
with others has nothing stable to key on: its views last for the life of the
|
|
47
|
+
page. Views previously saved on a page whose element carries a `src` are not
|
|
48
|
+
carried into the new per-layout bucket.
|
|
49
|
+
- The renderer sized its drawing buffer to the window rather than to the canvas
|
|
50
|
+
element, which was only ever right for a viewer filling the page. In an
|
|
51
|
+
embedded `<gds-lens>` the browser stretched a window-sized buffer over the
|
|
52
|
+
element's box, so the layout was drawn distorted; every coordinate the mouse
|
|
53
|
+
produced -- the readout, the ruler, the right-click "Copy coordinate",
|
|
54
|
+
zoom-at-cursor -- answered for a pixel the pointer was not on; and each viewer
|
|
55
|
+
allocated a window-sized buffer and mask texture however small it was on
|
|
56
|
+
screen. It now sizes from the element, and a `ResizeObserver` on the canvas
|
|
57
|
+
keeps it in step with a box that changes without the window changing.
|
|
58
|
+
- The canvas right-click menu passed viewport coordinates to the renderer and
|
|
59
|
+
clamped itself against the window, so in an embedded viewer it opened in the
|
|
60
|
+
wrong place and reported the coordinate of a different pixel.
|
|
61
|
+
- A viewer that had not been asked for a layout showed "Loading layout..." over
|
|
62
|
+
an empty progress bar, which read as a load that had hung. It now says "No
|
|
63
|
+
layout loaded" until something actually asks for one.
|
|
64
|
+
- `import "gds-lens"` shipped no default host. `sideEffects` in package.json
|
|
65
|
+
did not list `src/hosts/browser.js`, whose whole purpose is the side effect
|
|
66
|
+
of installing `window.gdsLensHost`, so every bundler dropped it -- including
|
|
67
|
+
the one that builds `dist/esm`. The documented entry point therefore came up
|
|
68
|
+
with no `.lyp` or marker pickers, no saved views, no drag-and-drop, no
|
|
69
|
+
`window.gdsLens`, and a console error claiming no layout would ever appear on
|
|
70
|
+
pages whose layout had loaded perfectly well. The served payloads were never
|
|
71
|
+
affected, since they load `gds-lens-host.js` as a separate script.
|
|
72
|
+
|
|
73
|
+
The `esm-bundle` test that was meant to catch this asserted on the string
|
|
74
|
+
`gdsLensHost`, which viewer.js contains anyway; it now looks for the host's
|
|
75
|
+
own implementation.
|
|
76
|
+
- A viewer adopted while its WebAssembly module was still starting failed to
|
|
77
|
+
come up at all, reporting `Cannot set properties of null (setting 'width')`.
|
|
78
|
+
`ready` resolves as soon as the viewer is built, which is well before `main()`
|
|
79
|
+
creates the GL context, so an element could be removed and its viewer adopted
|
|
80
|
+
inside that window -- and `adopt` re-pointed the module's DOM root on the next
|
|
81
|
+
turn of the module promise, which is after `main()` has already read it. It
|
|
82
|
+
now writes through the object Emscripten uses *as* the Module, so the new root
|
|
83
|
+
is in place immediately.
|
|
84
|
+
- An element whose parked viewer had been adopted by another element went on
|
|
85
|
+
driving it, so a stray `load()` or `src` change on a detached element wrote
|
|
86
|
+
into whatever was on screen. Such an element now rejects instead.
|
|
87
|
+
- Removing a `<gds-lens>` before its engine finished loading raised an unhandled
|
|
88
|
+
promise rejection, which reached the embedding app's error reporting.
|
|
89
|
+
- An element removed and re-added before its engine arrived built two viewers,
|
|
90
|
+
orphaning the first one's WebAssembly instance and WebGL context behind the
|
|
91
|
+
second's shadow tree.
|
|
92
|
+
- The viewer no longer assigns `window.onerror`, which clobbered the embedding
|
|
93
|
+
page's handler. Page-level failures are reported through an added listener
|
|
94
|
+
and fanned out to whichever debug panels are open.
|
|
95
|
+
|
|
96
|
+
### Changed
|
|
97
|
+
|
|
98
|
+
- `src/viewer.js` exports `createViewer(element)` instead of doing its work in
|
|
99
|
+
its module body, which is what allows more than one viewer. `window.gdsLensHost`
|
|
100
|
+
is consequently read when each viewer mounts rather than once at import, so an
|
|
101
|
+
app can install a host any time before its first `<gds-lens>` renders.
|
|
102
|
+
- `src/mount-target.js` is gone. It existed only to hand an element to
|
|
103
|
+
viewer.js's module body, which is now a parameter.
|
|
11
104
|
|
|
12
105
|
## [0.1.1] - 2026-08-25
|
|
13
106
|
|
|
@@ -194,6 +287,7 @@ web page rather than for a webview.
|
|
|
194
287
|
worker-loading route and shipped unsubstituted. The `createWorker` host hook
|
|
195
288
|
replaces it.
|
|
196
289
|
|
|
197
|
-
[Unreleased]: https://github.com/EthanLowenthal/GDS-Lens/compare/
|
|
290
|
+
[Unreleased]: https://github.com/EthanLowenthal/GDS-Lens/compare/v1.0.0...HEAD
|
|
291
|
+
[1.0.0]: https://github.com/EthanLowenthal/GDS-Lens/compare/v0.1.1...v1.0.0
|
|
198
292
|
[0.1.1]: https://github.com/EthanLowenthal/GDS-Lens/compare/v0.1.0...v0.1.1
|
|
199
293
|
[0.1.0]: https://github.com/EthanLowenthal/GDS-Lens/releases/tag/v0.1.0
|
package/README.md
CHANGED
|
@@ -1,11 +1,15 @@
|
|
|
1
1
|
# GDS Lens
|
|
2
2
|
|
|
3
|
+
[](https://www.npmjs.com/package/gds-lens)
|
|
4
|
+
[](LICENCE.md)
|
|
5
|
+
|
|
3
6
|
GDSII and OASIS chip layouts, parsed and rendered in the browser. A drop-in
|
|
4
7
|
custom element compiled to pure JS+WASM with a WebGL2 renderer.
|
|
5
8
|
|
|
6
9
|

|
|
7
10
|
|
|
8
|
-
> **Status**:
|
|
11
|
+
> **Status**: 1.0. The element's API is stable, and breaking changes wait
|
|
12
|
+
> for a major version. See [the changelog](CHANGELOG.md).
|
|
9
13
|
|
|
10
14
|
## Quick start
|
|
11
15
|
|
|
@@ -39,41 +43,9 @@ await viewer.load("chip.gds"); // a URL, or bytes you already have
|
|
|
39
43
|
await viewer.goToPoint(120.5, -40); // center on a coordinate, in microns
|
|
40
44
|
```
|
|
41
45
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
and a streaming WebAssembly compile — copy `dist/web/` and load its scripts in
|
|
46
|
-
order. The order matters: both must precede `gds-lens.js`, which reads them as
|
|
47
|
-
it starts.
|
|
48
|
-
|
|
49
|
-
```html
|
|
50
|
-
<script src="gds-lens-engine.js"></script> <!-- the wasm module -->
|
|
51
|
-
<script src="gds-lens-host.js"></script> <!-- the default ViewerHost -->
|
|
52
|
-
<script src="gds-lens.js"></script> <!-- the element -->
|
|
53
|
-
|
|
54
|
-
<gds-lens src="chip.gds" style="width: 100%; height: 600px"></gds-lens>
|
|
55
|
-
```
|
|
56
|
-
|
|
57
|
-
#### What each file is
|
|
58
|
-
|
|
59
|
-
The payload is six files, four of which are required:
|
|
60
|
-
|
|
61
|
-
| File | Status | What it is |
|
|
62
|
-
|---|---|---|
|
|
63
|
-
| `gds-lens.js` | required | The element, the viewer, and the control panel. This is the package. Load it last. |
|
|
64
|
-
| `gds-lens-engine.js` | required | gdstk's GDSII and OASIS reader and the WebGL2 renderer, compiled to WebAssembly. Defines the `createGdstkModule` global `gds-lens.js` looks for, which is why it goes first. The parse worker loads it too. |
|
|
65
|
-
| `gds-lens-engine.wasm` | required | The binary, fetched by `gds-lens-engine.js` from beside it. `inline-wasm` embeds it instead. |
|
|
66
|
-
| `gds-lens-worker.js` | required | The parse worker: reads and triangulates off the main thread so the canvas stays responsive. Fetched by the worker, never by your page. |
|
|
67
|
-
| `gds-lens-host.js` | optional | The default `ViewerHost`, described in [The ViewerHost interface](#the-viewerhost-interface). If you set `window.gdsLensHost` yourself before `gds-lens.js` runs, omit it. |
|
|
68
|
-
| `gds-lens.html` | optional | A working reference page. For the script order, read this file; there is no need to deploy it. |
|
|
69
|
-
|
|
70
|
-
Every name carries the package prefix because these get copied into someone
|
|
71
|
-
else's web root, where a bare `host.js` or `wasm-worker.js` is a likely
|
|
72
|
-
collision. Serve them from one directory, in the preceding order.
|
|
73
|
-
|
|
74
|
-
`dist/web/gds-lens.html` is a working page doing exactly this. Everything in the
|
|
75
|
-
payload must be on the same origin as the page: the WebAssembly binary and the
|
|
76
|
-
parse worker are both fetched relative to the scripts.
|
|
46
|
+
If you would rather serve a payload than bundle one — for a smaller download and
|
|
47
|
+
a streaming WebAssembly compile — see
|
|
48
|
+
[Embedding GDS Lens](docs/embedding.md#serve-the-files-instead-of-bundling).
|
|
77
49
|
|
|
78
50
|
## What it does
|
|
79
51
|
|
|
@@ -100,15 +72,18 @@ CI rather than committed.
|
|
|
100
72
|
|
|
101
73
|
## Reference
|
|
102
74
|
|
|
103
|
-
The following sections describe the element
|
|
104
|
-
|
|
105
|
-
|
|
75
|
+
The following sections describe the element's own API. The rest lives beside
|
|
76
|
+
it:
|
|
77
|
+
|
|
78
|
+
| Page | What is in it |
|
|
79
|
+
|---|---|
|
|
80
|
+
| [React integration](docs/react.md) | A wrapper component, the JSX type declaration, server rendering, remounts. |
|
|
81
|
+
| [Embed the viewer](docs/embedding.md) | The `ViewerHost` interface, the three builds, the subpath exports, and the limits of the WebAssembly module. |
|
|
106
82
|
|
|
107
83
|
### The `<gds-lens>` element
|
|
108
84
|
|
|
109
85
|
The element is `display: block` with no intrinsic height, so give it one. It
|
|
110
|
-
takes one attribute
|
|
111
|
-
live at a time.
|
|
86
|
+
takes one attribute and exposes a handful of methods.
|
|
112
87
|
|
|
113
88
|
#### Attributes
|
|
114
89
|
|
|
@@ -130,188 +105,47 @@ The element exposes the following members:
|
|
|
130
105
|
| `setLyp(name, text)` | `Promise<void>` | Applies a `.lyp` layer-properties file. Pass `""` to clear. |
|
|
131
106
|
| `setMarkers(name, text)` | `Promise<void>` | Applies a marker database. The viewer detects the format from the content. |
|
|
132
107
|
| `showError(message)` | `Promise<void>` | Replaces the view with an error message. |
|
|
108
|
+
| `destroy()` | `Promise<void>` | Releases the viewer's WebAssembly instance and WebGL context for good. Rarely needed — see [Removal parks the viewer](#removal-parks-the-viewer). |
|
|
133
109
|
|
|
134
|
-
####
|
|
110
|
+
#### Several viewers on one page
|
|
135
111
|
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
112
|
+
Each `<gds-lens>` drives its own viewer, with its own shadow tree, its own
|
|
113
|
+
WebAssembly instance, and its own WebGL2 context. Put as many on a page as you
|
|
114
|
+
like; they share nothing, so a `.lyp` or a marker database applied to one leaves
|
|
115
|
+
the others alone.
|
|
139
116
|
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
the WebGL context, the parsed design, and the camera. Nothing reloads.
|
|
117
|
+
Each one costs a WebGL2 context, though, and browsers cap live contexts per page
|
|
118
|
+
at roughly 8 to 16 — past that the browser starts dropping the oldest. A page
|
|
119
|
+
showing a dozen layouts at once wants one viewer swapping layouts rather than a
|
|
120
|
+
dozen elements.
|
|
145
121
|
|
|
146
|
-
|
|
122
|
+
[`examples/multi-view.html`](examples/multi-view.html) is a working page of six:
|
|
123
|
+
three loading a layout from `src`, three waiting for a button, one of them
|
|
124
|
+
created and released on demand to keep a context free.
|
|
147
125
|
|
|
148
|
-
|
|
149
|
-
can do goes through a host object, which you install as `window.gdsLensHost`
|
|
150
|
-
before the element script runs. Leave it unset and a default host handles a
|
|
151
|
-
plain web page: `<input type=file>` for the pickers, `localStorage` for saved
|
|
152
|
-
views, `prompt()` for a name, plus `?src=` and drag-and-drop for loading.
|
|
126
|
+
#### Removal parks the viewer
|
|
153
127
|
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
`<gds-lens>` element rather than to `window`, which leaves the rest of the
|
|
159
|
-
page's own drop targets alone.
|
|
128
|
+
An element leaving the DOM *parks* its viewer rather than tearing it down, and
|
|
129
|
+
the next `<gds-lens>` to mount without one of its own adopts it. That is what
|
|
130
|
+
makes a framework remount free: the element is new, the viewer is not, and the
|
|
131
|
+
parsed design, the camera and the GL context all survive.
|
|
160
132
|
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
can implement any of the following methods:
|
|
133
|
+
An element mounted *alongside* a live one finds nothing parked and builds its
|
|
134
|
+
own, which is why the preceding section is true.
|
|
164
135
|
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
| `unloadMarkers()` | `void` | The loaded marker database is dismissed. |
|
|
171
|
-
| `loadViews()` | `Promise<View[]>` | Once at mount, for saved camera positions. |
|
|
172
|
-
| `saveViews(views)` | `void` | The saved-view set changed. Persist it. |
|
|
173
|
-
| `promptViewName(existing)` | `Promise<string \| null>` | A view is being saved. `existing` is the names already used. |
|
|
174
|
-
| `requestReload()` | `void` | The user asks to re-read the layout. |
|
|
175
|
-
| `setAutoReload(on)` | `void` | The user asks to always reload on change. |
|
|
176
|
-
| `onGotoResult({ok, x, y})` | `void` | A `goToPoint` call finished, reporting whether it landed inside. |
|
|
177
|
-
| `isLightTheme()` | `boolean` | The viewer needs to know the theme. Defaults to the OS preference. |
|
|
178
|
-
| `createWorker()` | `Worker` | The parse Worker is needed. Where the scripts cannot be fetched by URL, override this. |
|
|
179
|
-
| `connect(viewer)` | `void` | At mount, handing you the surface described in the following section. |
|
|
180
|
-
|
|
181
|
-
#### The viewer surface
|
|
182
|
-
|
|
183
|
-
`connect(viewer)` gives you the other direction, for pushing into the viewer
|
|
184
|
-
rather than answering it:
|
|
185
|
-
|
|
186
|
-
| Method | Description |
|
|
187
|
-
|---|---|
|
|
188
|
-
| `load(bytes, {reload})` | Display a layout from bytes. |
|
|
189
|
-
| `showError(message)` | Show a fatal error. |
|
|
190
|
-
| `setLyp(name, text)` | Apply layer properties. |
|
|
191
|
-
| `setMarkers(name, text)` | Apply a marker database. |
|
|
192
|
-
| `showStale(text)` | Offer a reload, for when the file changed underneath. |
|
|
193
|
-
| `goToPoint(x, y)` | Center on a coordinate. |
|
|
194
|
-
| `toggleDebug()` | Show or hide the debug panel. |
|
|
195
|
-
| `setNamedViews(views)` | Replace the saved-view set. |
|
|
196
|
-
| `applyTheme()` | Re-ask `isLightTheme()` after a theme change. |
|
|
197
|
-
| `element` | The `<gds-lens>` the viewer is mounted in. Bind anything of your own to this rather than to `window`, so it stays inside the component. |
|
|
198
|
-
|
|
199
|
-
#### Example: a custom host
|
|
200
|
-
|
|
201
|
-
```js
|
|
202
|
-
window.gdsLensHost = {
|
|
203
|
-
async pickLyp() {
|
|
204
|
-
const text = await fetch("/pdk/layers.lyp").then((r) => r.text());
|
|
205
|
-
return { name: "layers.lyp", text };
|
|
206
|
-
},
|
|
207
|
-
isLightTheme: () => document.documentElement.dataset.theme === "light",
|
|
208
|
-
connect(viewer) {
|
|
209
|
-
myApp.on("layout", (bytes) => viewer.load(bytes));
|
|
210
|
-
}
|
|
211
|
-
};
|
|
212
|
-
```
|
|
213
|
-
|
|
214
|
-
### Subpath exports
|
|
215
|
-
|
|
216
|
-
The parsers are pure JavaScript with no DOM and no WebAssembly, so they import
|
|
217
|
-
cleanly into Node, a worker, or an extension host. The package exposes the
|
|
218
|
-
following entry points:
|
|
219
|
-
|
|
220
|
-
| Import | Exports |
|
|
221
|
-
|---|---|
|
|
222
|
-
| `gds-lens` | `GdsLens`, and registers `<gds-lens>`. The bundled module — everything inlined |
|
|
223
|
-
| `gds-lens/parsers` | `parseMarkerFile`, `parseLyrdb`, `parseDrcAscii`, `sniffMarkerFormat`, `parsePointList`, `flattenMarkerModel` |
|
|
224
|
-
| `gds-lens/layout-bytes` | `decodeLayoutBytes`, `looksGzipped`, `gzipStoredSize` |
|
|
225
|
-
| `gds-lens/coord-parse` | `parseCoordinatePair` |
|
|
226
|
-
| `gds-lens/cell-search` | `rankCellMatches`, `cellPathToTarget` |
|
|
227
|
-
| `gds-lens/load-errors` | `describeLoadFailure`, `isOutOfMemory`, `describeDecodeFailure` |
|
|
228
|
-
| `gds-lens/hosts/browser` | `createBrowserHost` |
|
|
229
|
-
| `gds-lens/web/*` | The prebuilt payload, for hosts that serve files rather than bundle |
|
|
230
|
-
| `gds-lens/inline-wasm/*` | The same payload with the binary embedded, for hosts that cannot fetch |
|
|
231
|
-
| `gds-lens/esm/*` | The bundled module by path, if you would rather not rely on `.` |
|
|
232
|
-
|
|
233
|
-
TypeScript declarations ship for all of these.
|
|
234
|
-
|
|
235
|
-
#### The three builds
|
|
236
|
-
|
|
237
|
-
`import "gds-lens"` gets you `dist/esm/`, and that is the right default. The
|
|
238
|
-
other two are there for cases it cannot cover. The following table compares
|
|
239
|
-
them:
|
|
240
|
-
|
|
241
|
-
| Property | `esm` | `web` | `inline-wasm` |
|
|
242
|
-
|---|---|---|---|
|
|
243
|
-
| How it arrives | `import "gds-lens"` | scripts you serve | scripts you serve |
|
|
244
|
-
| Files to serve | none | 4 (+2 optional) | 3 (+2 optional) |
|
|
245
|
-
| Bundler configuration | none | N/A | N/A |
|
|
246
|
-
| Total transfer, gzipped | 252 KB | 235 KB | 243 KB |
|
|
247
|
-
| Streaming WebAssembly compile | no | yes | no |
|
|
248
|
-
| Binary cached separately from the JS | no | yes | no |
|
|
249
|
-
| Sensitive to the page's encoding | no | no | yes |
|
|
250
|
-
| Needs `blob:` in `script-src` | yes | no | no |
|
|
251
|
-
|
|
252
|
-
**`esm`** is one file with everything inside it: the markup, the styles,
|
|
253
|
-
lil-gui, the default host, the WebAssembly binary, and the parse worker's whole
|
|
254
|
-
script. Nothing is fetched, so nothing has to be served or copied, and no
|
|
255
|
-
bundler needs configuring. It costs the streaming compile and about 17 KB over
|
|
256
|
-
the payload.
|
|
257
|
-
|
|
258
|
-
Both the main thread and the parse worker need Emscripten's module, and a
|
|
259
|
-
worker cannot share the main thread's copy. Rather than inline it twice —
|
|
260
|
-
which would add about 190 KB gzipped for nothing — it is inlined once as text
|
|
261
|
-
and loaded from a `blob:` URL by both. That is the one thing this build asks
|
|
262
|
-
of a page's CSP that the others do not: `blob:` in `script-src`, on top of
|
|
263
|
-
the `worker-src blob:` all three need.
|
|
264
|
-
|
|
265
|
-
**`web`** is the payload to serve if you can. `gds-lens-engine.js` fetches
|
|
266
|
-
`gds-lens-engine.wasm` from beside it, so serve the two together; the binary
|
|
267
|
-
compiles as it streams and is cached on its own.
|
|
268
|
-
|
|
269
|
-
**`inline-wasm`** is `web` with the binary embedded in `gds-lens-engine.js`, for
|
|
270
|
-
hosts that cannot fetch a file next to their own scripts — a VS Code webview
|
|
271
|
-
cannot, from a Worker or from the main thread. Nothing else differs.
|
|
272
|
-
|
|
273
|
-
On that last row: `inline-wasm` embeds the binary as a raw string, so
|
|
274
|
-
`gds-lens-engine.js` has to be *decoded* as UTF-8 or it is corrupted, and the module
|
|
275
|
-
then fails with a `WebAssembly.instantiate()` error about section lengths that
|
|
276
|
-
says nothing about the cause. Either `Content-Type: text/javascript;
|
|
277
|
-
charset=utf-8` or `<meta charset="UTF-8">` on the page satisfies it; only the
|
|
278
|
-
absence of both breaks. That payload warns in the console when the document is
|
|
279
|
-
not UTF-8. `esm` escapes its non-ASCII, so it does not care.
|
|
280
|
-
|
|
281
|
-
#### Compressed layouts
|
|
282
|
-
|
|
283
|
-
Gzip is handled for you: `<gds-lens src="chip.gds.gz">` works, and so does a
|
|
284
|
-
plain `.gds` that is secretly gzipped, because the format is decided by magic
|
|
285
|
-
number rather than by filename. Expansion happens in JavaScript rather than
|
|
286
|
-
inside the WebAssembly heap, which is where a second copy of the
|
|
287
|
-
file is most expensive, and it is capped at 2 GB.
|
|
288
|
-
|
|
289
|
-
The same decoder is exported if you want it separately — to check a file before
|
|
290
|
-
handing it over, say:
|
|
291
|
-
|
|
292
|
-
```js
|
|
293
|
-
import { decodeLayoutBytes } from "gds-lens/layout-bytes";
|
|
294
|
-
|
|
295
|
-
const result = await decodeLayoutBytes(new Uint8Array(await file.arrayBuffer()));
|
|
296
|
-
if (result.ok) await viewer.load(result.bytes);
|
|
297
|
-
else console.error(result.reason); // "too-large" | "corrupt"
|
|
298
|
-
```
|
|
136
|
+
Nothing frees a parked viewer on its own. A page that creates viewers it will
|
|
137
|
+
never show again, and is hitting the context limit, can call `destroy()`; an
|
|
138
|
+
ordinary unmount should let it park. See
|
|
139
|
+
[Remounts and StrictMode](docs/react.md#remounts-and-strictmode) for what this
|
|
140
|
+
means in a framework.
|
|
299
141
|
|
|
300
142
|
### Limits
|
|
301
143
|
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
end to end, so a couple of million top-level polygons is the practical
|
|
308
|
-
ceiling.
|
|
309
|
-
- **Hierarchy is nearly free**. A cell placed eight or more times becomes a GPU
|
|
310
|
-
instance batch: 24 bytes per placement rather than a full geometry copy. A
|
|
311
|
-
design that flattens to 115 million polygons loads in about 2 GB.
|
|
312
|
-
|
|
313
|
-
Past that the module aborts, and the viewer turns the error into an explanation
|
|
314
|
-
rather than an engine string.
|
|
144
|
+
The parser and renderer run in a 32-bit WebAssembly address space, so a layout
|
|
145
|
+
has to fit — file plus geometry — inside it. See
|
|
146
|
+
[Limits](docs/embedding.md#limits) for the numbers and
|
|
147
|
+
[Compressed layouts](docs/embedding.md#compressed-layouts) for what that means
|
|
148
|
+
for gzipped files.
|
|
315
149
|
|
|
316
150
|
## Build from source
|
|
317
151
|
|