gds-lens 1.0.1 → 1.0.2

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,32 @@ 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.0.2] - 2026-08-25
11
+
12
+ ### Changed
13
+
14
+ - Marker-file warnings are spelled out rather than counted. The `⚠ N warnings`
15
+ row is a folder now, with one wrapping row per warning inside it. The
16
+ sentences used to live only in a hover title and a `?gdsDebug=1` console
17
+ line, so the one thing the row existed to report -- a marker may be in the
18
+ wrong place, a value was not drawn -- was the one thing it did not say.
19
+
20
+ ### Fixed
21
+
22
+ - Right-click -> "Copy coordinate" did nothing. The menu is dismissed by a
23
+ `pointerdown` listener on `window`, which asked whether the menu contained
24
+ `event.target` -- but the viewer moved into a shadow root in 1.0.0, and an
25
+ event from inside one is retargeted to the `<gds-lens>` host by the time it
26
+ reaches `window`. The menu therefore hid itself (`display: none`) on the very
27
+ press that was landing on its own item, so the button never became a click.
28
+ The menu opened and showed the right coordinate throughout, which is what
29
+ made it look alive.
30
+ - The keyboard guard that keeps `m`, `h` and `/` from reaching the viewer while
31
+ someone is typing in one of lil-gui's text boxes, broken by the same
32
+ retargeting: it saw the host element rather than the focused input for every
33
+ keystroke, so typing in a filter box could switch modes, toggle the hierarchy
34
+ tree, or move focus.
35
+
10
36
  ## [1.0.1] - 2026-08-25
11
37
 
12
38
  ### Fixed
@@ -302,7 +328,8 @@ web page rather than for a webview.
302
328
  worker-loading route and shipped unsubstituted. The `createWorker` host hook
303
329
  replaces it.
304
330
 
305
- [Unreleased]: https://github.com/EthanLowenthal/GDS-Lens/compare/v1.0.1...HEAD
331
+ [Unreleased]: https://github.com/EthanLowenthal/GDS-Lens/compare/v1.0.2...HEAD
332
+ [1.0.2]: https://github.com/EthanLowenthal/GDS-Lens/compare/v1.0.1...v1.0.2
306
333
  [1.0.1]: https://github.com/EthanLowenthal/GDS-Lens/compare/v1.0.0...v1.0.1
307
334
  [1.0.0]: https://github.com/EthanLowenthal/GDS-Lens/compare/v0.1.1...v1.0.0
308
335
  [0.1.1]: https://github.com/EthanLowenthal/GDS-Lens/compare/v0.1.0...v0.1.1
