@arronqzy/react-blueprint 1.0.1 → 1.0.4

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.
Files changed (39) hide show
  1. package/README.md +103 -0
  2. package/index.ts +2 -1
  3. package/package.json +7 -6
  4. package/src/BlueprintContextMenu.tsx +5 -3
  5. package/src/BlueprintNodeConfigSidebar.tsx +59 -57
  6. package/src/blueprint.css +21 -15
  7. package/src/components/BlueprintExecutionLogPanel.tsx +32 -30
  8. package/src/components/BlueprintLibrarySelect.tsx +11 -4
  9. package/src/components/BlueprintMetaDialog.tsx +20 -10
  10. package/src/components/BlueprintNodeSwitchTaskDialog.tsx +14 -8
  11. package/src/components/BlueprintPanelToolbar.tsx +50 -42
  12. package/src/components/ClockNodeConfigPanel.tsx +19 -12
  13. package/src/components/FetchNodeConfigPanel.tsx +40 -34
  14. package/src/components/FetchUrlAutocomplete.tsx +3 -1
  15. package/src/components/JsonNodeConfigPanel.tsx +15 -7
  16. package/src/components/LogicNodeConfigPanel.tsx +13 -7
  17. package/src/components/ViewElementMultiSelect.tsx +25 -18
  18. package/src/dev-seed.ts +2 -2
  19. package/src/fetch-config-task-store.ts +8 -2
  20. package/src/graph/blueprint-graph.ts +5 -1
  21. package/src/graph/document.ts +97 -22
  22. package/src/graph/node-summary.ts +32 -18
  23. package/src/graph/sync-nodes.ts +3 -1
  24. package/src/hooks/useBlueprintDebugSession.ts +24 -22
  25. package/src/hooks/useBlueprintFlowColorMode.ts +30 -6
  26. package/src/hooks/useBlueprintPageLifecycle.ts +29 -1
  27. package/src/library/blueprint-io.ts +12 -3
  28. package/src/library/swagger-docs.ts +15 -5
  29. package/src/main.tsx +12 -5
  30. package/src/nodes/AndFlowNode.tsx +7 -5
  31. package/src/nodes/BlueprintFlowNode.tsx +4 -2
  32. package/src/nodes/BlueprintNodeCard.tsx +4 -2
  33. package/src/nodes/BlueprintNodeShell.tsx +4 -2
  34. package/src/nodes/ClockFlowNode.tsx +5 -3
  35. package/src/nodes/FetchFlowNode.tsx +4 -2
  36. package/src/nodes/JsonFlowNode.tsx +4 -2
  37. package/src/nodes/LifecycleFlowNode.tsx +5 -3
  38. package/src/nodes/LogicFlowNode.tsx +4 -2
  39. package/src/runtime/document-to-runnable-graph.ts +9 -6
@@ -1,8 +1,32 @@
1
- import { useTheme } from "@arronqzy/ui";
1
+ import { useSyncExternalStore } from "react";
2
2
 
