@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 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,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",