@@ -1052,6 +1052,17 @@ var init_viewer = __esm({
1052
1052
  .lil-gui .lil-controller.marker-selected button { background: var(--select-bg) !important; }
1053
1053
  .lil-gui .lil-controller.marker-selected .lil-name { color: var(--select-fg); }
1054
1054
  .lil-gui .lil-controller.marker-more-row { opacity: 0.55; pointer-events: none; }
1055
+ /* Warning rows inside the marker browser's "\u26A0 N warnings" folder.
1056
+ A warning is a sentence, not a value, so the row drops lil-gui's
1057
+ fixed widget height and single line and wraps instead -- a clipped
1058
+ warning is no more use than the bare count was. Inert: there is
1059
+ nothing to click. */
1060
+ .lil-gui .lil-controller.marker-warning-row { pointer-events: none; }
1061
+ .lil-gui .lil-controller.marker-warning-row button { height: auto; min-height: var(--widget-height);
1062
+ padding: 4px 7px; text-align: left;
1063
+ color: var(--danger-dim); background: none; }
1064
+ .lil-gui .lil-controller.marker-warning-row .lil-name { white-space: normal; overflow-wrap: anywhere;
1065
+ line-height: 1.35; }
1055
1066
  /* ---- Hierarchy tree (see renderHierarchy in viewer.js) ----
1056
1067
  The design's cell tree, down the left edge. Like the lil-gui panel
1057
1068
  on the right it floats over the canvas rather than taking width
@@ -4038,9 +4049,17 @@ No top cell places this one, so the tree has no branch that reaches it.`;
4038
4049
  markersFolder.open();
4039
4050
  if (model.warnings.length > 0) {
4040
4051
  fail("[GDS] marker warnings:", model.warnings.join(" | "));
4041
- const row = markersFolder.add({ w: () => {
4042
- } }, "w").name(`\u26A0 ${model.warnings.length} warning${model.warnings.length === 1 ? "" : "s"}`);
4043
- row.domElement.title = model.warnings.join("\n");
4052
+ const warningsFolder = markersFolder.addFolder(
4053
+ `\u26A0 ${model.warnings.length} warning${model.warnings.length === 1 ? "" : "s"}`
4054
+ );
4055
+ warningsFolder.close();
4056
+ warningsFolder.domElement.title = model.warnings.join("\n");
4057
+ for (const warning of model.warnings) {
4058
+ const row = warningsFolder.add({ w: () => {
4059
+ } }, "w").name(warning);
4060
+ row.domElement.classList.add("marker-warning-row");
4061
+ row.domElement.title = warning;
4062
+ }
4044
4063
  }
4045
4064
  const opacityController = markersFolder.add(markerUiState, "opacity", 0, 1, 0.05).name("Opacity").onChange((value) => modulePromise.then((Module) => Module.setMarkerOpacity(value)));
4046
4065
  opacityController.domElement.title = "Opacity of the whole marker overlay";
@@ -4142,7 +4161,7 @@ No top cell places this one, so the tree has no branch that reaches it.`;
4142
4161
  );
4143
4162
  });
4144
4163
  window.addEventListener("pointerdown", (event) => {
4145
- if (!canvasMenuEl.contains(event.target)) hideCanvasMenu();
4164
+ if (!event.composedPath().includes(canvasMenuEl)) hideCanvasMenu();
4146
4165
  }, true);
4147
4166
  window.addEventListener("wheel", hideCanvasMenu, { passive: true });
4148
4167
  window.addEventListener("resize", hideCanvasMenu);
@@ -4150,7 +4169,7 @@ No top cell places this one, so the tree has no branch that reaches it.`;
4150
4169
  }
4151
4170
  window.addEventListener("keydown", (event) => {
4152
4171
  if (!hasKeyboard()) return;
4153
- const t = event.target;
4172
+ const t = event.composedPath()[0] || event.target;
4154
4173
  const tag = t && t.tagName;
4155
4174
  if (tag === "TEXTAREA" || tag === "INPUT" && t.type !== "checkbox") return;
4156
4175
  if (event.key === "[") stepMarker(-1);
@@ -1028,6 +1028,17 @@ Usually a truncated or half-written file -- a copy that was interrupted, or a do
1028
1028
  .lil-gui .lil-controller.marker-selected button { background: var(--select-bg) !important; }
1029
1029
  .lil-gui .lil-controller.marker-selected .lil-name { color: var(--select-fg); }
1030
1030
  .lil-gui .lil-controller.marker-more-row { opacity: 0.55; pointer-events: none; }
1031
+ /* Warning rows inside the marker browser's "\u26A0 N warnings" folder.
1032
+ A warning is a sentence, not a value, so the row drops lil-gui's
1033
+ fixed widget height and single line and wraps instead -- a clipped
1034
+ warning is no more use than the bare count was. Inert: there is
1035
+ nothing to click. */
1036
+ .lil-gui .lil-controller.marker-warning-row { pointer-events: none; }
1037
+ .lil-gui .lil-controller.marker-warning-row button { height: auto; min-height: var(--widget-height);
1038
+ padding: 4px 7px; text-align: left;
1039
+ color: var(--danger-dim); background: none; }
1040
+ .lil-gui .lil-controller.marker-warning-row .lil-name { white-space: normal; overflow-wrap: anywhere;
1041
+ line-height: 1.35; }
1031
1042
  /* ---- Hierarchy tree (see renderHierarchy in viewer.js) ----
1032
1043
  The design's cell tree, down the left edge. Like the lil-gui panel
1033
1044
  on the right it floats over the canvas rather than taking width
@@ -4014,9 +4025,17 @@ No top cell places this one, so the tree has no branch that reaches it.`;
4014
4025
  markersFolder.open();
4015
4026
  if (model.warnings.length > 0) {
4016
4027
  fail("[GDS] marker warnings:", model.warnings.join(" | "));
4017
- const row = markersFolder.add({ w: () => {
4018
- } }, "w").name(`\u26A0 ${model.warnings.length} warning${model.warnings.length === 1 ? "" : "s"}`);
4019
- row.domElement.title = model.warnings.join("\n");
4028
+ const warningsFolder = markersFolder.addFolder(
4029
+ `\u26A0 ${model.warnings.length} warning${model.warnings.length === 1 ? "" : "s"}`
4030
+ );
4031
+ warningsFolder.close();
4032
+ warningsFolder.domElement.title = model.warnings.join("\n");
4033
+ for (const warning of model.warnings) {
4034
+ const row = warningsFolder.add({ w: () => {
4035
+ } }, "w").name(warning);
4036
+ row.domElement.classList.add("marker-warning-row");
4037
+ row.domElement.title = warning;
4038
+ }
4020
4039
  }
4021
4040
  const opacityController = markersFolder.add(markerUiState, "opacity", 0, 1, 0.05).name("Opacity").onChange((value) => modulePromise.then((Module) => Module.setMarkerOpacity(value)));
4022
4041
  opacityController.domElement.title = "Opacity of the whole marker overlay";
@@ -4118,7 +4137,7 @@ No top cell places this one, so the tree has no branch that reaches it.`;
4118
4137
  );
4119
4138
  });
4120
4139
  window.addEventListener("pointerdown", (event) => {
4121
- if (!canvasMenuEl.contains(event.target)) hideCanvasMenu();
4140
+ if (!event.composedPath().includes(canvasMenuEl)) hideCanvasMenu();
4122
4141
  }, true);
4123
4142
  window.addEventListener("wheel", hideCanvasMenu, { passive: true });
4124
4143
  window.addEventListener("resize", hideCanvasMenu);
@@ -4126,7 +4145,7 @@ No top cell places this one, so the tree has no branch that reaches it.`;
4126
4145
  }
4127
4146
  window.addEventListener("keydown", (event) => {
4128
4147
  if (!hasKeyboard()) return;
4129
- const t = event.target;
4148
+ const t = event.composedPath()[0] || event.target;
4130
4149
  const tag = t && t.tagName;
4131
4150
  if (tag === "TEXTAREA" || tag === "INPUT" && t.type !== "checkbox") return;
4132
4151
  if (event.key === "[") stepMarker(-1);
@@ -1028,6 +1028,17 @@ Usually a truncated or half-written file -- a copy that was interrupted, or a do
1028
1028
  .lil-gui .lil-controller.marker-selected button { background: var(--select-bg) !important; }
1029
1029
  .lil-gui .lil-controller.marker-selected .lil-name { color: var(--select-fg); }
1030
1030
  .lil-gui .lil-controller.marker-more-row { opacity: 0.55; pointer-events: none; }
1031
+ /* Warning rows inside the marker browser's "\u26A0 N warnings" folder.
1032
+ A warning is a sentence, not a value, so the row drops lil-gui's
1033
+ fixed widget height and single line and wraps instead -- a clipped
1034
+ warning is no more use than the bare count was. Inert: there is
1035
+ nothing to click. */
1036
+ .lil-gui .lil-controller.marker-warning-row { pointer-events: none; }
1037
+ .lil-gui .lil-controller.marker-warning-row button { height: auto; min-height: var(--widget-height);
1038
+ padding: 4px 7px; text-align: left;
1039
+ color: var(--danger-dim); background: none; }
1040
+ .lil-gui .lil-controller.marker-warning-row .lil-name { white-space: normal; overflow-wrap: anywhere;
1041
+ line-height: 1.35; }
1031
1042
  /* ---- Hierarchy tree (see renderHierarchy in viewer.js) ----
1032
1043
  The design's cell tree, down the left edge. Like the lil-gui panel
1033
1044
  on the right it floats over the canvas rather than taking width
@@ -4014,9 +4025,17 @@ No top cell places this one, so the tree has no branch that reaches it.`;
4014
4025
  markersFolder.open();
4015
4026
  if (model.warnings.length > 0) {
4016
4027
  fail("[GDS] marker warnings:", model.warnings.join(" | "));
4017
- const row = markersFolder.add({ w: () => {
4018
- } }, "w").name(`\u26A0 ${model.warnings.length} warning${model.warnings.length === 1 ? "" : "s"}`);
4019
- row.domElement.title = model.warnings.join("\n");
4028
+ const warningsFolder = markersFolder.addFolder(
4029
+ `\u26A0 ${model.warnings.length} warning${model.warnings.length === 1 ? "" : "s"}`
4030
+ );
4031
+ warningsFolder.close();
4032
+ warningsFolder.domElement.title = model.warnings.join("\n");
4033
+ for (const warning of model.warnings) {
4034
+ const row = warningsFolder.add({ w: () => {
4035
+ } }, "w").name(warning);
4036
+ row.domElement.classList.add("marker-warning-row");
4037
+ row.domElement.title = warning;
4038
+ }
4020
4039
  }
4021
4040
  const opacityController = markersFolder.add(markerUiState, "opacity", 0, 1, 0.05).name("Opacity").onChange((value) => modulePromise.then((Module) => Module.setMarkerOpacity(value)));
4022
4041
  opacityController.domElement.title = "Opacity of the whole marker overlay";
@@ -4118,7 +4137,7 @@ No top cell places this one, so the tree has no branch that reaches it.`;
4118
4137
  );
4119
4138
  });
4120
4139
  window.addEventListener("pointerdown", (event) => {
4121
- if (!canvasMenuEl.contains(event.target)) hideCanvasMenu();
4140
+ if (!event.composedPath().includes(canvasMenuEl)) hideCanvasMenu();
4122
4141
  }, true);
4123
4142
  window.addEventListener("wheel", hideCanvasMenu, { passive: true });
4124
4143
  window.addEventListener("resize", hideCanvasMenu);
@@ -4126,7 +4145,7 @@ No top cell places this one, so the tree has no branch that reaches it.`;
4126
4145
  }
4127
4146
  window.addEventListener("keydown", (event) => {
4128
4147
  if (!hasKeyboard()) return;
4129
- const t = event.target;
4148
+ const t = event.composedPath()[0] || event.target;
4130
4149
  const tag = t && t.tagName;
4131
4150
  if (tag === "TEXTAREA" || tag === "INPUT" && t.type !== "checkbox") return;
4132
4151
  if (event.key === "[") stepMarker(-1);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gds-lens",
3
- "version": "1.0.1",
3
+ "version": "1.0.2",
4
4
  "description": "GDSII & OASIS layout parsing and WebGL2 rendering for the browser",
5
5
  "license": "MIT",
6
6
  "author": "Ethan Lowenthal",
package/src/viewer.css CHANGED
@@ -267,6 +267,17 @@
267
267
  .lil-gui .lil-controller.marker-selected button { background: var(--select-bg) !important; }
268
268
  .lil-gui .lil-controller.marker-selected .lil-name { color: var(--select-fg); }
269
269
  .lil-gui .lil-controller.marker-more-row { opacity: 0.55; pointer-events: none; }
270
+ /* Warning rows inside the marker browser's "⚠ N warnings" folder.
271
+ A warning is a sentence, not a value, so the row drops lil-gui's
272
+ fixed widget height and single line and wraps instead -- a clipped
273
+ warning is no more use than the bare count was. Inert: there is
274
+ nothing to click. */
275
+ .lil-gui .lil-controller.marker-warning-row { pointer-events: none; }
276
+ .lil-gui .lil-controller.marker-warning-row button { height: auto; min-height: var(--widget-height);
277
+ padding: 4px 7px; text-align: left;
278
+ color: var(--danger-dim); background: none; }
279
+ .lil-gui .lil-controller.marker-warning-row .lil-name { white-space: normal; overflow-wrap: anywhere;
280
+ line-height: 1.35; }
270
281
  /* ---- Hierarchy tree (see renderHierarchy in viewer.js) ----
271
282
  The design's cell tree, down the left edge. Like the lil-gui panel
272
283
  on the right it floats over the canvas rather than taking width
package/src/viewer.js CHANGED
@@ -1776,9 +1776,20 @@ export function createViewer(mountTarget) {
1776
1776
 
1777
1777
  if (model.warnings.length > 0) {
1778
1778
  fail("[GDS] marker warnings:", model.warnings.join(" | "));
1779
- const row = markersFolder.add({ w: () => {} }, "w")
1780
- .name(`⚠ ${model.warnings.length} warning${model.warnings.length === 1 ? "" : "s"}`);
1781
- row.domElement.title = model.warnings.join("\n");
1779
+ // A count on its own is a dead end -- "2 warnings" does not say
1780
+ // whether the file lost half its results or merely used a value
1781
+ // type we draw as text. So the count is a folder and the sentences
1782
+ // are inside it, rather than living only in a hover title and a
1783
+ // console line nobody has open.
1784
+ const warningsFolder = markersFolder.addFolder(
1785
+ `⚠ ${model.warnings.length} warning${model.warnings.length === 1 ? "" : "s"}`);
1786
+ warningsFolder.close();
1787
+ warningsFolder.domElement.title = model.warnings.join("\n");
1788
+ for (const warning of model.warnings) {
1789
+ const row = warningsFolder.add({ w: () => {} }, "w").name(warning);
1790
+ row.domElement.classList.add("marker-warning-row");
1791
+ row.domElement.title = warning;
1792
+ }
1782
1793
  }
1783
1794
 
1784
1795
  const opacityController = markersFolder.add(markerUiState, "opacity", 0, 1, 0.05).name("Opacity")
@@ -1948,7 +1959,13 @@ export function createViewer(mountTarget) {
1948
1959
  // on it), zooming or panning the layout out from under it, and leaving the
1949
1960
  // webview. Escape is handled with the other keys below.
1950
1961
  window.addEventListener("pointerdown", (event) => {
1951
- if (!canvasMenuEl.contains(event.target)) hideCanvasMenu();
1962
+ // composedPath() rather than event.target: this listener sits on
1963
+ // window, outside the shadow root, so by the time a pointerdown
1964
+ // inside the viewer arrives here it has been retargeted to the
1965
+ // <gds-lens> host -- the menu never "contains" it. That closed the
1966
+ // menu (display: none) on the press that was landing on the menu
1967
+ // item, so the button never got a click and the copy never ran.
1968
+ if (!event.composedPath().includes(canvasMenuEl)) hideCanvasMenu();
1952
1969
  }, true);
1953
1970
  window.addEventListener("wheel", hideCanvasMenu, { passive: true });
1954
1971
  window.addEventListener("resize", hideCanvasMenu);
@@ -1967,7 +1984,11 @@ export function createViewer(mountTarget) {
1967
1984
  if (!hasKeyboard()) return;
1968
1985
  // Don't hijack typing in lil-gui's text/number inputs -- but focused
1969
1986
  // checkboxes and buttons must not block marker stepping.
1970
- const t = event.target;
1987
+ // composedPath()[0], for the same retargeting reason as the canvas
1988
+ // menu's dismissal above: event.target here is the <gds-lens> host, so
1989
+ // this guard used to see "GDS-LENS" for every keystroke and let m/h//
1990
+ // through while someone was typing in a lil-gui text box.
1991
+ const t = event.composedPath()[0] || event.target;
1971
1992
  const tag = t && t.tagName;
1972
1993
  if (tag === "TEXTAREA" || (tag === "INPUT" && t.type !== "checkbox")) return;
1973
1994
  if (event.key === "[") stepMarker(-1);