@huaqiu/component-gen-app 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/dist/index.html CHANGED
@@ -4,7 +4,7 @@
4
4
  <meta charset="UTF-8" />
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
6
  <title>华秋 Component Gen</title>
7
- <script type="module" crossorigin src="./assets/index-DTShI_jq.js"></script>
7
+ <script type="module" crossorigin src="./assets/index-B83Ehy7Z.js"></script>
8
8
  </head>
9
9
  <body>
10
10
  <div id="root"></div>
package/lib/index.d.ts CHANGED
@@ -129,6 +129,23 @@ interface ComponentGenAuthPort {
129
129
  login(): Promise<void>;
130
130
  onAuthStateChanged(listener: (authenticated: boolean) => void): () => void;
131
131
  }
132
+ /**
133
+ * Optional Place capability: send a generated artifact into the host EDA
134
+ * editor through HQ Edge (`hqEdge.placeArtifact`, same-origin edge-bridge).
135
+ *
136
+ * Structurally defined — the app stays DSH-agnostic. The host adapter decides
137
+ * availability via `canPlace` (editor compatibility, UX-only: HQ Edge
138
+ * re-enforces the same matrix server-side). Absent in standalone deployments,
139
+ * in which case the app hides the Place action.
140
+ */
141
+ interface ComponentGenPlacePort {
142
+ canPlace(type: 'symbol' | 'footprint'): boolean;
143
+ placeArtifact(request: {
144
+ type: 'symbol' | 'footprint';
145
+ artifactUri: string;
146
+ filename?: string;
147
+ }): Promise<unknown>;
148
+ }
132
149
  /** The whole contract the app needs from its host. */
