@yumiai/chat-widget 0.1.2 → 0.2.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 +92 -0
- package/README.md +119 -22
- package/dist/ExcelCore-DJOIVQMI.js +11 -0
- package/dist/ExcelCore-DJOIVQMI.js.map +1 -0
- package/dist/ExcelViewer-3YLLYYIQ.js +65 -0
- package/dist/ExcelViewer-3YLLYYIQ.js.map +1 -0
- package/dist/GerberViewerA2UI-7CNT7HX4.css +693 -0
- package/dist/GerberViewerA2UI-7CNT7HX4.css.map +1 -0
- package/dist/GerberViewerA2UI-X5FWAD5M.js +57 -0
- package/dist/GerberViewerA2UI-X5FWAD5M.js.map +1 -0
- package/dist/GraphStatsLegend-D5bPeXB_.d.cts +607 -0
- package/dist/GraphStatsLegend-D5bPeXB_.d.ts +607 -0
- package/dist/JsonRenderStandalone-EIZM62JU.js +18 -0
- package/dist/JsonRenderStandalone-EIZM62JU.js.map +1 -0
- package/dist/JsonRenderStandalone-POB4Q3N3.css +2384 -0
- package/dist/JsonRenderStandalone-POB4Q3N3.css.map +1 -0
- package/dist/JsonRenderStandalone-UsTcST4G.d.cts +23 -0
- package/dist/JsonRenderStandalone-UsTcST4G.d.ts +23 -0
- package/dist/KicadViewer-GV6ZC4AQ.js +124 -0
- package/dist/KicadViewer-GV6ZC4AQ.js.map +1 -0
- package/dist/KicadViewerCore-U7BWZHKJ.js +11 -0
- package/dist/KicadViewerCore-U7BWZHKJ.js.map +1 -0
- package/dist/PdfViewer-CHPDRK46.js +51 -0
- package/dist/PdfViewer-CHPDRK46.js.map +1 -0
- package/dist/PdfViewer-LPYGQETK.css +1899 -0
- package/dist/PdfViewer-LPYGQETK.css.map +1 -0
- package/dist/PdfViewerCore-HJPEHSRA.js +364 -0
- package/dist/PdfViewerCore-HJPEHSRA.js.map +1 -0
- package/dist/PowerPointCore-FPDR2BL4.js +11 -0
- package/dist/PowerPointCore-FPDR2BL4.js.map +1 -0
- package/dist/PowerPointViewer-LQTO6UCU.js +61 -0
- package/dist/PowerPointViewer-LQTO6UCU.js.map +1 -0
- package/dist/StepViewerCore-7W3L3R4E.js +285 -0
- package/dist/StepViewerCore-7W3L3R4E.js.map +1 -0
- package/dist/ThreeViewerCore-N3QJD5QI.js +161 -0
- package/dist/ThreeViewerCore-N3QJD5QI.js.map +1 -0
- package/dist/WordCore-JKSXK2XD.js +11 -0
- package/dist/WordCore-JKSXK2XD.js.map +1 -0
- package/dist/WordViewer-ZHCQMHOH.js +61 -0
- package/dist/WordViewer-ZHCQMHOH.js.map +1 -0
- package/dist/chunk-2SKA3F5U.js +88 -0
- package/dist/chunk-2SKA3F5U.js.map +1 -0
- package/dist/chunk-2UC7YLVX.js +318 -0
- package/dist/chunk-2UC7YLVX.js.map +1 -0
- package/dist/chunk-3R6T3LBR.js +24 -0
- package/dist/chunk-3R6T3LBR.js.map +1 -0
- package/dist/chunk-56WRZM3R.js +398 -0
- package/dist/chunk-56WRZM3R.js.map +1 -0
- package/dist/chunk-7A4FY6FK.js +10226 -0
- package/dist/chunk-7A4FY6FK.js.map +1 -0
- package/dist/chunk-7D4SUZUM.js +38 -0
- package/dist/chunk-7D4SUZUM.js.map +1 -0
- package/dist/chunk-7S67DOHQ.js +436 -0
- package/dist/chunk-7S67DOHQ.js.map +1 -0
- package/dist/chunk-CFKGNAJM.js +14013 -0
- package/dist/chunk-CFKGNAJM.js.map +1 -0
- package/dist/chunk-GAMA3VA7.js +99 -0
- package/dist/chunk-GAMA3VA7.js.map +1 -0
- package/dist/chunk-GYXTSY22.js +639 -0
- package/dist/chunk-GYXTSY22.js.map +1 -0
- package/dist/chunk-K4KGNVL5.js +77 -0
- package/dist/chunk-K4KGNVL5.js.map +1 -0
- package/dist/chunk-KQV7IKET.js +1621 -0
- package/dist/chunk-KQV7IKET.js.map +1 -0
- package/dist/chunk-O3NXUM6C.js +1871 -0
- package/dist/chunk-O3NXUM6C.js.map +1 -0
- package/dist/chunk-PZXSASDY.js +83 -0
- package/dist/chunk-PZXSASDY.js.map +1 -0
- package/dist/chunk-QLVPIM6R.js +595 -0
- package/dist/chunk-QLVPIM6R.js.map +1 -0
- package/dist/chunk-VXJWGLZ7.js +21 -0
- package/dist/chunk-VXJWGLZ7.js.map +1 -0
- package/dist/chunk-XQ562W7I.js +116 -0
- package/dist/chunk-XQ562W7I.js.map +1 -0
- package/dist/components/JsonRender/standalone.cjs +39368 -0
- package/dist/components/JsonRender/standalone.cjs.map +1 -0
- package/dist/components/JsonRender/standalone.css +2384 -0
- package/dist/components/JsonRender/standalone.css.map +1 -0
- package/dist/components/JsonRender/standalone.d.cts +16 -0
- package/dist/components/JsonRender/standalone.d.ts +16 -0
- package/dist/components/JsonRender/standalone.js +38 -0
- package/dist/components/JsonRender/standalone.js.map +1 -0
- package/dist/gerber-2d-entry-OQ4SQRBY.js +3950 -0
- package/dist/gerber-2d-entry-OQ4SQRBY.js.map +1 -0
- package/dist/gerber-3d-entry-DEHDBOO2.js +3679 -0
- package/dist/gerber-3d-entry-DEHDBOO2.js.map +1 -0
- package/dist/gerber-simulation-entry-EBDX72XE.js +1801 -0
- package/dist/gerber-simulation-entry-EBDX72XE.js.map +1 -0
- package/dist/index.cjs +60113 -2970
- package/dist/index.cjs.map +1 -1
- package/dist/index.css +11342 -1708
- package/dist/index.css.map +1 -1
- package/dist/index.d.cts +3275 -77
- package/dist/index.d.ts +3275 -77
- package/dist/index.js +18078 -2540
- package/dist/index.js.map +1 -1
- package/dist/provenance/index.cjs +2248 -0
- package/dist/provenance/index.cjs.map +1 -0
- package/dist/provenance/index.css +52 -0
- package/dist/provenance/index.css.map +1 -0
- package/dist/provenance/index.d.cts +12 -0
- package/dist/provenance/index.d.ts +12 -0
- package/dist/provenance/index.js +27 -0
- package/dist/provenance/index.js.map +1 -0
- package/dist/resolveToArrayBuffer-AQIDZHSQ.js +23 -0
- package/dist/resolveToArrayBuffer-AQIDZHSQ.js.map +1 -0
- package/package.json +96 -17
|
@@ -0,0 +1,639 @@
|
|
|
1
|
+
// src/components/FileViewer/viewers/GerberViewer.tsx
|
|
2
|
+
import { useEffect as useEffect4, useRef as useRef4, useState as useState4 } from "react";
|
|
3
|
+
|
|
4
|
+
// src/components/jetPaveGerberViewer/src/pages/Page2D.tsx
|
|
5
|
+
import { useEffect, useRef, useState } from "react";
|
|
6
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
7
|
+
var WIN = window;
|
|
8
|
+
var CONTAINER_KEY = "__gerber2d_container__";
|
|
9
|
+
var READY_KEY = "__gerber2d_ready__";
|
|
10
|
+
function getOrCreateContainer() {
|
|
11
|
+
const existing = WIN[CONTAINER_KEY];
|
|
12
|
+
if (existing) return existing;
|
|
13
|
+
delete WIN[READY_KEY];
|
|
14
|
+
const el = document.createElement("div");
|
|
15
|
+
el.className = "layout-2d";
|
|
16
|
+
el.innerHTML = `
|
|
17
|
+
<div id="loadingOverlay">\u6B63\u5728\u5904\u7406\u4E2D...</div>
|
|
18
|
+
<div class="container">
|
|
19
|
+
<div class="controls" style="display:none">
|
|
20
|
+
<div class="file-input-wrapper">
|
|
21
|
+
<input type="file" id="fileInput"
|
|
22
|
+
accept=".zip,.rar,.gtl,.gbl,.gts,.gbs,.gto,.gbo,.gtp,.gbp,.gm1,.drl,.gbr,.gko,.txt,.tx1,.g1,.g2,.g3,.g4,.g5,.g6,.gp1,.gp2,.gp3"
|
|
23
|
+
multiple />
|
|
24
|
+
</div>
|
|
25
|
+
<div class="file-info" id="fileInfo" style="display:none">
|
|
26
|
+
<div class="file-name" id="fileName"></div>
|
|
27
|
+
<div id="fileSize"></div>
|
|
28
|
+
</div>
|
|
29
|
+
<div class="status" id="status" style="display:none"></div>
|
|
30
|
+
</div>
|
|
31
|
+
<div class="viewer">
|
|
32
|
+
<div class="viewer-content">
|
|
33
|
+
<div class="webgl-canvas-container">
|
|
34
|
+
<canvas id="canvas"></canvas>
|
|
35
|
+
</div>
|
|
36
|
+
<div class="layer-list-container">
|
|
37
|
+
<div class="layer-list-title">\u56FE\u5C42\u5217\u8868</div>
|
|
38
|
+
<div class="layer-list-header">
|
|
39
|
+
<div class="layer-toggle-all" id="layerToggleAll">
|
|
40
|
+
<div class="layer-toggle checked" id="layerToggleAllCheckbox"></div>
|
|
41
|
+
<span class="layer-toggle-all-label">\u5168\u90E8\u663E\u793A/\u9690\u85CF</span>
|
|
42
|
+
</div>
|
|
43
|
+
</div>
|
|
44
|
+
<div class="layer-list" id="layerList"></div>
|
|
45
|
+
</div>
|
|
46
|
+
</div>
|
|
47
|
+
</div>
|
|
48
|
+
</div>`;
|
|
49
|
+
WIN[CONTAINER_KEY] = el;
|
|
50
|
+
return el;
|
|
51
|
+
}
|
|
52
|
+
function isViewerReady() {
|
|
53
|
+
return !!WIN[READY_KEY];
|
|
54
|
+
}
|
|
55
|
+
function markViewerReady() {
|
|
56
|
+
WIN[READY_KEY] = true;
|
|
57
|
+
}
|
|
58
|
+
function injectFile(file) {
|
|
59
|
+
const container = WIN[CONTAINER_KEY];
|
|
60
|
+
const fi = container?.querySelector("#fileInput");
|
|
61
|
+
if (!fi) throw new Error("#fileInput not found in persistent container");
|
|
62
|
+
console.log("[Page2D] injectFile: element found, isConnected =", fi.isConnected, ", file =", file.name, file.size);
|
|
63
|
+
const dt = new DataTransfer();
|
|
64
|
+
dt.items.add(file);
|
|
65
|
+
fi.files = dt.files;
|
|
66
|
+
console.log("[Page2D] injectFile: files set, count =", fi.files.length, ", dispatching change...");
|
|
67
|
+
const testListener = () => console.log("[Page2D] injectFile: \u2713 change event received by test listener");
|
|
68
|
+
fi.addEventListener("change", testListener, { once: true });
|
|
69
|
+
const handled = fi.dispatchEvent(new Event("change", { bubbles: true }));
|
|
70
|
+
console.log("[Page2D] injectFile: change dispatched, defaultPrevented =", !handled);
|
|
71
|
+
fi.removeEventListener("change", testListener);
|
|
72
|
+
}
|
|
73
|
+
async function fetchAndInject(url) {
|
|
74
|
+
const res = await fetch(url);
|
|
75
|
+
if (!res.ok) throw new Error(`Fetch failed: HTTP ${res.status}`);
|
|
76
|
+
const blob = await res.blob();
|
|
77
|
+
let name = "gerber.zip";
|
|
78
|
+
try {
|
|
79
|
+
const seg = new URL(url).pathname.split("/").filter(Boolean).pop();
|
|
80
|
+
if (seg) name = decodeURIComponent(seg);
|
|
81
|
+
} catch {
|
|
82
|
+
}
|
|
83
|
+
const hdr = new Uint8Array(await blob.slice(0, 4).arrayBuffer());
|
|
84
|
+
const isZip = hdr[0] === 80 && hdr[1] === 75;
|
|
85
|
+
const isRar = hdr[0] === 82 && hdr[1] === 97 && hdr[2] === 114;
|
|
86
|
+
if (isZip && !name.toLowerCase().endsWith(".zip"))
|
|
87
|
+
name = name.replace(/\.[^.]+$/, "") + ".zip";
|
|
88
|
+
if (isRar && !name.toLowerCase().endsWith(".rar"))
|
|
89
|
+
name = name.replace(/\.[^.]+$/, "") + ".rar";
|
|
90
|
+
const type = isZip ? "application/zip" : isRar ? "application/x-rar-compressed" : blob.type || "application/octet-stream";
|
|
91
|
+
injectFile(new File([blob], name, { type }));
|
|
92
|
+
}
|
|
93
|
+
function Page2D({ content, url, onLayerSelect }) {
|
|
94
|
+
const mountRef = useRef(null);
|
|
95
|
+
const [error, setError] = useState(null);
|
|
96
|
+
const onLayerSelectRef = useRef(onLayerSelect);
|
|
97
|
+
onLayerSelectRef.current = onLayerSelect;
|
|
98
|
+
useEffect(() => {
|
|
99
|
+
const mount = mountRef.current;
|
|
100
|
+
if (!mount) return;
|
|
101
|
+
const container = getOrCreateContainer();
|
|
102
|
+
mount.appendChild(container);
|
|
103
|
+
if (isViewerReady()) {
|
|
104
|
+
window.dispatchEvent(new Event("resize"));
|
|
105
|
+
}
|
|
106
|
+
const handleLayerClick = (e) => {
|
|
107
|
+
const cb = onLayerSelectRef.current;
|
|
108
|
+
if (!cb) return;
|
|
109
|
+
const item = e.target.closest?.(".layer-item");
|
|
110
|
+
if (!item) return;
|
|
111
|
+
const nameEl = item.querySelector(".layer-name");
|
|
112
|
+
const layerName = nameEl?.textContent?.trim();
|
|
113
|
+
if (!layerName) return;
|
|
114
|
+
const rect = item.getBoundingClientRect();
|
|
115
|
+
const mountRect = mount.getBoundingClientRect();
|
|
116
|
+
cb({
|
|
117
|
+
snippet: `[Gerber \u56FE\u5C42] ${layerName}`,
|
|
118
|
+
anchorRect: {
|
|
119
|
+
top: rect.bottom - mountRect.top + 4,
|
|
120
|
+
left: rect.left - mountRect.left + rect.width / 2
|
|
121
|
+
}
|
|
122
|
+
});
|
|
123
|
+
};
|
|
124
|
+
container.addEventListener("click", handleLayerClick);
|
|
125
|
+
return () => {
|
|
126
|
+
container.removeEventListener("click", handleLayerClick);
|
|
127
|
+
if (container.parentNode === mount) mount.removeChild(container);
|
|
128
|
+
};
|
|
129
|
+
}, []);
|
|
130
|
+
useEffect(() => {
|
|
131
|
+
let cancelled = false;
|
|
132
|
+
void (async () => {
|
|
133
|
+
try {
|
|
134
|
+
setError(null);
|
|
135
|
+
if (!isViewerReady()) {
|
|
136
|
+
console.log("[Page2D] importing 2d entry module...");
|
|
137
|
+
await import("./gerber-2d-entry-OQ4SQRBY.js");
|
|
138
|
+
markViewerReady();
|
|
139
|
+
console.log("[Page2D] module imported, checking DOM elements:", {
|
|
140
|
+
canvas: !!document.getElementById("canvas"),
|
|
141
|
+
fileInput: !!document.getElementById("fileInput"),
|
|
142
|
+
containerInDOM: !!WIN[CONTAINER_KEY]?.isConnected
|
|
143
|
+
});
|
|
144
|
+
} else {
|
|
145
|
+
console.log("[Page2D] viewer already ready, skipping import");
|
|
146
|
+
}
|
|
147
|
+
if (cancelled) return;
|
|
148
|
+
await new Promise((r) => requestAnimationFrame(() => r()));
|
|
149
|
+
if (cancelled) return;
|
|
150
|
+
console.log("[Page2D] inject check", {
|
|
151
|
+
hasContent: !!content,
|
|
152
|
+
contentUrl: content?.content_url,
|
|
153
|
+
contentLength: content?.content?.length,
|
|
154
|
+
fileName: content?.file_name,
|
|
155
|
+
url,
|
|
156
|
+
content
|
|
157
|
+
});
|
|
158
|
+
if (content?.content_url) {
|
|
159
|
+
console.log("[Page2D] fetchAndInject via content_url");
|
|
160
|
+
await fetchAndInject(content.content_url);
|
|
161
|
+
} else if (typeof content?.content === "string" && content.content.trim()) {
|
|
162
|
+
console.log("[Page2D] injectFile from content string, length =", content.content.length);
|
|
163
|
+
injectFile(
|
|
164
|
+
new File([content.content], content.file_name || "board.gbr", {
|
|
165
|
+
type: content.mime_type || "text/plain"
|
|
166
|
+
})
|
|
167
|
+
);
|
|
168
|
+
} else if (url) {
|
|
169
|
+
console.log("[Page2D] fetchAndInject via url prop");
|
|
170
|
+
await fetchAndInject(url);
|
|
171
|
+
} else {
|
|
172
|
+
console.warn("[Page2D] no data to inject \u2014 content and url are both empty");
|
|
173
|
+
}
|
|
174
|
+
} catch (err) {
|
|
175
|
+
if (!cancelled) {
|
|
176
|
+
console.error("[Page2D]", err);
|
|
177
|
+
setError(err instanceof Error ? err.message : String(err));
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
})();
|
|
181
|
+
return () => {
|
|
182
|
+
cancelled = true;
|
|
183
|
+
};
|
|
184
|
+
}, [content?.resource_id, content?.content_url, content?.content, content?.file_name, url]);
|
|
185
|
+
return /* @__PURE__ */ jsx("div", { ref: mountRef, className: "layout-2d-mount", children: error && /* @__PURE__ */ jsxs("div", { className: "layout-2d-error", children: [
|
|
186
|
+
"Gerber \u67E5\u770B\u5668\u52A0\u8F7D\u5931\u8D25: ",
|
|
187
|
+
error
|
|
188
|
+
] }) });
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// src/components/jetPaveGerberViewer/src/pages/Page3D.tsx
|
|
192
|
+
import { useEffect as useEffect2, useRef as useRef2, useState as useState2 } from "react";
|
|
193
|
+
|
|
194
|
+
// src/components/jetPaveGerberViewer/src/legacy/bootLegacyLoad.ts
|
|
195
|
+
function fireLoadIfAlreadyComplete() {
|
|
196
|
+
if (document.readyState === "complete") {
|
|
197
|
+
window.dispatchEvent(new Event("load"));
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
async function importAndBootLoad(importer) {
|
|
201
|
+
await importer();
|
|
202
|
+
fireLoadIfAlreadyComplete();
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
// src/components/jetPaveGerberViewer/src/pages/Page3D.tsx
|
|
206
|
+
import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
207
|
+
var WIN2 = window;
|
|
208
|
+
var CONTAINER_KEY2 = "__gerber3d_container__";
|
|
209
|
+
var READY_KEY2 = "__gerber3d_ready__";
|
|
210
|
+
function getOrCreateContainer2() {
|
|
211
|
+
const existing = WIN2[CONTAINER_KEY2];
|
|
212
|
+
if (existing) return existing;
|
|
213
|
+
delete WIN2[READY_KEY2];
|
|
214
|
+
const el = document.createElement("div");
|
|
215
|
+
el.className = "layout-3d";
|
|
216
|
+
el.innerHTML = `
|
|
217
|
+
<div id="viewer3d-canvas-host" class="viewer3d-canvas-host"></div>
|
|
218
|
+
<input type="file" id="file-input" class="file-input" style="display:none"
|
|
219
|
+
multiple accept=".zip,.rar,.gbr,.gtl,.gbl,.gko,.gm1,.drl,.out,.txt" />
|
|
220
|
+
<div id="gerber-3d-toolbar"
|
|
221
|
+
style="position:absolute;top:10px;left:10px;z-index:200;
|
|
222
|
+
background:rgba(0,0,0,0.7);color:#fff;padding:10px;border-radius:5px;
|
|
223
|
+
display:flex;flex-direction:column;gap:8px;">
|
|
224
|
+
<p style="font-size:12px;margin:0;color:#ccc;">\u5DE6\u952E\u65CB\u8F6C | \u53F3\u952E\u5E73\u79FB | \u6EDA\u8F6E\u7F29\u653E</p>
|
|
225
|
+
<div id="stats" style="font-size:12px;"></div>
|
|
226
|
+
</div>
|
|
227
|
+
<div id="info"></div>
|
|
228
|
+
<div id="loading">\u6B63\u5728\u5904\u7406...</div>`;
|
|
229
|
+
WIN2[CONTAINER_KEY2] = el;
|
|
230
|
+
return el;
|
|
231
|
+
}
|
|
232
|
+
function isViewerReady2() {
|
|
233
|
+
return !!WIN2[READY_KEY2];
|
|
234
|
+
}
|
|
235
|
+
function markViewerReady2() {
|
|
236
|
+
WIN2[READY_KEY2] = true;
|
|
237
|
+
}
|
|
238
|
+
function injectFile2(file) {
|
|
239
|
+
const container = WIN2[CONTAINER_KEY2];
|
|
240
|
+
const fi = container?.querySelector("#file-input");
|
|
241
|
+
if (!fi) throw new Error("#file-input not found in persistent container");
|
|
242
|
+
const dt = new DataTransfer();
|
|
243
|
+
dt.items.add(file);
|
|
244
|
+
fi.files = dt.files;
|
|
245
|
+
fi.dispatchEvent(new Event("change", { bubbles: true }));
|
|
246
|
+
}
|
|
247
|
+
async function fetchAndInject2(url) {
|
|
248
|
+
const res = await fetch(url);
|
|
249
|
+
if (!res.ok) throw new Error(`Fetch failed: HTTP ${res.status}`);
|
|
250
|
+
const blob = await res.blob();
|
|
251
|
+
let name = "gerber.zip";
|
|
252
|
+
try {
|
|
253
|
+
const seg = new URL(url).pathname.split("/").filter(Boolean).pop();
|
|
254
|
+
if (seg) name = decodeURIComponent(seg);
|
|
255
|
+
} catch {
|
|
256
|
+
}
|
|
257
|
+
const hdr = new Uint8Array(await blob.slice(0, 4).arrayBuffer());
|
|
258
|
+
const isZip = hdr[0] === 80 && hdr[1] === 75;
|
|
259
|
+
const isRar = hdr[0] === 82 && hdr[1] === 97 && hdr[2] === 114;
|
|
260
|
+
if (isZip && !name.toLowerCase().endsWith(".zip"))
|
|
261
|
+
name = name.replace(/\.[^.]+$/, "") + ".zip";
|
|
262
|
+
if (isRar && !name.toLowerCase().endsWith(".rar"))
|
|
263
|
+
name = name.replace(/\.[^.]+$/, "") + ".rar";
|
|
264
|
+
const type = isZip ? "application/zip" : isRar ? "application/x-rar-compressed" : blob.type || "application/octet-stream";
|
|
265
|
+
injectFile2(new File([blob], name, { type }));
|
|
266
|
+
}
|
|
267
|
+
function Page3D({ content, url }) {
|
|
268
|
+
const mountRef = useRef2(null);
|
|
269
|
+
const [error, setError] = useState2(null);
|
|
270
|
+
useEffect2(() => {
|
|
271
|
+
const mount = mountRef.current;
|
|
272
|
+
if (!mount) return;
|
|
273
|
+
const container = getOrCreateContainer2();
|
|
274
|
+
mount.appendChild(container);
|
|
275
|
+
if (isViewerReady2()) {
|
|
276
|
+
window.dispatchEvent(new Event("resize"));
|
|
277
|
+
}
|
|
278
|
+
return () => {
|
|
279
|
+
if (container.parentNode === mount) mount.removeChild(container);
|
|
280
|
+
};
|
|
281
|
+
}, []);
|
|
282
|
+
useEffect2(() => {
|
|
283
|
+
let cancelled = false;
|
|
284
|
+
void (async () => {
|
|
285
|
+
try {
|
|
286
|
+
setError(null);
|
|
287
|
+
if (!isViewerReady2()) {
|
|
288
|
+
await importAndBootLoad(
|
|
289
|
+
() => import("./gerber-3d-entry-DEHDBOO2.js")
|
|
290
|
+
);
|
|
291
|
+
markViewerReady2();
|
|
292
|
+
}
|
|
293
|
+
if (cancelled) return;
|
|
294
|
+
await new Promise((r) => requestAnimationFrame(() => r()));
|
|
295
|
+
if (cancelled) return;
|
|
296
|
+
window.dispatchEvent(new Event("resize"));
|
|
297
|
+
console.log("[Page3D] inject check", {
|
|
298
|
+
hasContent: !!content,
|
|
299
|
+
contentUrl: content?.content_url,
|
|
300
|
+
contentLength: content?.content?.length,
|
|
301
|
+
fileName: content?.file_name,
|
|
302
|
+
url
|
|
303
|
+
});
|
|
304
|
+
if (content?.content_url) {
|
|
305
|
+
console.log("[Page3D] fetchAndInject via content_url");
|
|
306
|
+
await fetchAndInject2(content.content_url);
|
|
307
|
+
} else if (typeof content?.content === "string" && content.content.trim()) {
|
|
308
|
+
console.log("[Page3D] injectFile from content string, length =", content.content.length);
|
|
309
|
+
injectFile2(
|
|
310
|
+
new File([content.content], content.file_name || "board.gbr", {
|
|
311
|
+
type: content.mime_type || "text/plain"
|
|
312
|
+
})
|
|
313
|
+
);
|
|
314
|
+
} else if (url) {
|
|
315
|
+
console.log("[Page3D] fetchAndInject via url prop");
|
|
316
|
+
await fetchAndInject2(url);
|
|
317
|
+
} else {
|
|
318
|
+
console.warn("[Page3D] no data to inject \u2014 content and url are both empty");
|
|
319
|
+
}
|
|
320
|
+
} catch (err) {
|
|
321
|
+
if (!cancelled) {
|
|
322
|
+
console.error("[Page3D]", err);
|
|
323
|
+
setError(err instanceof Error ? err.message : String(err));
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
})();
|
|
327
|
+
return () => {
|
|
328
|
+
cancelled = true;
|
|
329
|
+
};
|
|
330
|
+
}, [content?.resource_id, content?.content_url, content?.content, content?.file_name, url]);
|
|
331
|
+
return /* @__PURE__ */ jsx2("div", { ref: mountRef, className: "layout-3d-mount", children: error && /* @__PURE__ */ jsxs2("div", { className: "layout-3d-error", children: [
|
|
332
|
+
"3D \u67E5\u770B\u5668\u52A0\u8F7D\u5931\u8D25: ",
|
|
333
|
+
error
|
|
334
|
+
] }) });
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
// src/components/jetPaveGerberViewer/src/pages/PageSimulation.tsx
|
|
338
|
+
import { useEffect as useEffect3, useRef as useRef3, useState as useState3 } from "react";
|
|
339
|
+
import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
340
|
+
var WIN3 = window;
|
|
341
|
+
var CONTAINER_KEY3 = "__gerberSim_container__";
|
|
342
|
+
var READY_KEY3 = "__gerberSim_ready__";
|
|
343
|
+
function getOrCreateContainer3() {
|
|
344
|
+
const existing = WIN3[CONTAINER_KEY3];
|
|
345
|
+
if (existing) return existing;
|
|
346
|
+
delete WIN3[READY_KEY3];
|
|
347
|
+
const el = document.createElement("div");
|
|
348
|
+
el.className = "layout-simulation";
|
|
349
|
+
el.innerHTML = `
|
|
350
|
+
<div class="container">
|
|
351
|
+
<div class="control-panel">
|
|
352
|
+
<h2 class="panel-title">\u4EFF\u771F\u56FE</h2>
|
|
353
|
+
|
|
354
|
+
<div class="control-group" style="display:none">
|
|
355
|
+
<div class="file-input-wrapper">
|
|
356
|
+
<span class="file-input-button">\u{1F4C1} \u9009\u62E9 Gerber \u6587\u4EF6</span>
|
|
357
|
+
<input type="file" id="fileInput"
|
|
358
|
+
accept=".zip,.rar,.gbr,.gtl,.gbl,.gts,.gbs,.gto,.gbo,.gtp,.gbp,.gko,.drl,.txt"
|
|
359
|
+
multiple />
|
|
360
|
+
</div>
|
|
361
|
+
</div>
|
|
362
|
+
|
|
363
|
+
<div class="control-group">
|
|
364
|
+
<div class="control-label">\u67E5\u770B\u9762</div>
|
|
365
|
+
<div class="view-toggle">
|
|
366
|
+
<button type="button" id="btnTopView" class="active">\u9876\u5C42 (Top)</button>
|
|
367
|
+
<button type="button" id="btnBottomView">\u5E95\u5C42 (Bottom)</button>
|
|
368
|
+
</div>
|
|
369
|
+
</div>
|
|
370
|
+
|
|
371
|
+
<div class="control-group">
|
|
372
|
+
<div class="control-row">
|
|
373
|
+
<span class="control-label">\u963B\u710A\u989C\u8272</span>
|
|
374
|
+
<div class="color-preview">
|
|
375
|
+
<div class="color-swatch" id="maskColorPreview" style="background:#1C590B"></div>
|
|
376
|
+
<select id="maskColorSelect">
|
|
377
|
+
<option value="#1C590B" selected>\u7EFF\u8272</option>
|
|
378
|
+
<option value="#1a3a6e">\u84DD\u8272</option>
|
|
379
|
+
<option value="#8b0000">\u7EA2\u8272</option>
|
|
380
|
+
<option value="#1a1a1a">\u9ED1\u8272</option>
|
|
381
|
+
<option value="#f5f5f5">\u767D\u8272</option>
|
|
382
|
+
<option value="#2d2d2d">\u54D1\u5149\u9ED1</option>
|
|
383
|
+
<option value="#4a0080">\u7D2B\u8272</option>
|
|
384
|
+
<option value="#c4a000">\u9EC4\u8272</option>
|
|
385
|
+
</select>
|
|
386
|
+
</div>
|
|
387
|
+
</div>
|
|
388
|
+
</div>
|
|
389
|
+
|
|
390
|
+
<div class="control-group">
|
|
391
|
+
<div class="control-row">
|
|
392
|
+
<span class="control-label">\u4E1D\u5370\u989C\u8272</span>
|
|
393
|
+
<div class="color-preview">
|
|
394
|
+
<div class="color-swatch" id="silkColorPreview" style="background:#ffffff"></div>
|
|
395
|
+
<select id="silkColorSelect">
|
|
396
|
+
<option value="#ffffff" selected>\u767D\u8272</option>
|
|
397
|
+
<option value="#000000">\u9ED1\u8272</option>
|
|
398
|
+
<option value="#f5e642">\u9EC4\u8272</option>
|
|
399
|
+
</select>
|
|
400
|
+
</div>
|
|
401
|
+
</div>
|
|
402
|
+
</div>
|
|
403
|
+
|
|
404
|
+
<div class="control-group">
|
|
405
|
+
<div class="control-row">
|
|
406
|
+
<span class="control-label">\u8868\u9762\u5904\u7406</span>
|
|
407
|
+
<div class="color-preview">
|
|
408
|
+
<div class="color-swatch" id="finishColorPreview" style="background:#D4AF37"></div>
|
|
409
|
+
<select id="finishSelect">
|
|
410
|
+
<option value="ENIG" selected>\u6C89\u91D1 (ENIG)</option>
|
|
411
|
+
<option value="HASL">\u55B7\u9521 (HASL)</option>
|
|
412
|
+
<option value="OSP">OSP</option>
|
|
413
|
+
<option value="SILVER">\u9540\u94F6</option>
|
|
414
|
+
<option value="ENEPIG">\u5316\u954D\u94AF\u91D1</option>
|
|
415
|
+
</select>
|
|
416
|
+
</div>
|
|
417
|
+
</div>
|
|
418
|
+
</div>
|
|
419
|
+
|
|
420
|
+
<div class="control-group">
|
|
421
|
+
<div class="switch-row">
|
|
422
|
+
<span class="control-label">\u663E\u793A\u963B\u710A\u5C42</span>
|
|
423
|
+
<label class="switch"><input type="checkbox" id="showMask" checked /><span class="slider"></span></label>
|
|
424
|
+
</div>
|
|
425
|
+
<div class="switch-row">
|
|
426
|
+
<span class="control-label">\u663E\u793A\u4E1D\u5370\u5C42</span>
|
|
427
|
+
<label class="switch"><input type="checkbox" id="showSilk" checked /><span class="slider"></span></label>
|
|
428
|
+
</div>
|
|
429
|
+
<div class="switch-row">
|
|
430
|
+
<span class="control-label">\u663E\u793A\u94BB\u5B54</span>
|
|
431
|
+
<label class="switch"><input type="checkbox" id="showDrill" checked /><span class="slider"></span></label>
|
|
432
|
+
</div>
|
|
433
|
+
</div>
|
|
434
|
+
|
|
435
|
+
<div class="control-group" style="margin-top:auto">
|
|
436
|
+
<button type="button" class="btn btn-success" id="btnExport">\u{1F4F7} \u5BFC\u51FA\u4EFF\u771F\u56FE</button>
|
|
437
|
+
<button type="button" class="btn btn-secondary" id="btnClear">\u{1F5D1}\uFE0F \u6E05\u7A7A</button>
|
|
438
|
+
</div>
|
|
439
|
+
|
|
440
|
+
</div>
|
|
441
|
+
|
|
442
|
+
<div class="main-view">
|
|
443
|
+
<div class="canvas-container" id="canvasContainer">
|
|
444
|
+
<canvas id="simulationCanvas"></canvas>
|
|
445
|
+
<div class="loading-overlay hidden" id="loadingOverlay">
|
|
446
|
+
<div class="spinner"></div>
|
|
447
|
+
<div class="loading-text" id="loadingText">\u6B63\u5728\u89E3\u6790\u6587\u4EF6...</div>
|
|
448
|
+
</div>
|
|
449
|
+
</div>
|
|
450
|
+
<div class="hidden-status">
|
|
451
|
+
<span id="statusLeft"></span>
|
|
452
|
+
<span id="statusRight"></span>
|
|
453
|
+
<div id="layerList"></div>
|
|
454
|
+
</div>
|
|
455
|
+
</div>
|
|
456
|
+
</div>`;
|
|
457
|
+
WIN3[CONTAINER_KEY3] = el;
|
|
458
|
+
return el;
|
|
459
|
+
}
|
|
460
|
+
function isViewerReady3() {
|
|
461
|
+
return !!WIN3[READY_KEY3];
|
|
462
|
+
}
|
|
463
|
+
function markViewerReady3() {
|
|
464
|
+
WIN3[READY_KEY3] = true;
|
|
465
|
+
}
|
|
466
|
+
function injectFile3(file) {
|
|
467
|
+
const container = WIN3[CONTAINER_KEY3];
|
|
468
|
+
const fi = container?.querySelector("#fileInput");
|
|
469
|
+
if (!fi) throw new Error("#fileInput not found in persistent container");
|
|
470
|
+
const dt = new DataTransfer();
|
|
471
|
+
dt.items.add(file);
|
|
472
|
+
fi.files = dt.files;
|
|
473
|
+
fi.dispatchEvent(new Event("change", { bubbles: true }));
|
|
474
|
+
}
|
|
475
|
+
async function fetchAndInject3(url) {
|
|
476
|
+
const res = await fetch(url);
|
|
477
|
+
if (!res.ok) throw new Error(`Fetch failed: HTTP ${res.status}`);
|
|
478
|
+
const blob = await res.blob();
|
|
479
|
+
let name = "gerber.zip";
|
|
480
|
+
try {
|
|
481
|
+
const seg = new URL(url).pathname.split("/").filter(Boolean).pop();
|
|
482
|
+
if (seg) name = decodeURIComponent(seg);
|
|
483
|
+
} catch {
|
|
484
|
+
}
|
|
485
|
+
const hdr = new Uint8Array(await blob.slice(0, 4).arrayBuffer());
|
|
486
|
+
const isZip = hdr[0] === 80 && hdr[1] === 75;
|
|
487
|
+
const isRar = hdr[0] === 82 && hdr[1] === 97 && hdr[2] === 114;
|
|
488
|
+
if (isZip && !name.toLowerCase().endsWith(".zip"))
|
|
489
|
+
name = name.replace(/\.[^.]+$/, "") + ".zip";
|
|
490
|
+
if (isRar && !name.toLowerCase().endsWith(".rar"))
|
|
491
|
+
name = name.replace(/\.[^.]+$/, "") + ".rar";
|
|
492
|
+
const type = isZip ? "application/zip" : isRar ? "application/x-rar-compressed" : blob.type || "application/octet-stream";
|
|
493
|
+
injectFile3(new File([blob], name, { type }));
|
|
494
|
+
}
|
|
495
|
+
function PageSimulation({ content, url }) {
|
|
496
|
+
const mountRef = useRef3(null);
|
|
497
|
+
const [error, setError] = useState3(null);
|
|
498
|
+
useEffect3(() => {
|
|
499
|
+
const mount = mountRef.current;
|
|
500
|
+
if (!mount) return;
|
|
501
|
+
const container = getOrCreateContainer3();
|
|
502
|
+
mount.appendChild(container);
|
|
503
|
+
if (isViewerReady3()) {
|
|
504
|
+
window.dispatchEvent(new Event("resize"));
|
|
505
|
+
}
|
|
506
|
+
return () => {
|
|
507
|
+
if (container.parentNode === mount) mount.removeChild(container);
|
|
508
|
+
};
|
|
509
|
+
}, []);
|
|
510
|
+
useEffect3(() => {
|
|
511
|
+
let cancelled = false;
|
|
512
|
+
void (async () => {
|
|
513
|
+
try {
|
|
514
|
+
setError(null);
|
|
515
|
+
if (!isViewerReady3()) {
|
|
516
|
+
await import("./gerber-simulation-entry-EBDX72XE.js");
|
|
517
|
+
markViewerReady3();
|
|
518
|
+
}
|
|
519
|
+
if (cancelled) return;
|
|
520
|
+
await new Promise((r) => requestAnimationFrame(() => r()));
|
|
521
|
+
if (cancelled) return;
|
|
522
|
+
window.dispatchEvent(new Event("resize"));
|
|
523
|
+
console.log("[PageSimulation] inject check", {
|
|
524
|
+
hasContent: !!content,
|
|
525
|
+
contentUrl: content?.content_url,
|
|
526
|
+
contentLength: content?.content?.length,
|
|
527
|
+
fileName: content?.file_name,
|
|
528
|
+
url
|
|
529
|
+
});
|
|
530
|
+
if (content?.content_url) {
|
|
531
|
+
console.log("[PageSimulation] fetchAndInject via content_url");
|
|
532
|
+
await fetchAndInject3(content.content_url);
|
|
533
|
+
} else if (typeof content?.content === "string" && content.content.trim()) {
|
|
534
|
+
console.log("[PageSimulation] injectFile from content string, length =", content.content.length);
|
|
535
|
+
injectFile3(
|
|
536
|
+
new File([content.content], content.file_name || "board.gbr", {
|
|
537
|
+
type: content.mime_type || "text/plain"
|
|
538
|
+
})
|
|
539
|
+
);
|
|
540
|
+
} else if (url) {
|
|
541
|
+
console.log("[PageSimulation] fetchAndInject via url prop");
|
|
542
|
+
await fetchAndInject3(url);
|
|
543
|
+
} else {
|
|
544
|
+
console.warn("[PageSimulation] no data to inject \u2014 content and url are both empty");
|
|
545
|
+
}
|
|
546
|
+
} catch (err) {
|
|
547
|
+
if (!cancelled) {
|
|
548
|
+
console.error("[PageSimulation]", err);
|
|
549
|
+
setError(err instanceof Error ? err.message : String(err));
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
})();
|
|
553
|
+
return () => {
|
|
554
|
+
cancelled = true;
|
|
555
|
+
};
|
|
556
|
+
}, [content?.resource_id, content?.content_url, content?.content, content?.file_name, url]);
|
|
557
|
+
return /* @__PURE__ */ jsx3("div", { ref: mountRef, className: "layout-simulation-mount", children: error && /* @__PURE__ */ jsxs3("div", { className: "layout-simulation-error", children: [
|
|
558
|
+
"\u4EFF\u771F\u67E5\u770B\u5668\u52A0\u8F7D\u5931\u8D25: ",
|
|
559
|
+
error
|
|
560
|
+
] }) });
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
// src/components/FileViewer/viewers/GerberViewer.tsx
|
|
564
|
+
import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
565
|
+
var ALL_MODES = ["2d", "3d", "simulation"];
|
|
566
|
+
var MODE_LABEL = {
|
|
567
|
+
"2d": "2D\u6A21\u5F0F",
|
|
568
|
+
"3d": "3D\u6A21\u5F0F",
|
|
569
|
+
simulation: "\u4EFF\u771F\u56FE"
|
|
570
|
+
};
|
|
571
|
+
var GerberViewer = ({
|
|
572
|
+
content,
|
|
573
|
+
url,
|
|
574
|
+
onGerberSelection,
|
|
575
|
+
mode: controlledMode,
|
|
576
|
+
defaultMode = "2d",
|
|
577
|
+
onModeChange
|
|
578
|
+
}) => {
|
|
579
|
+
const isControlled = controlledMode !== void 0;
|
|
580
|
+
const [internalMode, setInternalMode] = useState4(defaultMode);
|
|
581
|
+
const mode = isControlled ? controlledMode : internalMode;
|
|
582
|
+
const lastReportedRef = useRef4(mode);
|
|
583
|
+
useEffect4(() => {
|
|
584
|
+
if (!isControlled) return;
|
|
585
|
+
if (lastReportedRef.current === mode) return;
|
|
586
|
+
lastReportedRef.current = mode;
|
|
587
|
+
onModeChange?.(mode);
|
|
588
|
+
}, [isControlled, mode, onModeChange]);
|
|
589
|
+
const handleModeClick = (next) => {
|
|
590
|
+
if (!isControlled) {
|
|
591
|
+
setInternalMode(next);
|
|
592
|
+
lastReportedRef.current = next;
|
|
593
|
+
}
|
|
594
|
+
onModeChange?.(next);
|
|
595
|
+
};
|
|
596
|
+
return /* @__PURE__ */ jsxs4(
|
|
597
|
+
"div",
|
|
598
|
+
{
|
|
599
|
+
className: "ycw-file-viewer-gerber ycw-file-viewer-gerber-embedded",
|
|
600
|
+
"data-mode": mode,
|
|
601
|
+
children: [
|
|
602
|
+
/* @__PURE__ */ jsx4(
|
|
603
|
+
"div",
|
|
604
|
+
{
|
|
605
|
+
className: "ycw-gerber-mode-bar",
|
|
606
|
+
"data-controlled": isControlled ? "true" : void 0,
|
|
607
|
+
role: isControlled ? "group" : void 0,
|
|
608
|
+
"aria-label": isControlled ? "Gerber view mode (controlled)" : void 0,
|
|
609
|
+
children: ALL_MODES.map((m) => /* @__PURE__ */ jsx4(
|
|
610
|
+
"button",
|
|
611
|
+
{
|
|
612
|
+
type: "button",
|
|
613
|
+
className: `viewer-action-btn${mode === m ? " active" : ""}`,
|
|
614
|
+
onClick: () => handleModeClick(m),
|
|
615
|
+
disabled: isControlled,
|
|
616
|
+
"aria-disabled": isControlled || void 0,
|
|
617
|
+
tabIndex: isControlled ? -1 : 0,
|
|
618
|
+
"data-active": mode === m ? "true" : void 0,
|
|
619
|
+
children: MODE_LABEL[m]
|
|
620
|
+
},
|
|
621
|
+
m
|
|
622
|
+
))
|
|
623
|
+
}
|
|
624
|
+
),
|
|
625
|
+
/* @__PURE__ */ jsxs4("div", { className: "ycw-gerber-viewer-body", children: [
|
|
626
|
+
mode === "2d" && /* @__PURE__ */ jsx4(Page2D, { content, url, onLayerSelect: onGerberSelection }),
|
|
627
|
+
mode === "3d" && /* @__PURE__ */ jsx4(Page3D, { content, url }),
|
|
628
|
+
mode === "simulation" && /* @__PURE__ */ jsx4(PageSimulation, { content, url })
|
|
629
|
+
] })
|
|
630
|
+
]
|
|
631
|
+
}
|
|
632
|
+
);
|
|
633
|
+
};
|
|
634
|
+
var GerberViewer_default = GerberViewer;
|
|
635
|
+
|
|
636
|
+
export {
|
|
637
|
+
GerberViewer_default
|
|
638
|
+
};
|
|
639
|
+
//# sourceMappingURL=chunk-GYXTSY22.js.map
|