@arronqzy/react-blueprint 1.0.0

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 (124) hide show
  1. package/.eslintrc.cjs +5 -0
  2. package/.pnpm-cache/metadata/registry.npmjs.org/@esbuild/aix-ppc64.json +1 -0
  3. package/.pnpm-cache/metadata/registry.npmjs.org/@esbuild/android-arm.json +1 -0
  4. package/.pnpm-cache/metadata/registry.npmjs.org/@esbuild/android-arm64.json +1 -0
  5. package/.pnpm-cache/metadata/registry.npmjs.org/@esbuild/android-x64.json +1 -0
  6. package/.pnpm-cache/metadata/registry.npmjs.org/@esbuild/darwin-arm64.json +1 -0
  7. package/.pnpm-cache/metadata/registry.npmjs.org/@esbuild/darwin-x64.json +1 -0
  8. package/.pnpm-cache/metadata/registry.npmjs.org/@esbuild/freebsd-arm64.json +1 -0
  9. package/.pnpm-cache/metadata/registry.npmjs.org/@esbuild/freebsd-x64.json +1 -0
  10. package/.pnpm-cache/metadata/registry.npmjs.org/@esbuild/linux-arm.json +1 -0
  11. package/.pnpm-cache/metadata/registry.npmjs.org/@esbuild/linux-arm64.json +1 -0
  12. package/.pnpm-cache/metadata/registry.npmjs.org/@esbuild/linux-ia32.json +1 -0
  13. package/.pnpm-cache/metadata/registry.npmjs.org/@esbuild/linux-loong64.json +1 -0
  14. package/.pnpm-cache/metadata/registry.npmjs.org/@esbuild/linux-mips64el.json +1 -0
  15. package/.pnpm-cache/metadata/registry.npmjs.org/@esbuild/linux-ppc64.json +1 -0
  16. package/.pnpm-cache/metadata/registry.npmjs.org/@esbuild/linux-riscv64.json +1 -0
  17. package/.pnpm-cache/metadata/registry.npmjs.org/@esbuild/linux-s390x.json +1 -0
  18. package/.pnpm-cache/metadata/registry.npmjs.org/@esbuild/linux-x64.json +1 -0
  19. package/.pnpm-cache/metadata/registry.npmjs.org/@esbuild/netbsd-x64.json +1 -0
  20. package/.pnpm-cache/metadata/registry.npmjs.org/@esbuild/openbsd-x64.json +1 -0
  21. package/.pnpm-cache/metadata/registry.npmjs.org/@esbuild/sunos-x64.json +1 -0
  22. package/.pnpm-cache/metadata/registry.npmjs.org/@esbuild/win32-arm64.json +1 -0
  23. package/.pnpm-cache/metadata/registry.npmjs.org/@esbuild/win32-ia32.json +1 -0
  24. package/.pnpm-cache/metadata/registry.npmjs.org/@esbuild/win32-x64.json +1 -0
  25. package/.pnpm-cache/metadata/registry.npmjs.org/@rollup/rollup-android-arm-eabi.json +1 -0
  26. package/.pnpm-cache/metadata/registry.npmjs.org/@rollup/rollup-android-arm64.json +1 -0
  27. package/.pnpm-cache/metadata/registry.npmjs.org/@rollup/rollup-darwin-arm64.json +1 -0
  28. package/.pnpm-cache/metadata/registry.npmjs.org/@rollup/rollup-darwin-x64.json +1 -0
  29. package/.pnpm-cache/metadata/registry.npmjs.org/@rollup/rollup-freebsd-arm64.json +1 -0
  30. package/.pnpm-cache/metadata/registry.npmjs.org/@rollup/rollup-freebsd-x64.json +1 -0
  31. package/.pnpm-cache/metadata/registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf.json +1 -0
  32. package/.pnpm-cache/metadata/registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf.json +1 -0
  33. package/.pnpm-cache/metadata/registry.npmjs.org/@rollup/rollup-linux-arm64-gnu.json +1 -0
  34. package/.pnpm-cache/metadata/registry.npmjs.org/@rollup/rollup-linux-arm64-musl.json +1 -0
  35. package/.pnpm-cache/metadata/registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu.json +1 -0
  36. package/.pnpm-cache/metadata/registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu.json +1 -0
  37. package/.pnpm-cache/metadata/registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu.json +1 -0
  38. package/.pnpm-cache/metadata/registry.npmjs.org/@rollup/rollup-linux-riscv64-musl.json +1 -0
  39. package/.pnpm-cache/metadata/registry.npmjs.org/@rollup/rollup-linux-s390x-gnu.json +1 -0
  40. package/.pnpm-cache/metadata/registry.npmjs.org/@rollup/rollup-linux-x64-gnu.json +1 -0
  41. package/.pnpm-cache/metadata/registry.npmjs.org/@rollup/rollup-linux-x64-musl.json +1 -0
  42. package/.pnpm-cache/metadata/registry.npmjs.org/@rollup/rollup-win32-arm64-msvc.json +1 -0
  43. package/.pnpm-cache/metadata/registry.npmjs.org/@rollup/rollup-win32-ia32-msvc.json +1 -0
  44. package/.pnpm-cache/metadata/registry.npmjs.org/@rollup/rollup-win32-x64-msvc.json +1 -0
  45. package/.pnpm-cache/metadata/registry.npmjs.org/eslint.json +1 -0
  46. package/.pnpm-cache/metadata/registry.npmjs.org/fsevents.json +1 -0
  47. package/.pnpm-cache/metadata/registry.npmjs.org/react-dom.json +1 -0
  48. package/.pnpm-cache/metadata/registry.npmjs.org/react-is.json +1 -0
  49. package/.pnpm-cache/metadata/registry.npmjs.org/turbo-darwin-64.json +1 -0
  50. package/.pnpm-cache/metadata/registry.npmjs.org/turbo-darwin-arm64.json +1 -0
  51. package/.pnpm-cache/metadata/registry.npmjs.org/turbo-linux-64.json +1 -0
  52. package/.pnpm-cache/metadata/registry.npmjs.org/turbo-linux-arm64.json +1 -0
  53. package/.pnpm-cache/metadata/registry.npmjs.org/turbo-windows-64.json +1 -0
  54. package/.pnpm-cache/metadata/registry.npmjs.org/turbo-windows-arm64.json +1 -0
  55. package/.pnpm-cache/metadata/registry.npmjs.org/typescript.json +1 -0
  56. package/.turbo/turbo-lint.log +4 -0
  57. package/dist/assets/index-CneNgzWb.css +1 -0
  58. package/dist/assets/index-DsVhHV0-.js +226 -0
  59. package/dist/index.html +14 -0
  60. package/index.html +13 -0
  61. package/index.ts +70 -0
  62. package/package.json +43 -0
  63. package/postcss.config.cjs +6 -0
  64. package/src/BlueprintCanvasContext.tsx +61 -0
  65. package/src/BlueprintContextMenu.tsx +108 -0
  66. package/src/BlueprintNodeConfigSidebar.tsx +358 -0
  67. package/src/blueprint.css +321 -0
  68. package/src/blueprintNodeTypes.ts +20 -0
  69. package/src/components/BlueprintExecutionLogPanel.tsx +427 -0
  70. package/src/components/BlueprintLibrarySelect.tsx +68 -0
  71. package/src/components/BlueprintMetaDialog.tsx +96 -0
  72. package/src/components/BlueprintNodeSwitchTaskDialog.tsx +58 -0
  73. package/src/components/BlueprintPanelToolbar.tsx +664 -0
  74. package/src/components/ClockNodeConfigPanel.tsx +139 -0
  75. package/src/components/FetchNodeConfigPanel.tsx +716 -0
  76. package/src/components/FetchUrlAutocomplete.tsx +190 -0
  77. package/src/components/JsonNodeConfigPanel.tsx +79 -0
  78. package/src/components/LogicNodeConfigPanel.tsx +80 -0
  79. package/src/components/ViewElementMultiSelect.tsx +140 -0
  80. package/src/createBlueprintEdgeTypes.tsx +10 -0
  81. package/src/dev-seed.ts +34 -0
  82. package/src/dev.css +38 -0
  83. package/src/edges/BlueprintSmoothEdge.tsx +35 -0
  84. package/src/fetch-config-task-store.ts +197 -0
  85. package/src/flowCoordinates.ts +19 -0
  86. package/src/flowDefaults.ts +9 -0
  87. package/src/graph/blueprint-graph.ts +261 -0
  88. package/src/graph/document.ts +384 -0
  89. package/src/graph/index.ts +7 -0
  90. package/src/graph/node-summary.ts +88 -0
  91. package/src/graph/node-types.ts +9 -0
  92. package/src/graph/react-flow-adapter.ts +127 -0
  93. package/src/graph/sync-edges.ts +69 -0
  94. package/src/graph/sync-nodes.ts +104 -0
  95. package/src/hooks/useBlueprintDebugSession.ts +515 -0
  96. package/src/hooks/useBlueprintFlowColorMode.ts +8 -0
  97. package/src/hooks/useBlueprintFlowState.ts +486 -0
  98. package/src/hooks/useBlueprintFlowViewport.ts +54 -0
  99. package/src/hooks/useBlueprintNodeSelectionGuard.ts +63 -0
  100. package/src/hooks/useBlueprintPageLifecycle.ts +245 -0
  101. package/src/index.tsx +271 -0
  102. package/src/library/blueprint-io.ts +108 -0
  103. package/src/library/blueprint-library-db.ts +112 -0
  104. package/src/library/execution-log-db.ts +171 -0
  105. package/src/library/execution-log-settings.ts +50 -0
  106. package/src/library/swagger-docs.ts +56 -0
  107. package/src/library/types.ts +35 -0
  108. package/src/main.tsx +43 -0
  109. package/src/nodes/AndFlowNode.tsx +59 -0
  110. package/src/nodes/BlueprintFlowNode.tsx +26 -0
  111. package/src/nodes/BlueprintNodeCard.tsx +169 -0
  112. package/src/nodes/BlueprintNodeShell.tsx +45 -0
  113. package/src/nodes/ClockFlowNode.tsx +67 -0
  114. package/src/nodes/FetchFlowNode.tsx +26 -0
  115. package/src/nodes/JsonFlowNode.tsx +26 -0
  116. package/src/nodes/LifecycleFlowNode.tsx +44 -0
  117. package/src/nodes/LogicFlowNode.tsx +26 -0
  118. package/src/runtime/document-to-runnable-graph.ts +48 -0
  119. package/src/runtime/execution-overlay.ts +169 -0
  120. package/src/types.ts +1 -0
  121. package/src/vite-env.d.ts +1 -0
  122. package/tailwind.config.ts +6 -0
  123. package/tsconfig.json +12 -0
  124. package/vite.config.ts +16 -0
