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.
- package/CHANGELOG.md +167 -0
- package/LICENCE.md +21 -0
- package/README.md +372 -0
- package/THIRD-PARTY-LICENSES.md +261 -0
- package/dist/esm/gds-lens.js +4731 -0
- package/dist/inline-wasm/gds-lens-engine.js +0 -0
- package/dist/inline-wasm/gds-lens-host.js +82 -0
- package/dist/inline-wasm/gds-lens-worker.js +118 -0
- package/dist/inline-wasm/gds-lens.html +29 -0
- package/dist/inline-wasm/gds-lens.js +4705 -0
- package/dist/web/gds-lens-engine.js +2 -0
- package/dist/web/gds-lens-engine.wasm +0 -0
- package/dist/web/gds-lens-host.js +82 -0
- package/dist/web/gds-lens-worker.js +118 -0
- package/dist/web/gds-lens.html +29 -0
- package/dist/web/gds-lens.js +4705 -0
- package/package.json +113 -0
- package/src/cell-search.js +88 -0
- package/src/coord-parse.js +50 -0
- package/src/engine-source.esm.js +49 -0
- package/src/engine-source.js +20 -0
- package/src/gds-lens.js +175 -0
- package/src/hosts/browser.js +156 -0
- package/src/layout-bytes.js +143 -0
- package/src/load-errors.js +64 -0
- package/src/marker-parsers.js +672 -0
- package/src/mount-target.js +23 -0
- package/src/viewer-shell.html +105 -0
- package/src/viewer.css +469 -0
- package/src/viewer.html +29 -0
- package/src/viewer.js +2688 -0
- package/src/wasm-worker.js +157 -0
- package/types/cell-search.d.ts +33 -0
- package/types/coord-parse.d.ts +11 -0
- package/types/gds-lens.d.ts +135 -0
- package/types/hosts-browser.d.ts +13 -0
- package/types/layout-bytes.d.ts +49 -0
- package/types/load-errors.d.ts +27 -0
- package/types/parsers.d.ts +84 -0
|
Binary file
|
|
@@ -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>
|