@huaqiu/dsh-tool-schematic-gen 0.3.7 → 0.3.8

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 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
- if (kind === "system") artifact = artOf(o.zipArtifact);
48
- else {
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
- artifact = list.length > 0 ? artOf(list[0]) : null;
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,69 @@ 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 | no |
104878
+ * | symbol | yes | no | yes | no | no |
104879
+ * | footprint | no | yes | no | yes | no |
104880
+ *
104881
+ * `generic` appears in no row: HQ Edge may be inside *some* EDA host without
104882
+ * knowing which editor, so placement must fail closed.
104883
+ */
104884
+ const COMPATIBLE_EDITORS = {
104885
+ schematic: /* @__PURE__ */ new Set(["sch"]),
104886
+ symbol: /* @__PURE__ */ new Set(["sch", "symbol"]),
104887
+ footprint: /* @__PURE__ */ new Set(["pcb", "footprint"])
104888
+ };
104889
+ /**
104890
+ * Can `artifactType` be placed into the editor identified by `editorType`?
104891
+ *
104892
+ * Fails closed: an unrecognized artifact or editor type yields `false`.
104893
+ */
104894
+ function canPlaceArtifact(artifactType, editorType) {
104895
+ const allowed = COMPATIBLE_EDITORS[artifactType];
104896
+ return allowed ? allowed.has(editorType) : false;
104897
+ }
104898
+ /**
104899
+ * Build the Place support for `artifactType` from a lazily-resolved `hqEdge`.
104900
+ *
104901
+ * Returns null when `getHqEdge` is unavailable (standalone DSH has no
104902
+ * edge-bridge, hence no `hqEdge` service). Otherwise returns a GETTER so
104903
+ * callers resolve the service lazily — plugin load order and HQ Edge restarts
104904
+ * both resolve correctly at click time. The getter yields null when
104905
+ * placement is unavailable or the current editor cannot accept the artifact
104906
+ * type (the card hides the button in that case).
104907
+ *
104908
+ * Editor gating is UX-only; HQ Edge re-enforces the matrix server-side (409).
104909
+ */
104910
+ function placeSupportOf(getHqEdge, artifactType) {
104911
+ if (typeof getHqEdge !== "function") return null;
104912
+ return () => {
104913
+ let hqEdge;
104914
+ try {
104915
+ hqEdge = getHqEdge();
104916
+ } catch {
104917
+ return null;
104918
+ }
104919
+ if (!hqEdge || typeof hqEdge.placeArtifact !== "function") return null;
104920
+ let editorType = "";
104921
+ try {
104922
+ editorType = hqEdge.context?.getEditorType?.() ?? hqEdge.context?.getCurrent?.()?.editorType ?? "";
104923
+ } catch {
104924
+ return null;
104925
+ }
104926
+ if (!editorType || !canPlaceArtifact(artifactType, editorType)) return null;
104927
+ return {
104928
+ editorType,
104929
+ place: (request) => hqEdge.placeArtifact(request)
104930
+ };
104931
+ };
104932
+ }
104933
+ //#endregion
104852
104934
  //#region src/trace.ts
104853
104935
  /**
104854
104936
  * Marker appended to the leaf of a synthesized instance path. It is invisible
@@ -105798,6 +105880,7 @@ while (n3 === a3[++i3] && n3 === a3[++i3] && n3 === a3[++i3] && n3 === a3[++i3]
105798
105880
  };
105799
105881
  }, [artifactKey, state.phase]);
105800
105882
  const [busy, setBusy] = (0, react.useState)(null);
105883
+ const [placeStatus, setPlaceStatus] = (0, react.useState)(null);
105801
105884
  function onDownload() {
105802
105885
  if (busy || payload.phase !== "ready") return;
105803
105886
  const kind = result?.kind ?? "schematic";
@@ -105819,6 +105902,52 @@ while (n3 === a3[++i3] && n3 === a3[++i3] && n3 === a3[++i3] && n3 === a3[++i3]
105819
105902
  setBusy(null);
105820
105903
  });
105821
105904
  }
105905
+ /**
105906
+ * Place the generated design into the host EDA editor via HQ Edge.
105907
+ *
105908
+ * A system design sends ONE request (the project zip; HQ Edge derives the
105909
+ * root from the `*.kicad_pro` name — the same convention as the preview).
105910
+ * A multi-sheet schematic sends one request per sheet so every sheet lands
105911
+ * in the editor. Failures are collected and reported as a one-line status;
105912
+ * a partial success is explicitly surfaced, not swallowed.
105913
+ */
105914
+ function placeRequestsOf() {
105915
+ if (!result) return [];
105916
+ return result.artifacts.filter((a) => a.uri).map((a) => ({
105917
+ artifactUri: a.uri,
105918
+ filename: a.filename
105919
+ }));
105920
+ }
105921
+ async function onPlace() {
105922
+ if (busy) return;
105923
+ const support = placeSupportOf(props.getHqEdge, "schematic")?.();
105924
+ if (!support) return;
105925
+ const requests = placeRequestsOf();
105926
+ if (requests.length === 0) return;
105927
+ setBusy("place");
105928
+ setPlaceStatus(null);
105929
+ let placed = 0;
105930
+ const failures = [];
105931
+ for (const req of requests) try {
105932
+ await support.place({
105933
+ type: "schematic",
105934
+ artifactUri: req.artifactUri,
105935
+ ...req.filename ? { filename: req.filename } : {}
105936
+ });
105937
+ placed++;
105938
+ } catch (err) {
105939
+ const detail = String(err?.message || err);
105940
+ console.warn("[hq-schematic-gen] place failed", detail);
105941
+ failures.push(detail);
105942
+ }
105943
+ setBusy(null);
105944
+ if (failures.length === 0) setPlaceStatus(t("card.place.done", { count: placed }));
105945
+ else if (placed > 0) setPlaceStatus(t("card.place.partial", {
105946
+ placed,
105947
+ failed: failures.length
105948
+ }) + " " + failures[0]);
105949
+ else setPlaceStatus(t("card.place.failed") + " " + failures[0]);
105950
+ }
105822
105951
  if (state.phase === "needs_auth") return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(LoginCard, {
105823
105952
  toolName: props.toolName,
105824
105953
  authState: props.authState,
@@ -105891,6 +106020,9 @@ while (n3 === a3[++i3] && n3 === a3[++i3] && n3 === a3[++i3] && n3 === a3[++i3]
105891
106020
  })
105892
106021
  });