133
150
  interface ComponentGenPorts {
134
151
  config(): Promise<ComponentGenConfig>;
@@ -144,6 +161,8 @@ interface ComponentGenPorts {
144
161
  /** data URL of a stored input thumbnail. */
145
162
  inputImage(imageId: string): Promise<string>;
146
163
  auth: ComponentGenAuthPort;
164
+ /** Optional Place-into-editor capability (HQ Edge host only). */
165
+ place?: ComponentGenPlacePort;
147
166
  }
148
167
  //#endregion
149
168
  //#region src/App.d.ts
@@ -289,6 +308,12 @@ declare const ZH: {
289
308
  readonly loadMore: "加载更多";
290
309
  readonly noMore: "没有更多了";
291
310
  };
311
+ readonly result: {
312
+ readonly place: "放置到编辑器";
313
+ readonly placing: "放置中…";
314
+ readonly placeDone: "已放置到编辑器";
315
+ readonly placeFailed: "放置失败:";
316
+ };
292
317
  readonly auth: {
293
318
  readonly notLoggedIn: "未登录华秋账号";
294
319
  readonly login: "登录华秋 EDA";
@@ -404,6 +429,8 @@ interface HttpPortsOptions {
404
429
  artifactsBase?: string;
405
430
  doFetch?: typeof fetch;
406
431
  auth?: ComponentGenAuthPort;
432
+ /** Optional Place-into-editor capability, forwarded verbatim. */
433
+ place?: ComponentGenPlacePort;
407
434
  }
408
435
  /** The shared fetch client. */
409
436
  declare function createHttpPorts(options: HttpPortsOptions): ComponentGenPorts;
package/lib/index.js CHANGED
@@ -136,6 +136,12 @@ const ZH = {
136
136
  loadMore: "加载更多",
137
137
  noMore: "没有更多了"
138
138
  },
139
+ result: {
140
+ place: "放置到编辑器",
141
+ placing: "放置中…",
142
+ placeDone: "已放置到编辑器",
143
+ placeFailed: "放置失败:"
144
+ },
139
145
  auth: {
140
146
  notLoggedIn: "未登录华秋账号",
141
147
  login: "登录华秋 EDA",
@@ -278,6 +284,12 @@ const EN = {
278
284
  loadMore: "Load more",
279
285
  noMore: "No more"
280
286
  },
287
+ result: {
288
+ place: "Place in editor",
289
+ placing: "Placing…",
290
+ placeDone: "Placed into the editor",
291
+ placeFailed: "Placement failed: "
292
+ },
281
293
  auth: {
282
294
  notLoggedIn: "Not logged in to Huaqiu EDA",
283
295
  login: "Login to Huaqiu EDA",
@@ -626,6 +638,19 @@ function artifactIdOf(result) {
626
638
  }
627
639
  return null;
628
640
  }
641
+ /**
642
+ * HQ Edge-resolvable URI of the generated artifact (`file://`), attached by
643
+ * the node half when the placement channel is available. Place stays hidden
644
+ * when it is missing.
645
+ */
646
+ function artifactUriOf(result) {
647
+ const art = result.artifact;
648
+ if (art && typeof art === "object") {
649
+ const uri = art.uri;
650
+ if (typeof uri === "string" && uri) return uri;
651
+ }
652
+ return null;
653
+ }
629
654
  function filenameOf(result, kind) {
630
655
  const f = result.filename;
631
656
  if (typeof f === "string" && f) return f;
@@ -633,9 +658,12 @@ function filenameOf(result, kind) {
633
658
  }
634
659
  function ResultStage({ ports, kind, result, t, srcKey }) {
635
660
  const artifactId = artifactIdOf(result);
661
+ const artifactUri = artifactUriOf(result);
636
662
  const [content, setContent] = useState(null);
637
663
  const [previewErr, setPreviewErr] = useState(null);
638
664
  const [downloading, setDownloading] = useState(false);
665
+ const [placing, setPlacing] = useState(false);
666
+ const [placeStatus, setPlaceStatus] = useState(null);
639
667
  useEffect(() => {
640
668
  if (!artifactId) return;
641
669
  let cancelled = false;
@@ -676,6 +704,32 @@ function ResultStage({ ports, kind, result, t, srcKey }) {
676
704
  ]);
677
705
  const autoGenerated = result.autoGenerated === true;
678
706
  const note = typeof result.note === "string" ? result.note : null;
707
+ /** Send the generated artifact into the host EDA editor (HQ Edge only). */
708
+ const place = useCallback(async () => {
709
+ if (placing || !ports.place || !artifactUri) return;
710
+ setPlacing(true);
711
+ setPlaceStatus(null);
712
+ try {
713
+ await ports.place.placeArtifact({
714
+ type: kind,
715
+ artifactUri,
716
+ ...result.filename && typeof result.filename === "string" ? { filename: result.filename } : {}
717
+ });
718
+ setPlaceStatus(t("result.placeDone"));
719
+ } catch (e) {
720
+ setPlaceStatus(t("result.placeFailed") + String(e?.message || e));
721
+ } finally {
722
+ setPlacing(false);
723
+ }
724
+ }, [
725
+ placing,
726
+ ports,
727
+ artifactUri,
728
+ kind,
729
+ result,
730
+ t
731
+ ]);
732
+ const canPlace = !!ports.place?.canPlace(kind) && !!artifactUri;
679
733
  return /* @__PURE__ */ jsx("div", {
680
734
  className: "cga-panel",
681
735
  children: /* @__PURE__ */ jsxs("div", {
@@ -698,16 +752,26 @@ function ResultStage({ ports, kind, result, t, srcKey }) {
698
752
  className: "cga-banner cga-banner--info",
699
753
  children: note
700
754
  }) : null,
701
- /* @__PURE__ */ jsx("div", {
755
+ /* @__PURE__ */ jsxs("div", {
702
756
  className: "cga-actions",
703
- children: /* @__PURE__ */ jsx("button", {
757
+ children: [/* @__PURE__ */ jsx("button", {
704
758
  type: "button",
705
759
  className: "cga-btn cga-btn--primary",
706
760
  onClick: () => void download(),
707
761
  disabled: downloading,
708
762
  children: kind === "symbol" ? t("symbol.download") : t("footprint.download")
709
- })
710
- })
763
+ }), canPlace ? /* @__PURE__ */ jsx("button", {
764
+ type: "button",
765
+ className: "cga-btn",
766
+ onClick: () => void place(),
767
+ disabled: placing,
768
+ children: placing ? t("result.placing") : t("result.place")
769
+ }) : null]
770
+ }),
771
+ placeStatus ? /* @__PURE__ */ jsx("div", {
772
+ className: "cga-banner cga-banner--info",
773
+ children: placeStatus
774
+ }) : null
711
775
  ]
712
776
  })
713
777
  });
@@ -2265,6 +2329,7 @@ function createHttpPorts(options) {
2265
2329
  const url = (p) => `${base}${p}`;
2266
2330
  const artifactUrl = (p) => `${artifactsBase}${p}`;
2267
2331
  return {
2332
+ ...options.place ? { place: options.place } : {},
2268
2333
  async config() {
2269
2334
  const cfg = await readJson(await doFetch(url("/config"), { headers: { accept: "application/json" } }));
2270
2335
  return {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@huaqiu/component-gen-app",
3
- "version": "0.3.7",
3
+ "version": "0.3.8",
4
4
  "type": "module",
5
5
  "main": "./lib/index.js",
6
6
  "types": "./lib/index.d.ts",
@@ -10,6 +10,7 @@
10
10
  import type {
11
11
  ComponentGenAuthPort,
12
12
  ComponentGenConfig,
13
+ ComponentGenPlacePort,
13
14
  ComponentGenPorts,
14
15
  HistoryEntry,
15
16
  HistoryPage,
@@ -44,6 +45,8 @@ export interface HttpPortsOptions {
44
45
  artifactsBase?: string
45
46
  doFetch?: typeof fetch
46
47
  auth?: ComponentGenAuthPort
48
+ /** Optional Place-into-editor capability, forwarded verbatim. */
49
+ place?: ComponentGenPlacePort
47
50
  }
48
51
 
49
52
  /** The shared fetch client. */
@@ -58,6 +61,8 @@ export function createHttpPorts(options: HttpPortsOptions): ComponentGenPorts {
58
61
  const artifactUrl = (p: string): string => `${artifactsBase}${p}`
59
62
 
60
63
  return {
64
+ // Optional Place capability, forwarded from the host adapter verbatim.
65
+ ...(options.place ? { place: options.place } : {}),
61
66
  async config(): Promise<ComponentGenConfig> {
62
67
  const res = await doFetch(url('/config'), { headers: { accept: 'application/json' } })
63
68
  const cfg = await readJson<Partial<ComponentGenConfig>>(res)
@@ -28,6 +28,20 @@ function artifactIdOf(result: Record<string, unknown>): string | null {
28
28
  return null
29
29
  }
30
30
 
31
+ /**
32
+ * HQ Edge-resolvable URI of the generated artifact (`file://`), attached by
33
+ * the node half when the placement channel is available. Place stays hidden
34
+ * when it is missing.
35
+ */
36
+ function artifactUriOf(result: Record<string, unknown>): string | null {
37
+ const art = result.artifact
38
+ if (art && typeof art === 'object') {
39
+ const uri = (art as { uri?: unknown }).uri
40
+ if (typeof uri === 'string' && uri) return uri
41
+ }
42
+ return null
43
+ }
44
+
31
45
  function filenameOf(result: Record<string, unknown>, kind: string): string {
32
46
  const f = result.filename
33
47
  if (typeof f === 'string' && f) return f
@@ -36,9 +50,12 @@ function filenameOf(result: Record<string, unknown>, kind: string): string {
36
50
 
37
51
  export function ResultStage({ ports, kind, result, t, srcKey }: ResultStageProps): ReactElement {
38
52
  const artifactId = artifactIdOf(result)
53
+ const artifactUri = artifactUriOf(result)
39
54
  const [content, setContent] = useState<string | null>(null)
40
55
  const [previewErr, setPreviewErr] = useState<string | null>(null)
41
56
  const [downloading, setDownloading] = useState(false)
57
+ const [placing, setPlacing] = useState(false)
58
+ const [placeStatus, setPlaceStatus] = useState<string | null>(null)
42
59
 
43
60
  useEffect(() => {
44
61
  if (!artifactId) return
@@ -71,6 +88,27 @@ export function ResultStage({ ports, kind, result, t, srcKey }: ResultStageProps
71
88
  const autoGenerated = result.autoGenerated === true
72
89
  const note = typeof result.note === 'string' ? result.note : null
73
90
 
91
+ /** Send the generated artifact into the host EDA editor (HQ Edge only). */
92
+ const place = useCallback(async (): Promise<void> => {
93
+ if (placing || !ports.place || !artifactUri) return
94
+ setPlacing(true)
95
+ setPlaceStatus(null)
96
+ try {
97
+ await ports.place.placeArtifact({
98
+ type: kind,
99
+ artifactUri,
100
+ ...(result.filename && typeof result.filename === 'string' ? { filename: result.filename } : {}),
101
+ })
102
+ setPlaceStatus(t('result.placeDone'))
103
+ } catch (e) {
104
+ setPlaceStatus(t('result.placeFailed') + String((e as Error)?.message || e))
105
+ } finally {
106
+ setPlacing(false)
107
+ }
108
+ }, [placing, ports, artifactUri, kind, result, t])
109
+
110
+ const canPlace = !!ports.place?.canPlace(kind) && !!artifactUri
111
+
74
112
  return (
75
113
  <div className="cga-panel">
76
114
  <div className="cga-panel__body">
@@ -85,7 +123,15 @@ export function ResultStage({ ports, kind, result, t, srcKey }: ResultStageProps
85
123
  <button type="button" className="cga-btn cga-btn--primary" onClick={() => void download()} disabled={downloading}>
86
124
  {kind === 'symbol' ? t('symbol.download') : t('footprint.download')}
87
125
  </button>
126
+ {canPlace
127
+ ? (
128
+ <button type="button" className="cga-btn" onClick={() => void place()} disabled={placing}>
129
+ {placing ? t('result.placing') : t('result.place')}
130
+ </button>
131
+ )
132
+ : null}
88
133
  </div>
134
+ {placeStatus ? <div className="cga-banner cga-banner--info">{placeStatus}</div> : null}
89
135
  </div>
90
136
  </div>
91
137
  )
package/src/copy/en.ts CHANGED
@@ -133,6 +133,12 @@ export const EN: DeepStrings<typeof ZH> = {
133
133
  loadMore: 'Load more',
134
134
  noMore: 'No more',
135
135
  },
136
+ result: {
137
+ place: 'Place in editor',
138
+ placing: 'Placing…',
139
+ placeDone: 'Placed into the editor',
140
+ placeFailed: 'Placement failed: ',
141
+ },
136
142
  auth: {
137
143
  notLoggedIn: 'Not logged in to Huaqiu EDA',
138
144
  login: 'Login to Huaqiu EDA',
package/src/copy/zh.ts CHANGED
@@ -130,6 +130,12 @@ export const ZH = {
130
130
  loadMore: '加载更多',
131
131
  noMore: '没有更多了',
132
132
  },
133
+ result: {
134
+ place: '放置到编辑器',
135
+ placing: '放置中…',
136
+ placeDone: '已放置到编辑器',
137
+ placeFailed: '放置失败:',
138
+ },
133
139
  auth: {
134
140
  notLoggedIn: '未登录华秋账号',
135
141
  login: '登录华秋 EDA',
package/src/ports.ts CHANGED
@@ -131,6 +131,20 @@ export interface ComponentGenAuthPort {
131
131
  onAuthStateChanged(listener: (authenticated: boolean) => void): () => void
132
132
  }
133
133
 
134
+ /**
135
+ * Optional Place capability: send a generated artifact into the host EDA
136
+ * editor through HQ Edge (`hqEdge.placeArtifact`, same-origin edge-bridge).
137
+ *
138
+ * Structurally defined — the app stays DSH-agnostic. The host adapter decides
139
+ * availability via `canPlace` (editor compatibility, UX-only: HQ Edge
140
+ * re-enforces the same matrix server-side). Absent in standalone deployments,
141
+ * in which case the app hides the Place action.
142
+ */
143
+ export interface ComponentGenPlacePort {
144
+ canPlace(type: 'symbol' | 'footprint'): boolean
145
+ placeArtifact(request: { type: 'symbol' | 'footprint'; artifactUri: string; filename?: string }): Promise<unknown>
146
+ }
147
+
134
148
  /** The whole contract the app needs from its host. */
135
149
  export interface ComponentGenPorts {
136
150
  config(): Promise<ComponentGenConfig>
@@ -146,4 +160,6 @@ export interface ComponentGenPorts {
146
160
  /** data URL of a stored input thumbnail. */
147
161
  inputImage(imageId: string): Promise<string>
148
162
  auth: ComponentGenAuthPort
163
+ /** Optional Place-into-editor capability (HQ Edge host only). */
164
+ place?: ComponentGenPlacePort
149
165
  }