@huaqiu/dsh-tool-schematic-gen 0.3.7 → 0.3.9
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/lib/client.js +166 -6
- package/lib/client.js.map +1 -1
- package/lib/index.mjs +31 -15
- package/package.json +6 -6
- package/src/client/hit-card.tsx +78 -0
- package/src/client/i18n.ts +10 -0
- package/src/client/index.ts +8 -0
- package/src/client/parse.ts +22 -3
- package/src/client/place.ts +17 -0
- package/src/tools.ts +43 -7
package/lib/client.js
CHANGED
|
@@ -27,7 +27,8 @@ window.__ModuleLoader__.load({
|
|
|
27
27
|
id,
|
|
28
28
|
type: typeof o.type === "string" ? o.type : null,
|
|
29
29
|
filename: typeof o.filename === "string" ? o.filename : null,
|
|
30
|
-
size: typeof o.size === "number" ? o.size : null
|
|
30
|
+
size: typeof o.size === "number" ? o.size : null,
|
|
31
|
+
uri: typeof o.uri === "string" && o.uri.length > 0 ? o.uri : null
|
|
31
32
|
};
|
|
32
33
|
}
|
|
33
34
|
function parseSchResult(text) {
|
|
@@ -44,15 +45,23 @@ window.__ModuleLoader__.load({
|
|
|
44
45
|
const status = typeof o.status === "string" ? o.status : null;
|
|
45
46
|
const kind = typeof o.kind === "string" ? o.kind : null;
|
|
46
47
|
let artifact = null;
|
|
47
|
-
|
|
48
|
-
|
|
48
|
+
const artifacts = [];
|
|
49
|
+
if (kind === "system") {
|
|
50
|
+
artifact = artOf(o.zipArtifact);
|
|
51
|
+
if (artifact) artifacts.push(artifact);
|
|
52
|
+
} else {
|
|
49
53
|
const list = Array.isArray(o.schArtifacts) ? o.schArtifacts : [];
|
|
50
|
-
|
|
54
|
+
for (const entry of list) {
|
|
55
|
+
const ref = artOf(entry);
|
|
56
|
+
if (ref) artifacts.push(ref);
|
|
57
|
+
}
|
|
58
|
+
artifact = artifacts.length > 0 ? artifacts[0] : null;
|
|
51
59
|
}
|
|
52
60
|
return {
|
|
53
61
|
status,
|
|
54
62
|
kind,
|
|
55
63
|
artifact,
|
|
64
|
+
artifacts,
|
|
56
65
|
designName: typeof o.design_name === "string" && o.design_name ? o.design_name : null,
|
|
57
66
|
fileCount: Array.isArray(o.schFiles) ? o.schFiles.length : Array.isArray(o.schArtifacts) ? o.schArtifacts.length : null,
|
|
58
67
|
moduleCount: typeof o.module_count === "number" ? o.module_count : null,
|
|
@@ -476,6 +485,11 @@ window.__ModuleLoader__.load({
|
|
|
476
485
|
"card.meta.connections": "{count} 条连接",
|
|
477
486
|
"card.action.download": "下载",
|
|
478
487
|
"card.action.downloading": "下载中…",
|
|
488
|
+
"card.action.place": "放置到编辑器",
|
|
489
|
+
"card.action.placing": "放置中…",
|
|
490
|
+
"card.place.done": "已放置 {count} 个文件到编辑器",
|
|
491
|
+
"card.place.partial": "已放置 {placed} 个,{failed} 个失败:",
|
|
492
|
+
"card.place.failed": "放置失败:",
|
|
479
493
|
"card.action.regenerate": "重新生成",
|
|
480
494
|
"card.action.regenerating": "重新生成中…",
|
|
481
495
|
"card.action.inspect": "详情",
|
|
@@ -522,6 +536,11 @@ window.__ModuleLoader__.load({
|
|
|
522
536
|
"card.meta.connections": "{count} connection(s)",
|
|
523
537
|
"card.action.download": "Download",
|
|
524
538
|
"card.action.downloading": "Downloading…",
|
|
539
|
+
"card.action.place": "Place in editor",
|
|
540
|
+
"card.action.placing": "Placing…",
|
|
541
|
+
"card.place.done": "Placed {count} file(s) into the editor",
|
|
542
|
+
"card.place.partial": "Placed {placed}, {failed} failed: ",
|
|
543
|
+
"card.place.failed": "Placement failed: ",
|
|
525
544
|
"card.action.regenerate": "Regenerate",
|
|
526
545
|
"card.action.regenerating": "Regenerating…",
|
|
527
546
|
"card.action.inspect": "Inspect",
|
|
@@ -104849,6 +104868,84 @@ while (n3 === a3[++i3] && n3 === a3[++i3] && n3 === a3[++i3] && n3 === a3[++i3]
|
|
|
104849
104868
|
}
|
|
104850
104869
|
}
|
|
104851
104870
|
//#endregion
|
|
104871
|
+
//#region ../dsh-artifacts/lib/placement.mjs
|
|
104872
|
+
/**
|
|
104873
|
+
* Editors each artifact type may be placed into.
|
|
104874
|
+
*
|
|
104875
|
+
* | Artifact | sch | pcb | symbol | footprint | generic |
|
|
104876
|
+
* |-----------|-----|-----|--------|-----------|---------|
|
|
104877
|
+
* | schematic | yes | no | no | no | yes |
|
|
104878
|
+
* | symbol | yes | no | yes | no | yes |
|
|
104879
|
+
* | footprint | no | yes | no | yes | yes |
|
|
104880
|
+
* | pcb | no | yes | no | no | yes |
|
|
104881
|
+
*
|
|
104882
|
+
* `pcb` rides the same design-block RPC as schematics (`DesignContent.pcb`):
|
|
104883
|
+
* the editor-side PCB placement flow is the placement team's work and builds
|
|
104884
|
+
* on this channel, so it must not be assumed away. `generic` means the host
|
|
104885
|
+
* did not declare a specific editor context, and per the host contract such a
|
|
104886
|
+
* deployment supports ALL placement targets by design — the EDA host is the
|
|
104887
|
+
* final authority (HQ Edge re-enforces the same matrix server-side). Unknown
|
|
104888
|
+
* editor values still fail closed.
|
|
104889
|
+
*/
|
|
104890
|
+
const COMPATIBLE_EDITORS = {
|
|
104891
|
+
schematic: /* @__PURE__ */ new Set(["sch", "generic"]),
|
|
104892
|
+
symbol: /* @__PURE__ */ new Set([
|
|
104893
|
+
"sch",
|
|
104894
|
+
"symbol",
|
|
104895
|
+
"generic"
|
|
104896
|
+
]),
|
|
104897
|
+
footprint: /* @__PURE__ */ new Set([
|
|
104898
|
+
"pcb",
|
|
104899
|
+
"footprint",
|
|
104900
|
+
"generic"
|
|
104901
|
+
]),
|
|
104902
|
+
pcb: /* @__PURE__ */ new Set(["pcb", "generic"])
|
|
104903
|
+
};
|
|
104904
|
+
/**
|
|
104905
|
+
* Can `artifactType` be placed into the editor identified by `editorType`?
|
|
104906
|
+
*
|
|
104907
|
+
* Fails closed: an unrecognized artifact or editor type yields `false`.
|
|
104908
|
+
*/
|
|
104909
|
+
function canPlaceArtifact(artifactType, editorType) {
|
|
104910
|
+
const allowed = COMPATIBLE_EDITORS[artifactType];
|
|
104911
|
+
return allowed ? allowed.has(editorType) : false;
|
|
104912
|
+
}
|
|
104913
|
+
/**
|
|
104914
|
+
* Build the Place support for `artifactType` from a lazily-resolved `hqEdge`.
|
|
104915
|
+
*
|
|
104916
|
+
* Returns null when `getHqEdge` is unavailable (standalone DSH has no
|
|
104917
|
+
* edge-bridge, hence no `hqEdge` service). Otherwise returns a GETTER so
|
|
104918
|
+
* callers resolve the service lazily — plugin load order and HQ Edge restarts
|
|
104919
|
+
* both resolve correctly at click time. The getter yields null when
|
|
104920
|
+
* placement is unavailable or the current editor cannot accept the artifact
|
|
104921
|
+
* type (the card hides the button in that case).
|
|
104922
|
+
*
|
|
104923
|
+
* Editor gating is UX-only; HQ Edge re-enforces the matrix server-side (409).
|
|
104924
|
+
*/
|
|
104925
|
+
function placeSupportOf(getHqEdge, artifactType) {
|
|
104926
|
+
if (typeof getHqEdge !== "function") return null;
|
|
104927
|
+
return () => {
|
|
104928
|
+
let hqEdge;
|
|
104929
|
+
try {
|
|
104930
|
+
hqEdge = getHqEdge();
|
|
104931
|
+
} catch {
|
|
104932
|
+
return null;
|
|
104933
|
+
}
|
|
104934
|
+
if (!hqEdge || typeof hqEdge.placeArtifact !== "function") return null;
|
|
104935
|
+
let editorType = "";
|
|
104936
|
+
try {
|
|
104937
|
+
editorType = hqEdge.context?.getEditorType?.() ?? hqEdge.context?.getCurrent?.()?.editorType ?? "";
|
|
104938
|
+
} catch {
|
|
104939
|
+
return null;
|
|
104940
|
+
}
|
|
104941
|
+
if (!editorType || !canPlaceArtifact(artifactType, editorType)) return null;
|
|
104942
|
+
return {
|
|
104943
|
+
editorType,
|
|
104944
|
+
place: (request) => hqEdge.placeArtifact(request)
|
|
104945
|
+
};
|
|
104946
|
+
};
|
|
104947
|
+
}
|
|
104948
|
+
//#endregion
|
|
104852
104949
|
//#region src/trace.ts
|
|
104853
104950
|
/**
|
|
104854
104951
|
* Marker appended to the leaf of a synthesized instance path. It is invisible
|
|
@@ -105798,6 +105895,7 @@ while (n3 === a3[++i3] && n3 === a3[++i3] && n3 === a3[++i3] && n3 === a3[++i3]
|
|
|
105798
105895
|
};
|
|
105799
105896
|
}, [artifactKey, state.phase]);
|
|
105800
105897
|
const [busy, setBusy] = (0, react.useState)(null);
|
|
105898
|
+
const [placeStatus, setPlaceStatus] = (0, react.useState)(null);
|
|
105801
105899
|
function onDownload() {
|
|
105802
105900
|
if (busy || payload.phase !== "ready") return;
|
|
105803
105901
|
const kind = result?.kind ?? "schematic";
|
|
@@ -105819,6 +105917,52 @@ while (n3 === a3[++i3] && n3 === a3[++i3] && n3 === a3[++i3] && n3 === a3[++i3]
|
|
|
105819
105917
|
setBusy(null);
|
|
105820
105918
|
});
|
|
105821
105919
|
}
|
|
105920
|
+
/**
|
|
105921
|
+
* Place the generated design into the host EDA editor via HQ Edge.
|
|
105922
|
+
*
|
|
105923
|
+
* A system design sends ONE request (the project zip; HQ Edge derives the
|
|
105924
|
+
* root from the `*.kicad_pro` name — the same convention as the preview).
|
|
105925
|
+
* A multi-sheet schematic sends one request per sheet so every sheet lands
|
|
105926
|
+
* in the editor. Failures are collected and reported as a one-line status;
|
|
105927
|
+
* a partial success is explicitly surfaced, not swallowed.
|
|
105928
|
+
*/
|
|
105929
|
+
function placeRequestsOf() {
|
|
105930
|
+
if (!result) return [];
|
|
105931
|
+
return result.artifacts.filter((a) => a.uri).map((a) => ({
|
|
105932
|
+
artifactUri: a.uri,
|
|
105933
|
+
filename: a.filename
|
|
105934
|
+
}));
|
|
105935
|
+
}
|
|
105936
|
+
async function onPlace() {
|
|
105937
|
+
if (busy) return;
|
|
105938
|
+
const support = placeSupportOf(props.getHqEdge, "schematic")?.();
|
|
105939
|
+
if (!support) return;
|
|
105940
|
+
const requests = placeRequestsOf();
|
|
105941
|
+
if (requests.length === 0) return;
|
|
105942
|
+
setBusy("place");
|
|
105943
|
+
setPlaceStatus(null);
|
|
105944
|
+
let placed = 0;
|
|
105945
|
+
const failures = [];
|
|
105946
|
+
for (const req of requests) try {
|
|
105947
|
+
await support.place({
|
|
105948
|
+
type: "schematic",
|
|
105949
|
+
artifactUri: req.artifactUri,
|
|
105950
|
+
...req.filename ? { filename: req.filename } : {}
|
|
105951
|
+
});
|
|
105952
|
+
placed++;
|
|
105953
|
+
} catch (err) {
|
|
105954
|
+
const detail = String(err?.message || err);
|
|
105955
|
+
console.warn("[hq-schematic-gen] place failed", detail);
|
|
105956
|
+
failures.push(detail);
|
|
105957
|
+
}
|
|
105958
|
+
setBusy(null);
|
|
105959
|
+
if (failures.length === 0) setPlaceStatus(t("card.place.done", { count: placed }));
|
|
105960
|
+
else if (placed > 0) setPlaceStatus(t("card.place.partial", {
|
|
105961
|
+
placed,
|
|
105962
|
+
failed: failures.length
|
|
105963
|
+
}) + " " + failures[0]);
|
|
105964
|
+
else setPlaceStatus(t("card.place.failed") + " " + failures[0]);
|
|
105965
|
+
}
|
|
105822
105966
|
if (state.phase === "needs_auth") return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(LoginCard, {
|
|
105823
105967
|
toolName: props.toolName,
|
|
105824
105968
|
authState: props.authState,
|
|
@@ -105891,6 +106035,9 @@ while (n3 === a3[++i3] && n3 === a3[++i3] && n3 === a3[++i3] && n3 === a3[++i3]
|
|
|
105891
106035
|
})
|
|
105892
106036
|
});
|
|
105893
106037
|
const canDownload = payload.phase === "ready" && (payload.source != null || payload.bytes != null);
|
|
106038
|
+
const placeSupport = placeSupportOf(props.getHqEdge, "schematic");
|
|
106039
|
+
const placeableCount = result.artifacts.filter((a) => a.uri).length;
|
|
106040
|
+
const canPlace = !!placeSupport?.() && placeableCount > 0;
|
|
105894
106041
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
105895
106042
|
className: "hq-sch",
|
|
105896
106043
|
children: [
|
|
@@ -105911,6 +106058,13 @@ while (n3 === a3[++i3] && n3 === a3[++i3] && n3 === a3[++i3] && n3 === a3[++i3]
|
|
|
105911
106058
|
disabled: !canDownload || busy === "download",
|
|
105912
106059
|
children: ["⭳ ", busy === "download" ? t("card.action.downloading") : t("card.action.download")]
|
|
105913
106060
|
}),
|
|
106061
|
+
canPlace ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
106062
|
+
type: "button",
|
|
106063
|
+
className: "hq-sch__act",
|
|
106064
|
+
onClick: onPlace,
|
|
106065
|
+
disabled: busy === "place",
|
|
106066
|
+
children: ["⇥ ", busy === "place" ? t("card.action.placing") : t("card.action.place")]
|
|
106067
|
+
}) : null,
|
|
105914
106068
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
105915
106069
|
type: "button",
|
|
105916
106070
|
className: "hq-sch__act",
|
|
@@ -105925,7 +106079,11 @@ while (n3 === a3[++i3] && n3 === a3[++i3] && n3 === a3[++i3] && n3 === a3[++i3]
|
|
|
105925
106079
|
children: t("card.action.inspect")
|
|
105926
106080
|
}) : null
|
|
105927
106081
|
]
|
|
105928
|
-
})
|
|
106082
|
+
}),
|
|
106083
|
+
placeStatus ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
106084
|
+
className: "hq-sch__note",
|
|
106085
|
+
children: placeStatus
|
|
106086
|
+
}) : null
|
|
105929
106087
|
]
|
|
105930
106088
|
});
|
|
105931
106089
|
});
|
|
@@ -106007,6 +106165,7 @@ while (n3 === a3[++i3] && n3 === a3[++i3] && n3 === a3[++i3] && n3 === a3[++i3]
|
|
|
106007
106165
|
function apply(ctx) {
|
|
106008
106166
|
const disposers = [];
|
|
106009
106167
|
const useAuthState = createUseAuthState(ctx.get("huaqiuAuth")?.auth);
|
|
106168
|
+
const getHqEdge = () => ctx.get("hqEdge");
|
|
106010
106169
|
const sendPrompt = (sessionId, message) => {
|
|
106011
106170
|
const sessions = ctx.get("sessions");
|
|
106012
106171
|
if (!sessions || typeof sessions.binding !== "function" || !sessionId) return Promise.reject(/* @__PURE__ */ new Error("no session conversation channel"));
|
|
@@ -106026,7 +106185,8 @@ while (n3 === a3[++i3] && n3 === a3[++i3] && n3 === a3[++i3] && n3 === a3[++i3]
|
|
|
106026
106185
|
sessionId: props.sessionId,
|
|
106027
106186
|
callId: props.callId,
|
|
106028
106187
|
sendPrompt,
|
|
106029
|
-
authState: useAuthState()
|
|
106188
|
+
authState: useAuthState(),
|
|
106189
|
+
getHqEdge
|
|
106030
106190
|
});
|
|
106031
106191
|
for (const toolName of TOOLVIEW_KEYS) disposers.push(slots.inject("tool.call.toolview", () => slots.register({
|
|
106032
106192
|
name: "tool.call.toolview",
|