@@ -0,0 +1,384 @@
1
+ import type {
2
+ ClockNodeConfig,
3
+ FetchRequestConfig,
4
+ JsonNodeConfig,
5
+ LogicNodeConfig,
6
+ PageLifecyclePhase,
7
+ } from "@arronqzy/blueprint-dsl";
8
+ import {
9
+ BLUEPRINT_NODE_TYPE,
10
+ CLOCK_NODE_TYPE,
11
+ AND_NODE_TYPE,
12
+ DEFAULT_CLOCK_NODE_CONFIG,
13
+ DEFAULT_FETCH_REQUEST_CONFIG,
14
+ DEFAULT_JSON_NODE_CONFIG,
15
+ DEFAULT_LOGIC_NODE_CONFIG,
16
+ DEFAULT_LOGIC_NODE_TYPE,
17
+ FETCH_NODE_TYPE,
18
+ JSON_NODE_TYPE,
19
+ LIFECYCLE_NODE_TYPE,
20
+ normalizeClockConfig,
21
+ VIEW_NODE_TYPE,
22
+ } from "@arronqzy/blueprint-dsl";
23
+
24
+ export type { FetchRequestConfig as BlueprintFetchConfig } from "@arronqzy/blueprint-dsl";
25
+ export type { JsonNodeConfig as BlueprintJsonConfig } from "@arronqzy/blueprint-dsl";
26
+ export type { LogicNodeConfig as BlueprintLogicConfig } from "@arronqzy/blueprint-dsl";
27
+ export type { ClockNodeConfig as BlueprintClockConfig } from "@arronqzy/blueprint-dsl";
28
+
29
+ export type BlueprintNodeRole = "blueprint" | "logic" | "and" | "lifecycle" | "fetch" | "json" | "clock";
30
+
31
+ /** 决定右侧配置面板展示哪类配置 */
32
+ export type BlueprintConfigSource =
33
+ | "blueprint"
34
+ | "logic"
35
+ | "and"
36
+ | "lifecycle"
37
+ | "view"
38
+ | "fetch"
39
+ | "json"
40
+ | "clock";
41
+
42
+ export type BlueprintGraphNode = {
43
+ id: string;
44
+ role: BlueprintNodeRole;
45
+ /** 对应 @arronqzy/blueprint-dsl NodeDefinition.type */
46
+ nodeType: string;
47
+ label: string;
48
+ position: { x: number; y: number };
49
+ /** 配置面板类型;默认由 role 推断 */
50
+ configSource?: BlueprintConfigSource;
51
+ /** @deprecated 请使用 viewElementIds;读取时由 resolveViewElementIds 兼容 */
52
+ viewElementId?: string;
53
+ /** 关联的视图画布节点 id 列表,配置类型为 view 时可一次绑定多个 */
54
+ viewElementIds?: string[];
55
+ /** logic 节点所属的蓝图节点 */
56
+ parentId?: string;
57
+ /** blueprint 节点嵌套子蓝图 id(内部标识,保留兼容) */
58
+ nestedBlueprintId?: string;
59
+ /** 引用的蓝图库记录 id,执行时会等待该蓝图完成后继续 */
60
+ libraryBlueprintId?: string;
61
+ /** lifecycle 节点监听的生命周期阶段 */
62
+ lifecyclePhase?: PageLifecyclePhase;
63
+ /** 数据源获取节点的 fetch 配置 */
64
+ fetchConfig?: FetchRequestConfig;
65
+ /** JSON 节点的 JSON 字符串配置 */
66
+ jsonConfig?: JsonNodeConfig;
67
+ /** 逻辑节点的 JavaScript 脚本配置 */
68
+ logicConfig?: LogicNodeConfig;
69
+ /** 时钟节点的间隔与时间格式配置 */
70
+ clockConfig?: ClockNodeConfig;
71
+ };
72
+
73
+ export function resolveViewElementIds(
74
+ node: Pick<BlueprintGraphNode, "viewElementId" | "viewElementIds">
75
+ ): string[] {
76
+ if (node.viewElementIds?.length) return [...node.viewElementIds];
77
+ if (node.viewElementId) return [node.viewElementId];
78
+ return [];
79
+ }
80
+
81
+ /** 仅保留仍存在于视图画布中的关联 id */
82
+ export function pruneViewElementIds(
83
+ ids: readonly string[],
84
+ existingViewElementIds: ReadonlySet<string>
85
+ ): string[] {
86
+ return ids.filter((id) => existingViewElementIds.has(id));
87
+ }
88
+
89
+ export function resolveBlueprintConfigSource(
90
+ node: Pick<
91
+ BlueprintGraphNode,
92
+ "role" | "configSource" | "viewElementId" | "viewElementIds"
93
+ >
94
+ ): BlueprintConfigSource {
95
+ if (node.configSource) return node.configSource;
96
+ if (resolveViewElementIds(node).length > 0) return "view";
97
+ if (node.role === "lifecycle") return "lifecycle";
98
+ if (node.role === "clock") return "clock";
99
+ if (node.role === "and") return "and";
100
+ if (node.role === "fetch") return "fetch";
101
+ if (node.role === "json") return "json";
102
+ return node.role === "logic" ? "logic" : "blueprint";
103
+ }
104
+
105
+ /** 画布节点顶部(拖拽区)展示的配置类型文案 */
106
+ export const BLUEPRINT_CONFIG_TYPE_LABELS: Record<
107
+ BlueprintConfigSource,
108
+ string
109
+ > = {
110
+ blueprint: "蓝图",
111
+ logic: "逻辑",
112
+ and: "并运算",
113
+ lifecycle: "生命周期",
114
+ view: "视图",
115
+ fetch: "数据源获取",
116
+ json: "JSON 节点",
117
+ clock: "时钟",
118
+ };
119
+
120
+ export function resolveBlueprintNodeTypeLabel(
121
+ node: Pick<
122
+ BlueprintGraphNode,
123
+ "role" | "configSource" | "viewElementId" | "viewElementIds"
124
+ >
125
+ ): string {
126
+ return BLUEPRINT_CONFIG_TYPE_LABELS[resolveBlueprintConfigSource(node)];
127
+ }
128
+
129
+ /** 新建节点时的默认显示名称(与类型文案一致) */
130
+ export function defaultBlueprintNodeLabel(
131
+ configSource: BlueprintConfigSource
132
+ ): string {
133
+ return BLUEPRINT_CONFIG_TYPE_LABELS[configSource];
134
+ }
135
+
136
+ export function resolveNodeFetchConfig(
137
+ node: Pick<BlueprintGraphNode, "fetchConfig">
138
+ ): FetchRequestConfig {
139
+ return {
140
+ ...DEFAULT_FETCH_REQUEST_CONFIG,
141
+ ...node.fetchConfig,
142
+ headers: {
143
+ ...DEFAULT_FETCH_REQUEST_CONFIG.headers,
144
+ ...node.fetchConfig?.headers,
145
+ },
146
+ swaggerEndpoints: node.fetchConfig?.swaggerEndpoints,
147
+ };
148
+ }
149
+
150
+ export function resolveNodeJsonConfig(
151
+ node: Pick<BlueprintGraphNode, "jsonConfig">
152
+ ): JsonNodeConfig {
153
+ return {
154
+ ...DEFAULT_JSON_NODE_CONFIG,
155
+ ...node.jsonConfig,
156
+ };
157
+ }
158
+
159
+ export function resolveNodeLogicConfig(
160
+ node: Pick<BlueprintGraphNode, "logicConfig">
161
+ ): LogicNodeConfig {
162
+ return {
163
+ ...DEFAULT_LOGIC_NODE_CONFIG,
164
+ ...node.logicConfig,
165
+ };
166
+ }
167
+
168
+ export function resolveNodeClockConfig(
169
+ node: Pick<BlueprintGraphNode, "clockConfig">
170
+ ): ClockNodeConfig {
171
+ return normalizeClockConfig({
172
+ ...DEFAULT_CLOCK_NODE_CONFIG,
173
+ ...node.clockConfig,
174
+ });
175
+ }
176
+
177
+ export type BlueprintGraphEdge = {
178
+ id: string;
179
+ source: string;
180
+ target: string;
181
+ sourceHandle?: string;
182
+ targetHandle?: string;
183
+ };
184
+
185
+ export type BlueprintDocument = {
186
+ id: string;
187
+ name?: string;
188
+ nodes: BlueprintGraphNode[];
189
+ edges: BlueprintGraphEdge[];
190
+ /** 为 true 时,假信号不阻塞任务链,会继续向下游传递 */
191
+ allowFalseSignalPropagation?: boolean;
192
+ };
193
+
194
+ export function createBlueprintDocument(id = "default"): BlueprintDocument {
195
+ return { id, nodes: [], edges: [] };
196
+ }
197
+
198
+ export function createNodeId(prefix = "node"): string {
199
+ return `${prefix}_${Date.now()}_${Math.random().toString(36).slice(2, 9)}`;
200
+ }
201
+
202
+ const noInputTargetNodeIds = (document: BlueprintDocument) =>
203
+ new Set(
204
+ document.nodes
205
+ .filter((node) => node.role === "lifecycle")
206
+ .map((node) => node.id)
207
+ );
208
+
209
+ /** 生命周期节点无输入口:移除连入边,并修正节点配置 */
210
+ export function sanitizeBlueprintDocument(
211
+ document: BlueprintDocument
212
+ ): BlueprintDocument {
213
+ const blockedTargets = noInputTargetNodeIds(document);
214
+
215
+ const nodes = document.nodes.map((node) => {
216
+ let next = node;
217
+ if (node.viewElementId && !node.viewElementIds?.length) {
218
+ next = {
219
+ ...next,
220
+ viewElementIds: [node.viewElementId],
221
+ viewElementId: undefined,
222
+ };
223
+ }
224
+ if (
225
+ resolveBlueprintConfigSource(next) === "view" &&
226
+ next.nodeType !== VIEW_NODE_TYPE
227
+ ) {
228
+ next = { ...next, nodeType: VIEW_NODE_TYPE };
229
+ }
230
+ if (next.role === "lifecycle") {
231
+ return { ...next, configSource: "lifecycle" as const };
232
+ }
233
+ if (next.role === "clock") {
234
+ return { ...next, configSource: "clock" as const };
235
+ }
236
+ if (next.configSource === "lifecycle" || next.configSource === "clock") {
237
+ return { ...next, configSource: undefined };
238
+ }
239
+ return next;
240
+ });
241
+
242
+ const edges = document.edges.filter((edge) => !blockedTargets.has(edge.target));
243
+
244
+ return { ...document, nodes, edges };
245
+ }
246
+
247
+ export function filterInvalidBlueprintEdges(
248
+ document: BlueprintDocument,
249
+ edges: BlueprintGraphEdge[]
250
+ ): BlueprintGraphEdge[] {
251
+ const blockedTargets = noInputTargetNodeIds(document);
252
+ return edges.filter((edge) => !blockedTargets.has(edge.target));
253
+ }
254
+
255
+ /** 切换配置类型时同步节点 role / nodeType,确保生命周期节点渲染为无输入口 */
256
+ export function patchNodeConfigSource(
257
+ node: BlueprintGraphNode,
258
+ configSource: BlueprintConfigSource
259
+ ): Partial<BlueprintGraphNode> {
260
+ if (configSource === "lifecycle") {
261
+ const phase = node.lifecyclePhase ?? "mounted";
262
+ return {
263
+ role: "lifecycle",
264
+ nodeType: LIFECYCLE_NODE_TYPE,
265
+ configSource: "lifecycle",
266
+ lifecyclePhase: phase,
267
+ viewElementId: undefined,
268
+ viewElementIds: undefined,
269
+ libraryBlueprintId: undefined,
270
+ fetchConfig: undefined,
271
+ jsonConfig: undefined,
272
+ logicConfig: undefined,
273
+ clockConfig: undefined,
274
+ };
275
+ }
276
+
277
+ if (configSource === "clock") {
278
+ return {
279
+ role: "clock",
280
+ nodeType: CLOCK_NODE_TYPE,
281
+ configSource: "clock",
282
+ clockConfig: resolveNodeClockConfig(node),
283
+ lifecyclePhase: undefined,
284
+ viewElementId: undefined,
285
+ viewElementIds: undefined,
286
+ libraryBlueprintId: undefined,
287
+ fetchConfig: undefined,
288
+ jsonConfig: undefined,
289
+ logicConfig: undefined,
290
+ };
291
+ }
292
+
293
+ if (configSource === "fetch") {
294
+ return {
295
+ role: "fetch",
296
+ nodeType: FETCH_NODE_TYPE,
297
+ configSource: "fetch",
298
+ lifecyclePhase: undefined,
299
+ viewElementId: undefined,
300
+ viewElementIds: undefined,
301
+ libraryBlueprintId: undefined,
302
+ fetchConfig: resolveNodeFetchConfig(node),
303
+ jsonConfig: undefined,
304
+ logicConfig: undefined,
305
+ clockConfig: undefined,
306
+ };
307
+ }
308
+
309
+ if (configSource === "json") {
310
+ return {
311
+ role: "json",
312
+ nodeType: JSON_NODE_TYPE,
313
+ configSource: "json",
314
+ lifecyclePhase: undefined,
315
+ viewElementId: undefined,
316
+ viewElementIds: undefined,
317
+ libraryBlueprintId: undefined,
318
+ fetchConfig: undefined,
319
+ jsonConfig: resolveNodeJsonConfig(node),
320
+ logicConfig: undefined,
321
+ clockConfig: undefined,
322
+ };
323
+ }
324
+
325
+ if (configSource === "blueprint") {
326
+ return {
327
+ role: "blueprint",
328
+ nodeType: BLUEPRINT_NODE_TYPE,
329
+ configSource: "blueprint",
330
+ lifecyclePhase: undefined,
331
+ nestedBlueprintId: node.nestedBlueprintId ?? createNodeId("nested_bp"),
332
+ viewElementId: undefined,
333
+ viewElementIds: undefined,
334
+ fetchConfig: undefined,
335
+ jsonConfig: undefined,
336
+ logicConfig: undefined,
337
+ clockConfig: undefined,
338
+ };
339
+ }
340
+
341
+ if (configSource === "logic") {
342
+ return {
343
+ role: "logic",
344
+ nodeType: DEFAULT_LOGIC_NODE_TYPE,
345
+ configSource: "logic",
346
+ lifecyclePhase: undefined,
347
+ viewElementId: undefined,
348
+ viewElementIds: undefined,
349
+ libraryBlueprintId: undefined,
350
+ fetchConfig: undefined,
351
+ jsonConfig: undefined,
352
+ logicConfig: resolveNodeLogicConfig(node),
353
+ clockConfig: undefined,
354
+ };
355
+ }
356
+
357
+ if (configSource === "and") {
358
+ return {
359
+ role: "and",
360
+ nodeType: AND_NODE_TYPE,
361
+ configSource: "and",
362
+ lifecyclePhase: undefined,
363
+ viewElementId: undefined,
364
+ viewElementIds: undefined,
365
+ libraryBlueprintId: undefined,
366
+ fetchConfig: undefined,
367
+ jsonConfig: undefined,
368
+ logicConfig: undefined,
369
+ clockConfig: undefined,
370
+ };
371
+ }
372
+
373
+ return {
374
+ role: "blueprint",
375
+ nodeType: VIEW_NODE_TYPE,
376
+ configSource: "view",
377
+ lifecyclePhase: undefined,
378
+ libraryBlueprintId: undefined,
379
+ fetchConfig: undefined,
380
+ jsonConfig: undefined,
381
+ logicConfig: undefined,
382
+ clockConfig: undefined,
383
+ };
384
+ }
@@ -0,0 +1,7 @@
1
+ export * from "./document";
2
+ export * from "./node-types";
3
+ export * from "./blueprint-graph";
4
+ export * from "./react-flow-adapter";
5
+ export * from "./sync-edges";
6
+ export * from "./sync-nodes";
7
+ export * from "./node-summary";
@@ -0,0 +1,88 @@
1
+ import {
2
+ PAGE_LIFECYCLE_LABELS,
3
+ resolveFetchRequestUrl,
4
+ } from "@arronqzy/blueprint-dsl";
5
+
6
+ import {
7
+ resolveBlueprintConfigSource,
8
+ resolveNodeClockConfig,
9
+ resolveNodeFetchConfig,
10
+ resolveNodeJsonConfig,
11
+ resolveNodeLogicConfig,
12
+ resolveViewElementIds,
13
+ } from "./document";
14
+ import type { BlueprintFlowNodeData } from "./react-flow-adapter";
15
+
16
+ function truncate(text: string, max: number): string {
17
+ const trimmed = text.trim();
18
+ if (trimmed.length <= max) return trimmed;
19
+ return `${trimmed.slice(0, Math.max(0, max - 1))}…`;
20
+ }
21
+
22
+ /** 画布节点正文区展示的配置摘要 */
23
+ export function resolveBlueprintNodeSummary(
24
+ data: BlueprintFlowNodeData
25
+ ): string | undefined {
26
+ const configSource = resolveBlueprintConfigSource(data);
27
+
28
+ switch (configSource) {
29
+ case "fetch": {
30
+ const config = resolveNodeFetchConfig(data);
31
+ if (!config.url?.trim()) return "未配置 URL";
32
+ let url = config.url.trim();
33
+ try {
34
+ url = resolveFetchRequestUrl(config);
35
+ } catch {
36
+ /* 保留原始输入 */
37
+ }
38
+ const method = config.method ?? "GET";
39
+ return truncate(`${method} ${url}`, 44);
40
+ }
41
+ case "clock": {
42
+ const clock = resolveNodeClockConfig(data);
43
+ const parts = [
44
+ `${clock.outputCount} 次`,
45
+ clock.intervalSeconds > 0 ? `间隔 ${clock.intervalSeconds}s` : null,
46
+ clock.emitImmediately ? "立即首发" : "延迟首发",
47
+ ].filter(Boolean);
48
+ return parts.join(" · ");
49
+ }
50
+ case "json": {
51
+ const json = resolveNodeJsonConfig(data).jsonString?.trim();
52
+ if (!json) return "空 JSON";
53
+ return truncate(json.replace(/\s+/g, " "), 44);
54
+ }
55
+ case "logic": {
56
+ const code = resolveNodeLogicConfig(data).sourceCode?.trim();
57
+ if (!code) return "未配置脚本";
58
+ const firstLine =
59
+ code.split("\n").find((line) => line.trim())?.trim() ?? code;
60
+ return truncate(firstLine, 44);
61
+ }
62
+ case "blueprint": {
63
+ if (data.libraryBlueprintLabel) {
64
+ return truncate(data.libraryBlueprintLabel, 44);
65
+ }
66
+ if (data.libraryBlueprintId) {
67
+ return truncate(data.libraryBlueprintId, 44);
68
+ }
69
+ return "未选择蓝图";
70
+ }
71
+ case "lifecycle": {
72
+ const phase = data.lifecyclePhase ?? "mounted";
73
+ return PAGE_LIFECYCLE_LABELS[phase] ?? phase;
74
+ }
75
+ case "view": {
76
+ const ids = resolveViewElementIds(data);
77
+ if (ids.length === 0) return "未关联视图节点";
78
+ if (ids.length === 1) {
79
+ return truncate(`视图节点 ${ids[0]}`, 44);
80
+ }
81
+ return `已关联 ${ids.length} 个视图节点`;
82
+ }
83
+ case "and":
84
+ return "两路输入均为真时输出";
85
+ default:
86
+ return undefined;
87
+ }
88
+ }
@@ -0,0 +1,9 @@
1
+ /** 与 @arronqzy/blueprint-dsl 中 NodeDefinition.type 对齐,供执行层解析 */
2
+ export {
3
+ BLUEPRINT_NODE_TYPE,
4
+ DEFAULT_LOGIC_NODE_TYPE,
5
+ LIFECYCLE_NODE_TYPE,
6
+ JSON_NODE_TYPE,
7
+ CLOCK_NODE_TYPE,
8
+ AND_NODE_TYPE,
9
+ } from "@arronqzy/blueprint-dsl";
@@ -0,0 +1,127 @@
1
+ import type {
2
+ BlueprintDocument,
3
+ BlueprintGraphEdge,
4
+ BlueprintGraphNode,
5
+ } from "./document";
6
+ import { createNodeId } from "./document";
7
+
8
+ export type BlueprintFlowNodeData = {
9
+ label: string;
10
+ role: BlueprintGraphNode["role"];
11
+ nodeType: string;
12
+ configSource?: BlueprintGraphNode["configSource"];
13
+ viewElementId?: string;
14
+ viewElementIds?: string[];
15
+ parentId?: string;
16
+ nestedBlueprintId?: string;
17
+ libraryBlueprintId?: string;
18
+ libraryBlueprintLabel?: string;
19
+ lifecyclePhase?: BlueprintGraphNode["lifecyclePhase"];
20
+ fetchConfig?: BlueprintGraphNode["fetchConfig"];
21
+ jsonConfig?: BlueprintGraphNode["jsonConfig"];
22
+ clockConfig?: BlueprintGraphNode["clockConfig"];
23
+ logicConfig?: BlueprintGraphNode["logicConfig"];
24
+ /** 调试执行时时钟节点已发送次数 */
25
+ clockEmitProgress?: { current: number; total: number } | null;
26
+ /** 由画布注入,勿写入图数据 */
27
+ isSelected?: boolean;
28
+ /** 调试执行时高亮当前执行到的节点 */
29
+ isExecutionActive?: boolean;
30
+ /** 调试执行时当前节点主输出信号类型 */
31
+ executionSignalKind?: "true" | "false" | null;
32
+ };
33
+
34
+ /** 与 BlueprintNodeCard 固定宽度一致,供 RF 在 measured 前计算连线路径 */
35
+ export const BP_FLOW_NODE_WIDTH = 168;
36
+ export const BP_FLOW_NODE_HEIGHT = 92;
37
+
38
+ export type ReactFlowNodeView = {
39
+ id: string;
40
+ type: string;
41
+ position: { x: number; y: number };
42
+ width?: number;
43
+ height?: number;
44
+ dragHandle?: string;
45
+ data: BlueprintFlowNodeData;
46
+ };
47
+
48
+ export type ReactFlowEdgeView = {
49
+ id: string;
50
+ type?: string;
51
+ source: string;
52
+ target: string;
53
+ sourceHandle?: string | null;
54
+ targetHandle?: string | null;
55
+ };
56
+
57
+ export function toReactFlowNodes(document: BlueprintDocument): ReactFlowNodeView[] {
58
+ return document.nodes.map((n) => ({
59
+ id: n.id,
60
+ type:
61
+ n.role === "blueprint"
62
+ ? "blueprint"
63
+ : n.role === "lifecycle"
64
+ ? "lifecycle"
65
+ : n.role === "clock"
66
+ ? "clock"
67
+ : n.role === "and"
68
+ ? "and"
69
+ : n.role === "fetch"
70
+ ? "fetch"
71
+ : n.role === "json"
72
+ ? "json"
73
+ : "logic",
74
+ position: { ...n.position },
75
+ width: BP_FLOW_NODE_WIDTH,
76
+ height: BP_FLOW_NODE_HEIGHT,
77
+ zIndex: 0,
78
+ connectable: true,
79
+ dragHandle: ".bp-flow-drag-handle",
80
+ data: {
81
+ label: n.label,
82
+ role: n.role,
83
+ nodeType: n.nodeType,
84
+ configSource: n.configSource,
85
+ viewElementId: n.viewElementId,
86
+ viewElementIds: n.viewElementIds,
87
+ parentId: n.parentId,
88
+ nestedBlueprintId: n.nestedBlueprintId,
89
+ libraryBlueprintId: n.libraryBlueprintId,
90
+ lifecyclePhase: n.lifecyclePhase,
91
+ fetchConfig: n.fetchConfig,
92
+ jsonConfig: n.jsonConfig,
93
+ clockConfig: n.clockConfig,
94
+ logicConfig: n.logicConfig,
95
+ },
96
+ }));
97
+ }
98
+
99
+ export function toReactFlowEdges(document: BlueprintDocument): ReactFlowEdgeView[] {
100
+ return document.edges.map((e) => ({
101
+ id: e.id,
102
+ type: "smoothstep",
103
+ source: e.source,
104
+ target: e.target,
105
+ sourceHandle: e.sourceHandle ?? "out",
106
+ targetHandle: e.targetHandle ?? "in",
107
+ }));
108
+ }
109
+
110
+ export function syncEdgesFromFlow(flowEdges: ReactFlowEdgeView[]): BlueprintGraphEdge[] {
111
+ return flowEdges.map((e) => ({
112
+ id: e.id || createNodeId("edge"),
113
+ source: e.source,
114
+ target: e.target,
115
+ sourceHandle: e.sourceHandle ?? undefined,
116
+ targetHandle: e.targetHandle ?? undefined,
117
+ }));
118
+ }
119
+
120
+ export function collectPositionUpdates(
121
+ nodes: Array<{ id: string; position: { x: number; y: number } }>
122
+ ): Array<{ id: string; position: { x: number; y: number } }> {
123
+ return nodes.map((n) => ({
124
+ id: n.id,
125
+ position: { x: n.position.x, y: n.position.y },
126
+ }));
127
+ }
@@ -0,0 +1,69 @@
1
+ import type { Edge } from "@xyflow/react";
2
+
3
+ import type { BlueprintGraphEdge } from "./document";
4
+ import { syncEdgesFromFlow, toReactFlowEdges } from "./react-flow-adapter";
5
+ import type { BlueprintGraph } from "./blueprint-graph";
6
+
7
+ /** 边线描边(写入 edge.style;使用 hex 避免 SVG style 解析 hsl 异常) */
8
+ export const BP_EDGE_STYLE = {
9
+ stroke: "#2563eb",
10
+ strokeWidth: 2.5,
11
+ } as const;
12
+
13
+ export const BP_FLOW_EDGE_TYPE = "blueprintSmooth" as const;
14
+
15
+ export function flowEdgesToGraphEdges(flowEdges: Edge[]): BlueprintGraphEdge[] {
16
+ return syncEdgesFromFlow(
17
+ flowEdges.map((e) => ({
18
+ id: e.id,
19
+ source: e.source,
20
+ target: e.target,
21
+ sourceHandle: e.sourceHandle ?? "out",
22
+ targetHandle: e.targetHandle ?? "in",
23
+ }))
24
+ );
25
+ }
26
+
27
+ export function graphToFlowEdges(graph: BlueprintGraph): Edge[] {
28
+ return normalizeFlowEdges(toReactFlowEdges(graph.document) as Edge[]);
29
+ }
30
+
31
+ export function edgeListSignature(edges: { id: string }[]): string {
32
+ return edges
33
+ .map((e) => e.id)
34
+ .sort()
35
+ .join(",");
36
+ }
37
+
38
+ function edgeConnectionKey(edge: {
39
+ source: string;
40
+ target: string;
41
+ sourceHandle?: string | null;
42
+ targetHandle?: string | null;
43
+ }): string {
44
+ return `${edge.source}|${edge.sourceHandle ?? "out"}|${edge.target}|${edge.targetHandle ?? "in"}`;
45
+ }
46
+
47
+ /** 避免 onConnect 与 onEdgesChange 各加一条时重复 */
48
+ export function dedupeFlowEdges(edges: Edge[]): Edge[] {
49
+ const seen = new Set<string>();
50
+ const out: Edge[] = [];
51
+ for (const e of edges) {
52
+ const key = edgeConnectionKey(e);
53
+ if (seen.has(key)) continue;
54
+ seen.add(key);
55
+ out.push(e);
56
+ }
57
+ return out;
58
+ }
59
+
60
+ export function normalizeFlowEdges(edges: Edge[]): Edge[] {
61
+ return dedupeFlowEdges(edges).map((e) => ({
62
+ ...e,
63
+ type: BP_FLOW_EDGE_TYPE,
64
+ zIndex: 1000,
65
+ selectable: true,
66
+ deletable: true,
67
+ style: { ...BP_EDGE_STYLE },
68
+ })) as Edge[];
69
+ }