gds-lens 1.2.0 → 1.3.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 CHANGED
@@ -7,6 +7,71 @@ follows [semantic versioning](https://semver.org/spec/v2.0.0.html).
7
7
  From 1.0.0 on, a breaking change to the element's API waits for a major
8
8
  version. Before that, `0.x` releases changed it freely.
9
9
 
10
+ ## [1.3.0] - 2026-09-14
11
+
12
+ ### Added
13
+
14
+ - **Two layouts in one viewer.** A `<gds-lens>` can hold two layouts at once
15
+ and draw them through one camera into one canvas, for comparing two
16
+ revisions of a design:
17
+
18
+ ```js
19
+ await element.load(oldBytes);
20
+ await element.load(newBytes, { slot: "b", name: "rev-b.gds" });
21
+ await element.setBlend(0.5);
22
+ ```
23
+
24
+ - `load(source, { slot })` picks which layout to replace -- `"a"` (the
25
+ default, and the only one a single-layout viewer ever uses) or `"b"`. A
26
+ second layout arriving does not move the camera off what is already being
27
+ read, and loading either slot leaves the other alone.
28
+ - `unload(slot)` drops one again; `setBlend(t)` / `getBlend()` crossfade
29
+ between them, `0` showing only A, `1` only B, between overlaying them.
30
+ - With two loaded, the panel grows a **Compare** folder: the blend slider,
31
+ an optional per-layout tint, and **Highlight differences**, which marks
32
+ per (layer, datatype) where the two disagree -- one colour where only A
33
+ has geometry, another where only B does. Both rasterize through the same
34
+ camera into the same coverage mask in the same frame, so identical
35
+ geometry cancels exactly. It is a difference of what is drawn at the zoom
36
+ being viewed, not a geometric XOR, and differences under about half a
37
+ pixel are ignored; zoom in to resolve a smaller one.
38
+ - The layer list shows the **union** of both layouts' layers, chipped A or B
39
+ where only one has it, so an added or removed layer is visible as a row
40
+ rather than absent. The hierarchy browser roots both cell trees, and cell
41
+ and label searches run over both with the same chips on the hits.
42
+ - `getLayers()` entries gain `source` (0 for slot A, 1 for B), and
43
+ `gds-load`'s detail gains `slot`.
44
+
45
+ See [docs/embedding.md](docs/embedding.md#two-layouts-in-one-viewer). The
46
+ single-layout case is unchanged in every respect: no attribute, no mode
47
+ flag, and the Compare folder is not built at all until a second layout is
48
+ loaded.
49
+
50
+ - **gdsfactory / kfactory ports.** kfactory records each cell's ports as
51
+ KLayout meta info, which KLayout writes into the layout file itself: a
52
+ `$$$CONTEXT_INFO$$$` cell in GDSII, `KLAYOUT_CONTEXT` properties in OASIS,
53
+ each holding strings like `META('kfactory:ports:0')={'name'=>'o1',...}`. The
54
+ viewer now reads those back (`src/wasm/kfactory_ports.cpp`) -- name, type,
55
+ position, direction, width and layer, through the named cross-section --
56
+ and expands them through every placement, so a component's ports are marked
57
+ wherever it sits in the design: a bar across the port, an arrow the way it
58
+ faces, and its name once few enough are on screen to read. Optical ports
59
+ are orange, electrical green, anything else the highlight blue. A **Ports**
60
+ toggle in Display turns the overlay off; a **Ports** folder lists the top
61
+ cell's ports and centers the view on one when clicked. Each hierarchy cell
62
+ entry carries a `ports` array and the hierarchy a `portCount`, the
63
+ `gds-load` event's detail gains `portCount`, and the element exposes nothing
64
+ new otherwise -- a file without the metadata looks exactly as before. The
65
+ expansion stops at 200,000 port placements and says so in the panel.
66
+
67
+ - **`getCamera()` / `setCamera({zoom, panX, panY})`,
68
+ `getMeasurements()` / `addMeasurement(x0, y0, x1, y1)` /
69
+ `clearMeasurements()`, and `getLayers()` / `setLayerVisible(...)`** on the
70
+ viewer surface and the element, for an app that wants to frame the view,
71
+ place a ruler or drive layer visibility itself. Thin pass-throughs over
72
+ behavior that already existed internally; `getLayers()` is also the only way
73
+ to read a layer's name, group, colors and shape counts.
74
+
10
75
  ## [1.2.0] - 2026-09-03
11
76
 
12
77
  ### Added
package/README.md CHANGED
@@ -65,6 +65,14 @@ a streaming WebAssembly compile — see
65
65
  and ASCII DRC results.
66
66
  - **Navigates hierarchy**. You can search cells and labels, and measure
67
67
  distances.
68
+ - **Shows gdsfactory / kfactory ports**. A layout written by gdsfactory 8+
69
+ carries its ports as KLayout metadata inside the file; the viewer reads them
70
+ back, draws every port as a bar across its width with an arrow the way it
71
+ faces (and its name, close in), and lists the top cell's ports in the panel.
72
+ No Python involved.
73
+ - **Compares two layouts**. One viewer can hold two of them, drawn through one
74
+ camera, with a crossfade between them and a per-layer highlight of where they
75
+ differ. See [Two layouts in one viewer](docs/embedding.md#two-layouts-in-one-viewer).
68
76
 
69
77
  ## Installation
70
78
 
@@ -91,8 +99,6 @@ takes one attribute and exposes a handful of methods.
91
99
 
92
100
  #### Attributes
93
101
 
94
- The element takes one attribute:
95
-
96
102
  | Attribute | Description |
97
103
  |---|---|
98
104
  | `src` | URL of a layout to fetch and display. Setting it later reloads. |
@@ -104,26 +110,35 @@ The element exposes the following members:
104
110
  | Member | Returns | Description |
105
111
  |---|---|---|
106
112
  | `ready` | `Promise<ViewerSurface>` | Resolves once the engine has mounted. Every method in the following table awaits this, so you rarely need it directly. |
107
- | `load(source, options?)` | `Promise<void>` | `source` is a URL string, a `Uint8Array`, or an `ArrayBuffer`. `options.reload` keeps the current camera and layer visibility instead of framing the design. Resolves once the layout is on screen; rejects on a failed fetch, a file the parser refuses, or — with an error named `AbortError` — when a later load superseded this one. |
113
+ | `load(source, options?)` | `Promise<void>` | `source` is a URL string, a `Uint8Array`, or an `ArrayBuffer`. `options.reload` keeps the current camera and layer visibility instead of framing the design. `options.slot` is `"a"` (the default) or `"b"`, which loads a second layout alongside the first to compare them — see [Two layouts in one viewer](docs/embedding.md#two-layouts-in-one-viewer). Resolves once the layout is on screen; rejects on a failed fetch, a file the parser refuses, or — with an error named `AbortError` — when a later load into the same slot superseded this one. |
108
114
  | `showLoading(label?)` | `Promise<void>` | Shows the loading overlay, for the wait before a `load()` of bytes the page is fetching itself. `load(url)` does this on its own. |
109
115
  | `goToPoint(x, y)` | `Promise<boolean>` | Centers on a coordinate in microns and flashes a crosshair. Resolves `true` if the point is inside the layout. |
110
116
  | `setLyp(name, text)` | `Promise<void>` | Applies a `.lyp` layer-properties file. Pass `""` to clear. |
111
117
  | `setMarkers(name, text)` | `Promise<void>` | Applies a marker database. The viewer detects the format from the content. |
112
118
  | `showError(message)` | `Promise<void>` | Replaces the view with an error message. |
113
119
  | `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). |
114
-
115
- Every `load()` cancels the one before it, so two quick changes to `src` show
116
- the second layout even when the first is the slower download.
120
+ | `getCamera()` | `Promise<{zoom, panX, panY}>` | The current pan/zoom. |
121
+ | `setCamera(camera)` | `Promise<void>` | Sets the pan/zoom. Clamped to the loaded design's bounds (the union of both, with two layouts loaded). |
122
+ | `getLayers()` | `Promise<LayerInfo[]>` | Every layer's name, group, colors, visibility and shape counts, plus the `source` slot it came from. |
123
+ | `setLayerVisible(layer, datatype, visible)` | `Promise<void>` | Shows or hides one layer. |
124
+ | `getMeasurements()` | `Promise<{x0,y0,x1,y1}[]>` | Every ruler on screen, in micron endpoints. |
125
+ | `addMeasurement(x0, y0, x1, y1)` | `Promise<void>` | Places a finished ruler without disturbing measure mode. |
126
+ | `clearMeasurements()` | `Promise<void>` | Removes every ruler. Rulers don't survive a reload either way. |
127
+ | `unload(slot?)` | `Promise<void>` | Drops the second layout, leaving a single-layout viewer. |
128
+ | `setBlend(t)` | `Promise<void>` | Crossfades two loaded layouts: `0` shows only A, `1` only B, between overlays them. |
129
+ | `getBlend()` | `Promise<number>` | The current crossfade. |
130
+
131
+ Every `load()` cancels the one before it *for the same slot*, so two quick
132
+ changes to `src` show the second layout even when the first is the slower
133
+ download.
117
134
 
118
135
  #### Events
119
136
 
120
- The element dispatches two events on itself, whichever way a load was started
121
- — the `src` attribute, `load()`, or a host pushing bytes through its surface.
122
- Neither bubbles.
137
+ The element dispatches events on itself. None bubbles.
123
138
 
124
139
  | Event | `detail` | When |
125
140
  |---|---|---|
126
- | `gds-load` | `{ layerCount, cellCount }` | A layout finished loading and is on screen. |
141
+ | `gds-load` | `{ slot, layerCount, cellCount, portCount }` | A layout finished loading and is on screen, whichever way the load was started — the `src` attribute, `load()`, or a host pushing bytes through its surface. `slot` is which of the two layouts it is. `portCount` is how many ports its kfactory metadata declared, 0 for a plain file. |
127
142
  | `gds-error` | `{ message }` | A load failed, or `showError()` was called. `message` is the text the viewer shows. |
128
143
 
129
144
  ```js