3
- /** 与 next-themes 全局主题对齐,勿用 React Flow 的 system(会跟 OS 走) */
4
- export function useBlueprintFlowColorMode(): "light" | "dark" {
5
- const { resolvedTheme, theme } = useTheme();
6
- const current = resolvedTheme ?? theme;
7
- return current === "dark" ? "dark" : "light";
3
+ export type BlueprintColorMode = "light" | "dark";
4
+
5
+ function readDocumentColorMode(): BlueprintColorMode {
6
+ if (typeof document === "undefined") return "light";
7
+ const root = document.documentElement;
8
+ if (root.classList.contains("dark")) return "dark";
9
+ if (root.classList.contains("light")) return "light";
10
+ const dataTheme = root.dataset.theme;
11
+ if (dataTheme === "dark" || dataTheme === "light") return dataTheme;
12
+ return "light";
13
+ }
14
+
15
+ function subscribeDocumentColorMode(onChange: () => void) {
16
+ const root = document.documentElement;
17
+ const observer = new MutationObserver(onChange);
18
+ observer.observe(root, {
19
+ attributes: true,
20
+ attributeFilter: ["class", "data-theme"],
21
+ });
22
+ return () => observer.disconnect();
23
+ }
24
+
25
+ /** 与 document 上的全局主题(class / data-theme)对齐,避免与 panel 手动切换脱节 */
26
+ export function useBlueprintFlowColorMode(): BlueprintColorMode {
27
+ return useSyncExternalStore(
28
+ subscribeDocumentColorMode,
29
+ readDocumentColorMode,
30
+ () => "light"
31
+ );
8
32
  }
@@ -1,4 +1,4 @@
1
- import { useEffect, useRef, useState } from "react";
1
+ import { useCallback, useEffect, useRef, useState } from "react";
2
2
  import {
3
3
  BlueprintGraphRunner,
4
4
  detectBlueprintReferenceCycle,
@@ -242,4 +242,32 @@ export function useBlueprintPageLifecycle({
242
242
  if (!enabled || !bootCompleted) return;
243
243
  void emitPhases(graphRef.current, ["updated"], runnerOptionsRef.current);
244
244
  }, [bootCompleted, enabled, onUpdated]);
245
+
246
+ const triggerBlueprintNode = useCallback(
247
+ async (nodeId: string, inputValue?: unknown) => {
248
+ const id = nodeId.trim();
249
+ if (!id) return;
250
+ const opts = runnerOptionsRef.current;
251
+ if (opts.resolveLibraryBlueprint) {
252
+ const result = await detectBlueprintReferenceCycle({
253
+ rootGraph: documentToRunnableGraph(graphRef.current.document, {
254
+ libraryNameById: opts.libraryNameById,
255
+ }),
256
+ rootLibraryBlueprintId: opts.rootLibraryBlueprintId,
257
+ resolveLibraryBlueprint: opts.resolveLibraryBlueprint,
258
+ resolveBlueprintName: (libId) =>
259
+ resolveBlueprintName(opts.libraryNameById, libId),
260
+ });
261
+ if (!result.ok) {
262
+ opts.onExecutionBlocked?.(result.message);
263
+ return;
264
+ }
265
+ }
266
+ const runner = createRunner(graphRef.current, opts);
267
+ await runner.triggerNode(id, inputValue);
268
+ },
269
+ []
270
+ );
271
+
272
+ return { triggerBlueprintNode };
245
273
  }
@@ -8,6 +8,15 @@ import {
8
8
  type BlueprintLibrarySource,
9
9
  type BlueprintMetaDraft,
10
10
  } from "./types";
11
+ import { resolveLocale, tForLocale } from "@arronqzy/i18n";
12
+
13
+ function unnamedBlueprintName() {
14
+ return tForLocale(resolveLocale())("blueprint.dialog.unnamedBlueprint");
15
+ }
16
+
17
+ function importedBlueprintName() {
18
+ return tForLocale(resolveLocale())("blueprint.dialog.importedBlueprint");
19
+ }
11
20
 
12
21
  export function createLibraryBlueprintId() {
13
22
  return createNodeId("lib_bp");
@@ -27,7 +36,7 @@ export function buildBlueprintExportPayload(
27
36
  return {
28
37
  kind: BLUEPRINT_EXPORT_KIND,
29
38
  version: BLUEPRINT_EXPORT_VERSION,
30
- name: meta.name.trim() || "未命名蓝图",
39
+ name: meta.name.trim() || unnamedBlueprintName(),
31
40
  remark: meta.remark.trim() || undefined,
32
41
  exportedAt: Date.now(),
33
42
  document: {
@@ -51,7 +60,7 @@ export function parseBlueprintImportFile(raw: unknown): BlueprintExportPayload {
51
60
  return {
52
61
  kind: BLUEPRINT_EXPORT_KIND,
53
62
  version: BLUEPRINT_EXPORT_VERSION,
54
- name: typeof data.name === "string" ? data.name : "导入的蓝图",
63
+ name: typeof data.name === "string" ? data.name : importedBlueprintName(),
55
64
  remark: typeof data.remark === "string" ? data.remark : undefined,
56
65
  exportedAt: typeof data.exportedAt === "number" ? data.exportedAt : Date.now(),
57
66
  document: data.document,
@@ -81,7 +90,7 @@ export function buildLibraryRecord(args: {
81
90
  createdAt?: number;
82
91
  }): BlueprintLibraryRecord {
83
92
  const now = Date.now();
84
- const name = args.meta.name.trim() || "未命名蓝图";
93
+ const name = args.meta.name.trim() || unnamedBlueprintName();
85
94
  return {
86
95
  id: args.id ?? createLibraryBlueprintId(),
87
96
  name,
@@ -2,14 +2,20 @@ import {
2
2
  parseSwaggerDocument,
3
3
  type ParsedSwaggerDocument,
4
4
  } from "@arronqzy/blueprint-dsl";
5
+ import { resolveLocale, tForLocale } from "@arronqzy/i18n";
6
+
7
+ function t() {
8
+ return tForLocale(resolveLocale());
9
+ }
5
10
 
6
11
  export async function loadSwaggerDocument(
7
12
  docsUrl: string,
8
13
  signal?: AbortSignal
9
14
  ): Promise<ParsedSwaggerDocument> {
15
+ const translate = t();
10
16
  const trimmed = docsUrl.trim();
11
17
  if (!trimmed) {
12
- throw new Error("请输入 Swagger 文档 URL");
18
+ throw new Error(translate("blueprint.config.enterSwaggerUrl"));
13
19
  }
14
20
 
15
21
  if (signal?.aborted) {
@@ -28,7 +34,9 @@ export async function loadSwaggerDocument(
28
34
  throw new DOMException("Aborted", "AbortError");
29
35
  }
30
36
  throw new Error(
31
- error instanceof Error ? error.message : "无法请求 Swagger 文档"
37
+ error instanceof Error
38
+ ? error.message
39
+ : translate("blueprint.config.cannotFetchSwagger")
32
40
  );
33
41
  }
34
42
 
@@ -37,19 +45,21 @@ export async function loadSwaggerDocument(
37
45
  }
38
46
 
39
47
  if (!response.ok) {
40
- throw new Error(`Swagger 文档请求失败: HTTP ${response.status}`);
48
+ throw new Error(
49
+ translate("blueprint.config.swaggerHttpFailed", { status: response.status })
50
+ );
41
51
  }
42
52
 
43
53
  const contentType = response.headers.get("content-type") ?? "";
44
54
  if (contentType.includes("text/html")) {
45
- throw new Error("返回内容为 HTML,请填写 OpenAPI/Swagger 的 JSON 地址");
55
+ throw new Error(translate("blueprint.config.swaggerHtmlReturned"));
46
56
  }
47
57
 
48
58
  let spec: unknown;
49
59
  try {
50
60
  spec = await response.json();
51
61
  } catch {
52
- throw new Error("Swagger 文档不是有效的 JSON");
62
+ throw new Error(translate("blueprint.config.swaggerInvalidJson"));
53
63
  }
54
64
 
55
65
  return parseSwaggerDocument(spec, trimmed);
package/src/main.tsx CHANGED
@@ -1,6 +1,7 @@
1
1
  import { useState } from "react";
2
2
  import { createRoot } from "react-dom/client";
3
3
  import { ThemeProvider } from "@arronqzy/ui";
4
+ import { I18nProvider, useI18n } from "@arronqzy/i18n/react";
4
5
  import "@arronqzy/ui/styles.css";
5
6
  import "@xyflow/react/dist/style.css";
6
7
  import "./dev.css";
@@ -10,6 +11,7 @@ import { BluePrintReactRoot } from ".";
10
11
  import { createDevBlueprintGraph } from "./dev-seed";
11
12
 
12
13
  function DevApp() {
14
+ const { t } = useI18n();
13
15
  const [graph, setGraph] = useState(() => createDevBlueprintGraph());
14
16
  const [selectedNodeId, setSelectedNodeId] = useState<string | null>(null);
15
17
 
@@ -18,10 +20,13 @@ function DevApp() {
18
20
  <header className="bp-dev-toolbar">
19
21
  <strong>Blueprint Dev</strong>
20
22
  <span className="text-muted-foreground">
21
- 右键画布添加节点 · 从右侧圆点拖到左侧圆点连线 · 初始应看到 A→B 蓝线
23
+ {t("blueprint.dev.hint")}
22
24
  </span>
23
25
  <span className="text-muted-foreground">
24
- 节点 {graph.document.nodes.length} · 边 {graph.document.edges.length}
26
+ {t("blueprint.dev.stats", {
27
+ nodes: graph.document.nodes.length,
28
+ edges: graph.document.edges.length,
29
+ })}
25
30
  </span>
26
31
  </header>
27
32
  <div className="bp-dev-canvas">
@@ -37,7 +42,9 @@ function DevApp() {
37
42
  }
38
43
 
39
44
  createRoot(document.getElementById("app")!).render(
40
- <ThemeProvider defaultTheme="light" enableSystem={false}>
41
- <DevApp />
42
- </ThemeProvider>
45
+ <I18nProvider>
46
+ <ThemeProvider defaultTheme="light" enableSystem={false}>
47
+ <DevApp />
48
+ </ThemeProvider>
49
+ </I18nProvider>
43
50
  );
@@ -1,3 +1,4 @@
1
+ import { useI18n } from "@arronqzy/i18n/react";
1
2
  import { Handle, Position, type NodeProps } from "@xyflow/react";
2
3
  import { cn } from "@arronqzy/ui";
3
4
 
@@ -10,6 +11,7 @@ import { BlueprintNodeCard } from "./BlueprintNodeCard";
10
11
 
11
12
  /** 并运算节点:两路输入均为真时输出真信号;单端口多连线按「或」合并 */
12
13
  export function AndFlowNode({ id, data }: NodeProps) {
14
+ const { t } = useI18n();
13
15
  const nodeData = data as BlueprintFlowNodeData;
14
16
  const onSelect = useBlueprintNodeSelect();
15
17
  const executionTone = resolveBlueprintNodeExecutionTone(nodeData);
@@ -28,7 +30,7 @@ export function AndFlowNode({ id, data }: NodeProps) {
28
30
  id="inA"
29
31
  className="bp-flow-handle bp-flow-handle--target"
30
32
  style={{ top: "35%" }}
31
- title="输入 A(多连线为或)"
33
+ title={t("blueprint.node.andInA")}
32
34
  />
33
35
  <Handle
34
36
  type="target"
@@ -36,13 +38,13 @@ export function AndFlowNode({ id, data }: NodeProps) {
36
38
  id="inB"
37
39
  className="bp-flow-handle bp-flow-handle--target"
38
40
  style={{ top: "65%" }}
39
- title="输入 B(多连线为或)"
41
+ title={t("blueprint.node.andInB")}
40
42
  />
41
43
  <BlueprintNodeCard
42
44
  nodeId={id}
43
45
  label={nodeData.label}
44
- meta={resolveBlueprintNodeTypeLabel(nodeData)}
45
- subtitle={resolveBlueprintNodeSummary(nodeData)}
46
+ meta={resolveBlueprintNodeTypeLabel(nodeData, t)}
47
+ subtitle={resolveBlueprintNodeSummary(nodeData, t)}
46
48
  variant="and"
47
49
  selected={Boolean(nodeData.isSelected)}
48
50
  onSelect={onSelect}
@@ -52,7 +54,7 @@ export function AndFlowNode({ id, data }: NodeProps) {
52
54
  position={Position.Right}
53
55
  id="out"
54
56
  className="bp-flow-handle bp-flow-handle--source"
55
- title="两路均为真时输出真信号"
57
+ title={t("blueprint.node.andOut")}
56
58
  />
57
59
  </div>
58
60
  );
@@ -1,3 +1,4 @@
1
+ import { useI18n } from "@arronqzy/i18n/react";
1
2
  import type { NodeProps } from "@xyflow/react";
2
3
 
3
4
  import { resolveBlueprintNodeTypeLabel } from "../graph/document";
@@ -8,6 +9,7 @@ import type { BlueprintFlowNodeData } from "../types";
8
9
  import { BlueprintNodeShell } from "./BlueprintNodeShell";
9
10
 
10
11
  export function BlueprintFlowNode({ id, data }: NodeProps) {
12
+ const { t } = useI18n();
11
13
  const nodeData = data as BlueprintFlowNodeData;
12
14
  const onSelect = useBlueprintNodeSelect();
13
15
 
@@ -15,8 +17,8 @@ export function BlueprintFlowNode({ id, data }: NodeProps) {
15
17
  <BlueprintNodeShell
16
18
  nodeId={id}
17
19
  label={nodeData.label}
18
- meta={resolveBlueprintNodeTypeLabel(nodeData)}
19
- subtitle={resolveBlueprintNodeSummary(nodeData)}
20
+ meta={resolveBlueprintNodeTypeLabel(nodeData, t)}
21
+ subtitle={resolveBlueprintNodeSummary(nodeData, t)}
20
22
  variant="blueprint"
21
23
  selected={Boolean(nodeData.isSelected)}
22
24
  executionTone={resolveBlueprintNodeExecutionTone(nodeData)}
@@ -1,3 +1,4 @@
1
+ import { useI18n } from "@arronqzy/i18n/react";
1
2
  import { cn } from "@arronqzy/ui";
2
3
 
3
4
  export type BlueprintNodeCardProps = {
@@ -81,6 +82,7 @@ export function BlueprintNodeCard({
81
82
  hideLeadingDot = false,
82
83
  onSelect,
83
84
  }: BlueprintNodeCardProps) {
85
+ const { t } = useI18n();
84
86
  const v = variantStyle[variant];
85
87
 
86
88
  return (
@@ -101,7 +103,7 @@ export function BlueprintNodeCard({
101
103
  "bp-flow-drag-handle flex cursor-grab items-center gap-2 border-b border-border/50 px-2 py-1.5",
102
104
  "bg-muted/25 text-muted-foreground active:cursor-grabbing"
103
105
  )}
104
- title="拖拽移动"
106
+ title={t("blueprint.node.dragToMove")}
105
107
  >
106
108
  <NodeGrip />
107
109
  {meta ? (
@@ -148,7 +150,7 @@ export function BlueprintNodeCard({
148
150
  "shrink-0 rounded px-1.5 py-0.5 text-[10px] font-semibold leading-none",
149
151
  v.badge
150
152
  )}
151
- title="已发送信号次数"
153
+ title={t("blueprint.node.signalSentCount")}
152
154
  >
153
155
  {progressLabel}
154
156
  </span>
@@ -1,3 +1,4 @@
1
+ import { useI18n } from "@arronqzy/i18n/react";
1
2
  import { Handle, Position } from "@xyflow/react";
2
3
  import { cn } from "@arronqzy/ui";
3
4
 
@@ -17,6 +18,7 @@ export function BlueprintNodeShell({
17
18
  executionTone = null,
18
19
  ...cardProps
19
20
  }: BlueprintNodeShellProps) {
21
+ const { t } = useI18n();
20
22
  return (
21
23
  <div
22
24
  className={cn(
@@ -30,7 +32,7 @@ export function BlueprintNodeShell({
30
32
  position={Position.Left}
31
33
  id="in"
32
34
  className="bp-flow-handle bp-flow-handle--target"
33
- title="真/假信号输入"
35
+ title={t("blueprint.node.signalIn")}
34
36
  />
35
37
  <BlueprintNodeCard {...cardProps} selected={selected} />
36
38
  <Handle
@@ -38,7 +40,7 @@ export function BlueprintNodeShell({
38
40
  position={Position.Right}
39
41
  id="out"
40
42
  className="bp-flow-handle bp-flow-handle--source"
41
- title="真/假信号输出"
43
+ title={t("blueprint.node.signalOut")}
42
44
  />
43
45
  </div>
44
46
  );
@@ -1,3 +1,4 @@
1
+ import { useI18n } from "@arronqzy/i18n/react";
1
2
  import type { NodeProps } from "@xyflow/react";
2
3
  import { cn } from "@arronqzy/ui";
3
4
 
@@ -22,6 +23,7 @@ function ClockAbortIcon({ className }: { className?: string }) {
22
23
 
23
24
  /** 时钟节点:收到真信号后按配置间隔输出含当前时间的真信号 */
24
25
  export function ClockFlowNode({ id, data }: NodeProps) {
26
+ const { t } = useI18n();
25
27
  const nodeData = data as BlueprintFlowNodeData;
26
28
  const onSelect = useBlueprintNodeSelect();
27
29
  const onAbortClock = useBlueprintClockAbort();
@@ -36,8 +38,8 @@ export function ClockFlowNode({ id, data }: NodeProps) {
36
38
  <BlueprintNodeShell
37
39
  nodeId={id}
38
40
  label={nodeData.label}
39
- meta={resolveBlueprintNodeTypeLabel(nodeData)}
40
- subtitle={resolveBlueprintNodeSummary(nodeData)}
41
+ meta={resolveBlueprintNodeTypeLabel(nodeData, t)}
42
+ subtitle={resolveBlueprintNodeSummary(nodeData, t)}
41
43
  progressLabel={progressLabel}
42
44
  variant="clock"
43
45
  selected={Boolean(nodeData.isSelected)}
@@ -52,7 +54,7 @@ export function ClockFlowNode({ id, data }: NodeProps) {
52
54
  "rounded border border-rose-500/50 bg-rose-500/15 text-rose-600 shadow-sm",
53
55
  "hover:bg-rose-500/25 dark:text-rose-300"
54
56
  )}
55
- title="中止时钟:停止剩余次数与正在执行的下游任务"
57
+ title={t("blueprint.node.abortClock")}
56
58
  onPointerDown={(e) => e.stopPropagation()}
57
59
  onClick={(e) => {
58
60
  e.stopPropagation();
@@ -1,3 +1,4 @@
1
+ import { useI18n } from "@arronqzy/i18n/react";
1
2
  import type { NodeProps } from "@xyflow/react";
2
3
 
3
4
  import { resolveBlueprintNodeTypeLabel } from "../graph/document";
@@ -8,6 +9,7 @@ import type { BlueprintFlowNodeData } from "../types";
8
9
  import { BlueprintNodeShell } from "./BlueprintNodeShell";
9
10
 
10
11
  export function FetchFlowNode({ id, data }: NodeProps) {
12
+ const { t } = useI18n();
11
13
  const nodeData = data as BlueprintFlowNodeData;
12
14
  const onSelect = useBlueprintNodeSelect();
13
15
 
@@ -15,8 +17,8 @@ export function FetchFlowNode({ id, data }: NodeProps) {
15
17
  <BlueprintNodeShell
16
18
  nodeId={id}
17
19
  label={nodeData.label}
18
- meta={resolveBlueprintNodeTypeLabel(nodeData)}
19
- subtitle={resolveBlueprintNodeSummary(nodeData)}
20
+ meta={resolveBlueprintNodeTypeLabel(nodeData, t)}
21
+ subtitle={resolveBlueprintNodeSummary(nodeData, t)}
20
22
  variant="fetch"
21
23
  selected={Boolean(nodeData.isSelected)}
22
24
  executionTone={resolveBlueprintNodeExecutionTone(nodeData)}
@@ -1,3 +1,4 @@
1
+ import { useI18n } from "@arronqzy/i18n/react";
1
2
  import type { NodeProps } from "@xyflow/react";
2
3
 
3
4
  import { resolveBlueprintNodeTypeLabel } from "../graph/document";
@@ -8,6 +9,7 @@ import type { BlueprintFlowNodeData } from "../types";
8
9
  import { BlueprintNodeShell } from "./BlueprintNodeShell";
9
10
 
10
11
  export function JsonFlowNode({ id, data }: NodeProps) {
12
+ const { t } = useI18n();
11
13
  const nodeData = data as BlueprintFlowNodeData;
12
14
  const onSelect = useBlueprintNodeSelect();
13
15
 
@@ -15,8 +17,8 @@ export function JsonFlowNode({ id, data }: NodeProps) {
15
17
  <BlueprintNodeShell
16
18
  nodeId={id}
17
19
  label={nodeData.label}
18
- meta={resolveBlueprintNodeTypeLabel(nodeData)}
19
- subtitle={resolveBlueprintNodeSummary(nodeData)}
20
+ meta={resolveBlueprintNodeTypeLabel(nodeData, t)}
21
+ subtitle={resolveBlueprintNodeSummary(nodeData, t)}
20
22
  variant="json"
21
23
  selected={Boolean(nodeData.isSelected)}
22
24
  executionTone={resolveBlueprintNodeExecutionTone(nodeData)}
@@ -1,3 +1,4 @@
1
+ import { useI18n } from "@arronqzy/i18n/react";
1
2
  import { Handle, Position, type NodeProps } from "@xyflow/react";
2
3
  import { cn } from "@arronqzy/ui";
3
4
 
@@ -10,6 +11,7 @@ import { BlueprintNodeCard } from "./BlueprintNodeCard";
10
11
 
11
12
  /** 生命周期节点:无输入口,仅在对应生命周期触发时从右侧输出口发出信号 */
12
13
  export function LifecycleFlowNode({ id, data }: NodeProps) {
14
+ const { t } = useI18n();
13
15
  const nodeData = data as BlueprintFlowNodeData;
14
16
  const onSelect = useBlueprintNodeSelect();
15
17
  const executionTone = resolveBlueprintNodeExecutionTone(nodeData);
@@ -25,8 +27,8 @@ export function LifecycleFlowNode({ id, data }: NodeProps) {
25
27
  <BlueprintNodeCard
26
28
  nodeId={id}
27
29
  label={nodeData.label}
28
- meta={resolveBlueprintNodeTypeLabel(nodeData)}
29
- subtitle={resolveBlueprintNodeSummary(nodeData)}
30
+ meta={resolveBlueprintNodeTypeLabel(nodeData, t)}
31
+ subtitle={resolveBlueprintNodeSummary(nodeData, t)}
30
32
  variant="lifecycle"
31
33
  selected={Boolean(nodeData.isSelected)}
32
34
  hideLeadingDot
@@ -37,7 +39,7 @@ export function LifecycleFlowNode({ id, data }: NodeProps) {
37
39
  position={Position.Right}
38
40
  id="out"
39
41
  className="bp-flow-handle bp-flow-handle--source"
40
- title="生命周期满足时输出真/假信号"
42
+ title={t("blueprint.node.lifecycleOut")}
41
43
  />
42
44
  </div>
43
45
  );
@@ -1,3 +1,4 @@
1
+ import { useI18n } from "@arronqzy/i18n/react";
1
2
  import type { NodeProps } from "@xyflow/react";
2
3
 
3
4
  import { resolveBlueprintNodeTypeLabel } from "../graph/document";
@@ -8,6 +9,7 @@ import type { BlueprintFlowNodeData } from "../types";
8
9
  import { BlueprintNodeShell } from "./BlueprintNodeShell";
9
10
 
10
11
  export function LogicFlowNode({ id, data }: NodeProps) {
12
+ const { t } = useI18n();
11
13
  const nodeData = data as BlueprintFlowNodeData;
12
14
  const onSelect = useBlueprintNodeSelect();
13
15
 
@@ -15,8 +17,8 @@ export function LogicFlowNode({ id, data }: NodeProps) {
15
17
  <BlueprintNodeShell
16
18
  nodeId={id}
17
19
  label={nodeData.label}
18
- meta={resolveBlueprintNodeTypeLabel(nodeData)}
19
- subtitle={resolveBlueprintNodeSummary(nodeData)}
20
+ meta={resolveBlueprintNodeTypeLabel(nodeData, t)}
21
+ subtitle={resolveBlueprintNodeSummary(nodeData, t)}
20
22
  variant="logic"
21
23
  selected={Boolean(nodeData.isSelected)}
22
24
  executionTone={resolveBlueprintNodeExecutionTone(nodeData)}
@@ -1,10 +1,11 @@
1
1
  import {
2
- VIEW_NODE_TYPE,
2
+ FETCH_NODE_TYPE,
3
3
  type RunnableGraph,
4
4
  } from "@arronqzy/blueprint-dsl";
5
5
 
6
6
  import {
7
- resolveBlueprintConfigSource,
7
+ resolveNodeFetchConfig,
8
+ resolveRunnableNodeType,
8
9
  resolveViewElementIds,
9
10
  type BlueprintDocument,
10
11
  } from "../graph/document";
@@ -17,17 +18,19 @@ export function documentToRunnableGraph(
17
18
  ): RunnableGraph {
18
19
  return {
19
20
  nodes: document.nodes.map((node) => {
20
- const configSource = resolveBlueprintConfigSource(node);
21
21
  const viewElementIds = resolveViewElementIds(node);
22
+ const nodeType = resolveRunnableNodeType(node);
22
23
  return {
23
24
  id: node.id,
24
- nodeType:
25
- configSource === "view" ? VIEW_NODE_TYPE : node.nodeType,
25
+ nodeType,
26
26
  lifecyclePhase: node.lifecyclePhase,
27
27
  libraryBlueprintId: node.libraryBlueprintId,
28
28
  viewElementIds:
29
29
  viewElementIds.length > 0 ? viewElementIds : undefined,
30
- fetchConfig: node.fetchConfig,
30
+ fetchConfig:
31
+ nodeType === FETCH_NODE_TYPE
32
+ ? resolveNodeFetchConfig(node)
33
+ : node.fetchConfig,
31
34
  jsonConfig: node.jsonConfig,
32
35
  logicConfig: node.logicConfig,
33
36
  clockConfig: node.clockConfig,