gds-lens 0.1.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.
@@ -0,0 +1,82 @@
1
+ (() => {
2
+ // src/hosts/browser.js
3
+ function createBrowserHost() {
4
+ const VIEWS_KEY = "gds-lens:named-views";
5
+ const readViews = () => {
6
+ try {
7
+ const parsed = JSON.parse(localStorage.getItem(VIEWS_KEY) || "[]");
8
+ return Array.isArray(parsed) ? parsed : [];
9
+ } catch {
10
+ return [];
11
+ }
12
+ };
13
+ const pickText = (accept) => new Promise((resolve) => {
14
+ const input = document.createElement("input");
15
+ input.type = "file";
16
+ input.accept = accept;
17
+ input.addEventListener("change", () => {
18
+ const file = input.files && input.files[0];
19
+ if (!file) return resolve(null);
20
+ file.text().then(
21
+ (text) => resolve({ name: file.name, text }),
22
+ () => resolve(null)
23
+ );
24
+ });
25
+ input.click();
26
+ });
27
+ return {
28
+ pickLyp: () => pickText(".lyp"),
29
+ pickMarkers: () => pickText(".lyrdb,.txt,.db"),
30
+ loadViews: async () => readViews(),
31
+ saveViews: (views) => {
32
+ try {
33
+ localStorage.setItem(VIEWS_KEY, JSON.stringify(views));
34
+ } catch {
35
+ }
36
+ },
37
+ // A plain page has to be told which layout to show. Three ways in,
38
+ // covering the three situations a page is actually in: a URL for an
39
+ // embed, a drop for a scratch look at a local file, and a direct call
40
+ // for a page driving the viewer itself (or a test).
41
+ connect: (viewer) => {
42
+ window.gdsLens = viewer;
43
+ const loadBytes = (bytes) => viewer.load(bytes, { reload: false });
44
+ const src = new URLSearchParams(location.search).get("src");
45
+ if (src) {
46
+ fetch(src).then((response) => {
47
+ if (!response.ok) throw new Error(`HTTP ${response.status}`);
48
+ return response.arrayBuffer();
49
+ }).then((buffer) => loadBytes(new Uint8Array(buffer))).catch((err) => viewer.showError(`Could not fetch ${src}: ${err.message || err}`));
50
+ }
51
+ const bindDrop = (target) => {
52
+ if (!target) return;
53
+ target.addEventListener("dragover", (event) => event.preventDefault());
54
+ target.addEventListener("drop", (event) => {
55
+ event.preventDefault();
56
+ const file = event.dataTransfer && event.dataTransfer.files[0];
57
+ if (!file) return;
58
+ file.arrayBuffer().then(
59
+ (buffer) => loadBytes(new Uint8Array(buffer)),
60
+ (err) => viewer.showError(`Could not read ${file.name}: ${err.message || err}`)
61
+ );
62
+ });
63
+ };
64
+ bindDrop(viewer.element || document.querySelector("gds-lens"));
65
+ if (typeof viewer.onAdopt === "function") viewer.onAdopt(bindDrop);
66
+ },
67
+ promptViewName: async (existingNames) => {
68
+ const name = window.prompt(
69
+ existingNames.length ? `Name this view (reusing a name replaces it):
70
+ ${existingNames.join(", ")}` : "Name this view:"
71
+ );
72
+ return name && name.trim() ? name.trim() : null;
73
+ }
74
+ // No requestReload / setAutoReload / onGotoResult: a plain page has no
75
+ // file on disk to watch and no command palette to answer. The viewer
76
+ // hides the stale-file banner when requestReload is missing.
77
+ };
78
+ }
79
+ if (typeof window !== "undefined" && !window.gdsLensHost) {
80
+ window.gdsLensHost = createBrowserHost();
81
+ }
82
+ })();
@@ -0,0 +1,118 @@
1
+ (() => {
2
+ // src/load-errors.js
3
+ var OOM_PATTERN = /memory access out of bounds|Cannot enlarge memory|Aborted\(|out of memory|Array buffer allocation failed|Invalid (typed )?array length/i;
4
+ var OOM_MESSAGE = "Out of memory: this layout is too large to open.\n\nThe viewer parses layouts in a 32-bit WebAssembly module, so the whole flattened design has to fit in 4 GB. Layouts that reuse cells (arrays and repeated placements) go much further than fully flattened ones, because a repeated cell is drawn as GPU instances instead of being copied for every placement.";
5
+ function describeLoadFailure(err, prefix) {
6
+ const text = err && err.message ? err.message : String(err);
7
+ if (OOM_PATTERN.test(text)) return `${OOM_MESSAGE}
8
+
9
+ (${text})`;
10
+ return prefix ? `${prefix}: ${text}` : text;
11
+ }
12
+
13
+ // src/wasm-worker.js
14
+ function safeStringify(arg) {
15
+ if (typeof arg === "string") return arg;
16
+ if (arg instanceof Error) return arg.stack || arg.message;
17
+ try {
18
+ return JSON.stringify(arg);
19
+ } catch {
20
+ return String(arg);
21
+ }
22
+ }
23
+ var originalLog = console.log.bind(console);
24
+ var originalError = console.error.bind(console);
25
+ console.log = (...args) => {
26
+ originalLog(...args);
27
+ try {
28
+ postMessage({ type: "gdsLog", level: "log", text: args.map(safeStringify).join(" ") });
29
+ } catch {
30
+ }
31
+ };
32
+ console.error = (...args) => {
33
+ originalError(...args);
34
+ try {
35
+ postMessage({ type: "gdsLog", level: "error", text: args.map(safeStringify).join(" ") });
36
+ } catch {
37
+ }
38
+ };
39
+ console.log("[GDS worker] script started executing, typeof createGdstkModule:", typeof createGdstkModule);
40
+ self.onerror = (msg, url, line, col, err) => {
41
+ console.error("[GDS worker] self.onerror:", msg, "at", url + ":" + line + ":" + col, err && err.stack);
42
+ };
43
+ self.addEventListener("unhandledrejection", (event) => {
44
+ console.error("[GDS worker] unhandled promise rejection inside worker:", event.reason);
45
+ });
46
+ function moduleArgs() {
47
+ const base = self.gdsLensScriptBase;
48
+ if (!base) return {};
49
+ return { locateFile: (file) => new URL(file, base).href };
50
+ }
51
+ console.log("[GDS worker] registering onmessage handler");
52
+ self.onmessage = (event) => {
53
+ const message = event.data;
54
+ console.log("[GDS worker] received message, type:", message.type);
55
+ if (message.type !== "parse") return;
56
+ console.log("[GDS worker] fileData byteLength:", message.fileData && message.fileData.byteLength);
57
+ console.log("[GDS worker] calling createGdstkModule()...");
58
+ createGdstkModule(moduleArgs()).then((Module) => {
59
+ console.log("[GDS worker] createGdstkModule() resolved, Module keys:", Object.keys(Module).filter((k) => typeof Module[k] === "function"));
60
+ console.log("[GDS worker] writing /input.layout to MEMFS...");
61
+ Module.FS.writeFile("/input.layout", new Uint8Array(message.fileData));
62
+ console.log("[GDS worker] calling Module.parseGdsToLayers('/input.layout')...");
63
+ const result = Module.parseGdsToLayers("/input.layout");
64
+ console.log("[GDS worker] parseGdsToLayers returned, ok:", result.ok, "format:", result.format, "error:", result.error);
65
+ Module.FS.unlink("/input.layout");
66
+ if (!result.ok) {
67
+ postMessage({ type: "gdsResult", ok: false, error: result.error });
68
+ return;
69
+ }
70
+ console.log("[GDS worker] layers:", result.layers.length, "instance groups:", result.instanceGroups.length, "labels:", result.totalLabels, "cells:", result.hierarchy.cellCount, "-- posting gdsResult back to main thread");
71
+ const transferList = [];
72
+ for (const layer of result.layers) {
73
+ transferList.push(
74
+ layer.outlineVertices.buffer,
75
+ layer.outlineRanges.buffer,
76
+ layer.fillVertices.buffer
77
+ );
78
+ transferList.push(
79
+ layer.textChars.buffer,
80
+ layer.textLengths.buffer,
81
+ layer.textOrigins.buffer,
82
+ layer.textAnchors.buffer
83
+ );
84
+ }
85
+ for (const group of result.instanceGroups) {
86
+ transferList.push(group.instances.buffer);
87
+ for (const layer of group.layers) {
88
+ transferList.push(
89
+ layer.outlineVertices.buffer,
90
+ layer.outlineRanges.buffer,
91
+ layer.fillVertices.buffer
92
+ );
93
+ }
94
+ }
95
+ for (const cell of result.hierarchy.cells) {
96
+ for (const ref of cell.refs) {
97
+ if (ref.placements) transferList.push(ref.placements.buffer);
98
+ }
99
+ }
100
+ postMessage(
101
+ {
102
+ type: "gdsResult",
103
+ ok: true,
104
+ layers: result.layers,
105
+ instanceGroups: result.instanceGroups,
106
+ hierarchy: result.hierarchy,
107
+ bbox: result.bbox
108
+ },
109
+ transferList
110
+ );
111
+ console.log("[GDS worker] postMessage(gdsResult) call returned");
112
+ }).catch((err) => {
113
+ console.error("[GDS worker] createGdstkModule() chain rejected:", err, err && err.stack);
114
+ postMessage({ type: "gdsResult", ok: false, error: describeLoadFailure(err, "Layout worker failed") });
115
+ });
116
+ };
117
+ console.log("[GDS worker] onmessage handler registered, script finished top-level execution");
118
+ })();
@@ -0,0 +1,29 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta http-equiv="Content-Security-Policy"
6
+ content="default-src 'none'; style-src 'unsafe-inline'; script-src 'self' 'wasm-unsafe-eval'; worker-src blob:; connect-src 'self';">
7
+ <title>GDS Lens</title>
8
+ <style>
9
+ /* The page harness only. Everything the viewer itself draws is styled
10
+ inside its shadow root (see viewer.css), which is why there is so
11
+ little here: a host page cannot reach in, and this cannot leak out. */
12
+ html, body { margin: 0; padding: 0; height: 100%; background: #1a1a1a; }
13
+ gds-lens { display: block; width: 100%; height: 100%; }
14
+ </style>
15
+ </head>
16
+ <body>
17
+ <gds-lens></gds-lens>
18
+
19
+ <!-- Emscripten's output, loaded as a classic script for the
20
+ createGdstkModule global it defines. -->
21
+ <script src="gds-lens-engine.js"></script>
22
+ <!-- The embedder's ViewerHost. Must precede gds-lens.js, which reads
23
+ window.gdsLensHost as it starts. Defaults to the plain-page host; a
24
+ host that needs different services replaces this file, or sets
25
+ window.gdsLensHost itself and omits it. -->
26
+ <script src="gds-lens-host.js"></script>
27
+ <script src="gds-lens.js"></script>
28
+ </body>
29
+ </html>