105893
106022
  const canDownload = payload.phase === "ready" && (payload.source != null || payload.bytes != null);
106023
+ const placeSupport = placeSupportOf(props.getHqEdge, "schematic");
106024
+ const placeableCount = result.artifacts.filter((a) => a.uri).length;
106025
+ const canPlace = !!placeSupport?.() && placeableCount > 0;
105894
106026
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
105895
106027
  className: "hq-sch",
105896
106028
  children: [
@@ -105911,6 +106043,13 @@ while (n3 === a3[++i3] && n3 === a3[++i3] && n3 === a3[++i3] && n3 === a3[++i3]
105911
106043
  disabled: !canDownload || busy === "download",
105912
106044
  children: ["⭳ ", busy === "download" ? t("card.action.downloading") : t("card.action.download")]
105913
106045
  }),
106046
+ canPlace ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
106047
+ type: "button",
106048
+ className: "hq-sch__act",
106049
+ onClick: onPlace,
106050
+ disabled: busy === "place",
106051
+ children: ["⇥ ", busy === "place" ? t("card.action.placing") : t("card.action.place")]
106052
+ }) : null,
105914
106053
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
105915
106054
  type: "button",
105916
106055
  className: "hq-sch__act",
@@ -105925,7 +106064,11 @@ while (n3 === a3[++i3] && n3 === a3[++i3] && n3 === a3[++i3] && n3 === a3[++i3]
105925
106064
  children: t("card.action.inspect")
105926
106065
  }) : null
105927
106066
  ]
105928
- })
106067
+ }),
106068
+ placeStatus ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
106069
+ className: "hq-sch__note",
106070
+ children: placeStatus
106071
+ }) : null
105929
106072
  ]
105930
106073
  });
105931
106074
  });
@@ -106007,6 +106150,7 @@ while (n3 === a3[++i3] && n3 === a3[++i3] && n3 === a3[++i3] && n3 === a3[++i3]
106007
106150
  function apply(ctx) {
106008
106151
  const disposers = [];
106009
106152
  const useAuthState = createUseAuthState(ctx.get("huaqiuAuth")?.auth);
106153
+ const getHqEdge = () => ctx.get("hqEdge");
106010
106154
  const sendPrompt = (sessionId, message) => {
106011
106155
  const sessions = ctx.get("sessions");
106012
106156
  if (!sessions || typeof sessions.binding !== "function" || !sessionId) return Promise.reject(/* @__PURE__ */ new Error("no session conversation channel"));
@@ -106026,7 +106170,8 @@ while (n3 === a3[++i3] && n3 === a3[++i3] && n3 === a3[++i3] && n3 === a3[++i3]
106026
106170
  sessionId: props.sessionId,
106027
106171
  callId: props.callId,
106028
106172
  sendPrompt,
106029
- authState: useAuthState()
106173
+ authState: useAuthState(),
106174
+ getHqEdge
106030
106175
  });
106031
106176
  for (const toolName of TOOLVIEW_KEYS) disposers.push(slots.inject("tool.call.toolview", () => slots.register({
106032
106177
  name: "tool.call.toolview",