@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.
- package/README.md +103 -0
- package/index.ts +2 -1
- package/package.json +7 -6
- package/src/BlueprintContextMenu.tsx +5 -3
- package/src/BlueprintNodeConfigSidebar.tsx +59 -57
- package/src/blueprint.css +21 -15
- package/src/components/BlueprintExecutionLogPanel.tsx +32 -30
- package/src/components/BlueprintLibrarySelect.tsx +11 -4
- package/src/components/BlueprintMetaDialog.tsx +20 -10
- package/src/components/BlueprintNodeSwitchTaskDialog.tsx +14 -8
- package/src/components/BlueprintPanelToolbar.tsx +50 -42
- package/src/components/ClockNodeConfigPanel.tsx +19 -12
- package/src/components/FetchNodeConfigPanel.tsx +40 -34
- package/src/components/FetchUrlAutocomplete.tsx +3 -1
- package/src/components/JsonNodeConfigPanel.tsx +15 -7
- package/src/components/LogicNodeConfigPanel.tsx +13 -7
- package/src/components/ViewElementMultiSelect.tsx +25 -18
- package/src/dev-seed.ts +2 -2
- package/src/fetch-config-task-store.ts +8 -2
- package/src/graph/blueprint-graph.ts +5 -1
- package/src/graph/document.ts +97 -22
- package/src/graph/node-summary.ts +32 -18
- package/src/graph/sync-nodes.ts +3 -1
- package/src/hooks/useBlueprintDebugSession.ts +24 -22
- package/src/hooks/useBlueprintFlowColorMode.ts +30 -6
- package/src/hooks/useBlueprintPageLifecycle.ts +29 -1
- package/src/library/blueprint-io.ts +12 -3
- package/src/library/swagger-docs.ts +15 -5
- package/src/main.tsx +12 -5
- package/src/nodes/AndFlowNode.tsx +7 -5
- package/src/nodes/BlueprintFlowNode.tsx +4 -2
- package/src/nodes/BlueprintNodeCard.tsx +4 -2
- package/src/nodes/BlueprintNodeShell.tsx +4 -2
- package/src/nodes/ClockFlowNode.tsx +5 -3
- package/src/nodes/FetchFlowNode.tsx +4 -2
- package/src/nodes/JsonFlowNode.tsx +4 -2
- package/src/nodes/LifecycleFlowNode.tsx +5 -3
- package/src/nodes/LogicFlowNode.tsx +4 -2
- package/src/runtime/document-to-runnable-graph.ts +9 -6
package/README.md
ADDED
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
# @arronqzy/react-blueprint
|
|
2
|
+
|
|
3
|
+
Abuilder 的**蓝图编辑器** React 包,基于 [React Flow](https://reactflow.dev/) 提供节点连线编辑、调试执行与蓝图库管理。
|
|
4
|
+
|
|
5
|
+
## 功能概览
|
|
6
|
+
|
|
7
|
+
- **可视化蓝图**:拖拽节点、连线、对齐、视口平移缩放
|
|
8
|
+
- **节点类型**:逻辑、请求、时钟、JSON、子蓝图引用等(由 `blueprint-dsl` 定义行为)
|
|
9
|
+
- **调试执行**:单步/连续调试、执行轨迹、连线信号高亮、时钟节点进度
|
|
10
|
+
- **蓝图库**:IndexedDB 存储、导入导出、元数据编辑
|
|
11
|
+
- **执行日志**:运行记录持久化、导出与清理策略
|
|
12
|
+
- **视图联动**:生命周期钩子向视图节点写入 Scope
|
|
13
|
+
|
|
14
|
+
## 安装
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
pnpm add @arronqzy/react-blueprint @xyflow/react
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
```ts
|
|
21
|
+
import "@arronqzy/react-blueprint/blueprint.css";
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## 使用
|
|
25
|
+
|
|
26
|
+
### 蓝图画布
|
|
27
|
+
|
|
28
|
+
```tsx
|
|
29
|
+
import { useState } from "react";
|
|
30
|
+
import { BluePrintReactRoot, BlueprintGraph } from "@arronqzy/react-blueprint";
|
|
31
|
+
import "@arronqzy/react-blueprint/blueprint.css";
|
|
32
|
+
|
|
33
|
+
function BlueprintEditor() {
|
|
34
|
+
const [graph, setGraph] = useState(() => BlueprintGraph.empty());
|
|
35
|
+
|
|
36
|
+
return (
|
|
37
|
+
<BluePrintReactRoot
|
|
38
|
+
graph={graph}
|
|
39
|
+
onGraphChange={setGraph}
|
|
40
|
+
selectedNodeId={null}
|
|
41
|
+
onSelectNode={() => {}}
|
|
42
|
+
/>
|
|
43
|
+
);
|
|
44
|
+
}
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
### 文档转可执行图
|
|
48
|
+
|
|
49
|
+
```ts
|
|
50
|
+
import { documentToRunnableGraph } from "@arronqzy/react-blueprint";
|
|
51
|
+
|
|
52
|
+
const runnable = documentToRunnableGraph(document, { libraryNameById });
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
### 调试会话
|
|
56
|
+
|
|
57
|
+
```tsx
|
|
58
|
+
import { useBlueprintDebugSession } from "@arronqzy/react-blueprint";
|
|
59
|
+
|
|
60
|
+
const debug = useBlueprintDebugSession({
|
|
61
|
+
graph,
|
|
62
|
+
blueprintId,
|
|
63
|
+
blueprintName: "我的蓝图",
|
|
64
|
+
resolveLibraryBlueprint,
|
|
65
|
+
onViewScopeUpdate: (ids, scope) => { /* 回写视图 Scope */ },
|
|
66
|
+
});
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### 页面生命周期(与视图集成)
|
|
70
|
+
|
|
71
|
+
```tsx
|
|
72
|
+
import { useBlueprintPageLifecycle } from "@arronqzy/react-blueprint";
|
|
73
|
+
|
|
74
|
+
useBlueprintPageLifecycle({
|
|
75
|
+
graph,
|
|
76
|
+
active: blueprintOpen,
|
|
77
|
+
onViewScopeUpdate,
|
|
78
|
+
resolveLibraryBlueprint,
|
|
79
|
+
});
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
## 主要导出
|
|
83
|
+
|
|
84
|
+
| 模块 | 说明 |
|
|
85
|
+
|------|------|
|
|
86
|
+
| `BluePrintReactRoot` | 蓝图主画布 |
|
|
87
|
+
| `BlueprintGraph` | 图数据结构(`src/graph`) |
|
|
88
|
+
| `documentToRunnableGraph` | 文档 → 运行时图 |
|
|
89
|
+
| `useBlueprintDebugSession` | 调试工具栏与会话 |
|
|
90
|
+
| `useBlueprintPageLifecycle` | 挂载/卸载时启停蓝图 |
|
|
91
|
+
| `BlueprintNodeConfigSidebar` | 节点配置侧栏 |
|
|
92
|
+
| `BlueprintPanelToolbar` | 蓝图工具栏 |
|
|
93
|
+
| `blueprint-library-db` | 蓝图库 IndexedDB API |
|
|
94
|
+
|
|
95
|
+
## 依赖关系
|
|
96
|
+
|
|
97
|
+
- `@arronqzy/blueprint-dsl` — 节点定义与执行引擎
|
|
98
|
+
- `@arronqzy/react-rx-store` — 与画布状态订阅
|
|
99
|
+
- `@arronqzy/ui` — UI 组件
|
|
100
|
+
|
|
101
|
+
## 许可证
|
|
102
|
+
|
|
103
|
+
ISC
|
package/index.ts
CHANGED
|
@@ -7,7 +7,8 @@ export type {
|
|
|
7
7
|
BlueprintLibraryOption,
|
|
8
8
|
} from "./src/BlueprintNodeConfigSidebar";
|
|
9
9
|
export * from "./src/graph";
|
|
10
|
-
export {
|
|
10
|
+
export { patchNodeConfigSource } from "./src/graph/document";
|
|
11
|
+
export { resolveBlueprintConfigSource, resolveBlueprintNodeTypeLabel, getBlueprintNodeTypeLabel, getLifecyclePhaseLabel, BLUEPRINT_CONFIG_TYPE_LABEL_KEYS, BLUEPRINT_LIFECYCLE_PHASE_KEYS } from "./src/graph/document";
|
|
11
12
|
export type { BlueprintConfigSource } from "./src/graph/document";
|
|
12
13
|
export { documentToRunnableGraph } from "./src/runtime/document-to-runnable-graph";
|
|
13
14
|
export { BlueprintExecutionLogPanel } from "./src/components/BlueprintExecutionLogPanel";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@arronqzy/react-blueprint",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.4",
|
|
4
4
|
"description": "",
|
|
5
5
|
"main": "index.ts",
|
|
6
6
|
"exports": {
|
|
@@ -12,9 +12,10 @@
|
|
|
12
12
|
"dayjs": "^1.11.13",
|
|
13
13
|
"react-dom": "^19",
|
|
14
14
|
"@xyflow/react": "^12.9.3",
|
|
15
|
-
"@arronqzy/
|
|
16
|
-
"@arronqzy/
|
|
17
|
-
"@arronqzy/
|
|
15
|
+
"@arronqzy/i18n": "0.1.1",
|
|
16
|
+
"@arronqzy/ui": "1.0.3",
|
|
17
|
+
"@arronqzy/react-rx-store": "1.1.2",
|
|
18
|
+
"@arronqzy/blueprint-dsl": "1.0.3"
|
|
18
19
|
},
|
|
19
20
|
"devDependencies": {
|
|
20
21
|
"@types/react": "^19.0.1",
|
|
@@ -27,9 +28,9 @@
|
|
|
27
28
|
"eslint": "^8.57.0",
|
|
28
29
|
"typescript": "^5.5.4",
|
|
29
30
|
"vite": "^5.1.4",
|
|
31
|
+
"@arronqzy/tailwind": "1.0.2",
|
|
30
32
|
"@arronqzy/eslint-config": "0.0.0",
|
|
31
|
-
"@arronqzy/typescript-config": "1.0.0"
|
|
32
|
-
"@arronqzy/tailwind": "1.0.1"
|
|
33
|
+
"@arronqzy/typescript-config": "1.0.0"
|
|
33
34
|
},
|
|
34
35
|
"keywords": [],
|
|
35
36
|
"author": "",
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { useEffect, useRef } from "react";
|
|
2
|
+
import { useI18n } from "@arronqzy/i18n/react";
|
|
2
3
|
|
|
3
4
|
export type BlueprintContextMenuState =
|
|
4
5
|
| {
|
|
@@ -35,6 +36,7 @@ export function BlueprintContextMenu({
|
|
|
35
36
|
onDeleteNode,
|
|
36
37
|
onDeleteEdge,
|
|
37
38
|
}: BlueprintContextMenuProps) {
|
|
39
|
+
const { t } = useI18n();
|
|
38
40
|
const menuRef = useRef<HTMLDivElement>(null);
|
|
39
41
|
|
|
40
42
|
useEffect(() => {
|
|
@@ -74,7 +76,7 @@ export function BlueprintContextMenu({
|
|
|
74
76
|
onClose();
|
|
75
77
|
}}
|
|
76
78
|
>
|
|
77
|
-
|
|
79
|
+
{t("blueprint.toolbar.addNode")}
|
|
78
80
|
</button>
|
|
79
81
|
) : null}
|
|
80
82
|
|
|
@@ -87,7 +89,7 @@ export function BlueprintContextMenu({
|
|
|
87
89
|
onClose();
|
|
88
90
|
}}
|
|
89
91
|
>
|
|
90
|
-
|
|
92
|
+
{t("blueprint.toolbar.deleteNode")}
|
|
91
93
|
</button>
|
|
92
94
|
) : null}
|
|
93
95
|
|
|
@@ -100,7 +102,7 @@ export function BlueprintContextMenu({
|
|
|
100
102
|
onClose();
|
|
101
103
|
}}
|
|
102
104
|
>
|
|
103
|
-
|
|
105
|
+
{t("blueprint.toolbar.deleteEdge")}
|
|
104
106
|
</button>
|
|
105
107
|
) : null}
|
|
106
108
|
</div>
|
|
@@ -9,10 +9,10 @@ import {
|
|
|
9
9
|
} from "@arronqzy/ui";
|
|
10
10
|
import {
|
|
11
11
|
LIFECYCLE_NODE_TYPE,
|
|
12
|
-
PAGE_LIFECYCLE_LABELS,
|
|
13
12
|
PAGE_LIFECYCLE_PHASES,
|
|
14
13
|
type PageLifecyclePhase,
|
|
15
14
|
} from "@arronqzy/blueprint-dsl";
|
|
15
|
+
import { useI18n } from "@arronqzy/i18n/react";
|
|
16
16
|
|
|
17
17
|
import { FetchNodeConfigPanel } from "./components/FetchNodeConfigPanel";
|
|
18
18
|
import { ClockNodeConfigPanel } from "./components/ClockNodeConfigPanel";
|
|
@@ -20,12 +20,14 @@ import { JsonNodeConfigPanel } from "./components/JsonNodeConfigPanel";
|
|
|
20
20
|
import { LogicNodeConfigPanel } from "./components/LogicNodeConfigPanel";
|
|
21
21
|
import { ViewElementMultiSelect } from "./components/ViewElementMultiSelect";
|
|
22
22
|
import {
|
|
23
|
+
getLifecyclePhaseLabel,
|
|
23
24
|
patchNodeConfigSource,
|
|
24
25
|
pruneViewElementIds,
|
|
25
26
|
resolveBlueprintConfigSource,
|
|
26
27
|
resolveViewElementIds,
|
|
27
28
|
type BlueprintConfigSource,
|
|
28
29
|
type BlueprintGraphNode,
|
|
30
|
+
type BlueprintNodeRole,
|
|
29
31
|
} from "./graph/document";
|
|
30
32
|
|
|
31
33
|
export type BlueprintViewElementOption = {
|
|
@@ -67,6 +69,16 @@ export type BlueprintNodeConfigSidebarProps = {
|
|
|
67
69
|
) => void;
|
|
68
70
|
};
|
|
69
71
|
|
|
72
|
+
const ROLE_LABEL_KEYS: Record<BlueprintNodeRole, string> = {
|
|
73
|
+
blueprint: "blueprint.config.roleBlueprint",
|
|
74
|
+
lifecycle: "blueprint.config.roleLifecycle",
|
|
75
|
+
and: "blueprint.config.roleAnd",
|
|
76
|
+
fetch: "blueprint.config.roleFetch",
|
|
77
|
+
json: "blueprint.config.roleJson",
|
|
78
|
+
logic: "blueprint.config.roleLogic",
|
|
79
|
+
clock: "blueprint.config.roleClock",
|
|
80
|
+
};
|
|
81
|
+
|
|
70
82
|
export function BlueprintNodeConfigSidebar({
|
|
71
83
|
node,
|
|
72
84
|
viewElementOptions = [],
|
|
@@ -75,6 +87,7 @@ export function BlueprintNodeConfigSidebar({
|
|
|
75
87
|
onUpdateAllowFalseSignalPropagation,
|
|
76
88
|
onUpdateNode,
|
|
77
89
|
}: BlueprintNodeConfigSidebarProps) {
|
|
90
|
+
const { t } = useI18n();
|
|
78
91
|
const configSource = resolveBlueprintConfigSource(node);
|
|
79
92
|
const linkedViewElementIds = resolveViewElementIds(node);
|
|
80
93
|
const existingViewElementIdSet = useMemo(
|
|
@@ -87,6 +100,8 @@ export function BlueprintNodeConfigSidebar({
|
|
|
87
100
|
|
|
88
101
|
useEffect(() => {
|
|
89
102
|
if (configSource !== "view") return;
|
|
103
|
+
// 视图画布尚未就绪时不清理,避免误删有效绑定导致绑定节点「执行了但不写 scope」
|
|
104
|
+
if (viewElementOptions.length === 0) return;
|
|
90
105
|
const linked = resolveViewElementIds(node);
|
|
91
106
|
if (linked.length === 0) return;
|
|
92
107
|
const pruned = pruneViewElementIds(linked, existingViewElementIdSet);
|
|
@@ -103,30 +118,22 @@ export function BlueprintNodeConfigSidebar({
|
|
|
103
118
|
node.viewElementId,
|
|
104
119
|
node.viewElementIds,
|
|
105
120
|
onUpdateNode,
|
|
121
|
+
viewElementOptions.length,
|
|
106
122
|
]);
|
|
107
123
|
|
|
124
|
+
const roleLabel = t(ROLE_LABEL_KEYS[node.role] ?? "blueprint.config.roleLogic");
|
|
125
|
+
|
|
108
126
|
return (
|
|
109
127
|
<div className="flex h-full min-h-0 flex-col overflow-hidden bg-background text-foreground">
|
|
110
128
|
<div className="shrink-0 border-b border-border px-3 py-2">
|
|
111
|
-
<div className="text-xs font-semibold"
|
|
129
|
+
<div className="text-xs font-semibold">{t("blueprint.config.title")}</div>
|
|
112
130
|
<div className="mt-0.5 text-[11px] text-muted-foreground">
|
|
113
|
-
{node.
|
|
114
|
-
? "蓝图节点"
|
|
115
|
-
: node.role === "lifecycle"
|
|
116
|
-
? "生命周期节点"
|
|
117
|
-
: node.role === "and"
|
|
118
|
-
? "并运算节点"
|
|
119
|
-
: node.role === "fetch"
|
|
120
|
-
? "数据源节点"
|
|
121
|
-
: node.role === "json"
|
|
122
|
-
? "JSON 节点"
|
|
123
|
-
: "逻辑节点"}{" "}
|
|
124
|
-
· {node.id}
|
|
131
|
+
{roleLabel} · {node.id}
|
|
125
132
|
</div>
|
|
126
133
|
</div>
|
|
127
134
|
<div className="min-h-0 flex-1 space-y-3 overflow-auto p-3 text-xs">
|
|
128
135
|
<label className="block space-y-1">
|
|
129
|
-
<span className="text-muted-foreground"
|
|
136
|
+
<span className="text-muted-foreground">{t("blueprint.config.nodeName")}</span>
|
|
130
137
|
<Input
|
|
131
138
|
value={node.label}
|
|
132
139
|
onChange={(e: ChangeEvent<HTMLInputElement>) =>
|
|
@@ -137,7 +144,7 @@ export function BlueprintNodeConfigSidebar({
|
|
|
137
144
|
</label>
|
|
138
145
|
|
|
139
146
|
<label className="block space-y-1">
|
|
140
|
-
<span className="text-muted-foreground"
|
|
147
|
+
<span className="text-muted-foreground">{t("blueprint.config.configType")}</span>
|
|
141
148
|
<Select
|
|
142
149
|
value={configSource}
|
|
143
150
|
onValueChange={(value: string) => {
|
|
@@ -160,25 +167,25 @@ export function BlueprintNodeConfigSidebar({
|
|
|
160
167
|
<SelectValue />
|
|
161
168
|
</SelectTrigger>
|
|
162
169
|
<SelectContent>
|
|
163
|
-
<SelectItem value="blueprint"
|
|
164
|
-
<SelectItem value="logic"
|
|
165
|
-
<SelectItem value="and"
|
|
166
|
-
<SelectItem value="lifecycle"
|
|
167
|
-
<SelectItem value="fetch"
|
|
168
|
-
<SelectItem value="json">
|
|
169
|
-
<SelectItem value="clock"
|
|
170
|
-
<SelectItem value="view"
|
|
170
|
+
<SelectItem value="blueprint">{t("blueprint.config.configBlueprint")}</SelectItem>
|
|
171
|
+
<SelectItem value="logic">{t("blueprint.config.configLogic")}</SelectItem>
|
|
172
|
+
<SelectItem value="and">{t("blueprint.config.configAnd")}</SelectItem>
|
|
173
|
+
<SelectItem value="lifecycle">{t("blueprint.config.configLifecycle")}</SelectItem>
|
|
174
|
+
<SelectItem value="fetch">{t("blueprint.config.configFetch")}</SelectItem>
|
|
175
|
+
<SelectItem value="json">{t("blueprint.config.configJson")}</SelectItem>
|
|
176
|
+
<SelectItem value="clock">{t("blueprint.config.configClock")}</SelectItem>
|
|
177
|
+
<SelectItem value="view">{t("blueprint.config.configView")}</SelectItem>
|
|
171
178
|
</SelectContent>
|
|
172
179
|
</Select>
|
|
173
180
|
</label>
|
|
174
181
|
|
|
175
182
|
{configSource === "view" ? (
|
|
176
183
|
<div className="block space-y-1">
|
|
177
|
-
<span className="text-muted-foreground"
|
|
184
|
+
<span className="text-muted-foreground">{t("blueprint.config.linkedViewNodes")}</span>
|
|
178
185
|
<ViewElementMultiSelect
|
|
179
186
|
options={viewElementOptions}
|
|
180
187
|
value={linkedViewElementIds}
|
|
181
|
-
placeholder="
|
|
188
|
+
placeholder={t("blueprint.config.selectViewNode")}
|
|
182
189
|
onChange={(next) =>
|
|
183
190
|
onUpdateNode(node.id, {
|
|
184
191
|
viewElementIds: next,
|
|
@@ -189,15 +196,16 @@ export function BlueprintNodeConfigSidebar({
|
|
|
189
196
|
/>
|
|
190
197
|
{linkedViewElementIds.length === 0 ? (
|
|
191
198
|
<p className="text-[11px] text-muted-foreground">
|
|
192
|
-
|
|
199
|
+
{t("blueprint.config.viewMultiHint")}
|
|
193
200
|
</p>
|
|
194
201
|
) : (
|
|
195
202
|
<p className="text-[11px] text-muted-foreground">
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
203
|
+
{t("blueprint.config.linkedViewCount", {
|
|
204
|
+
count: linkedViewElementIds.length,
|
|
205
|
+
names: linkedViewElementIds
|
|
206
|
+
.map((id) => viewElementLabelById.get(id) ?? id)
|
|
207
|
+
.join("、"),
|
|
208
|
+
})}
|
|
201
209
|
</p>
|
|
202
210
|
)}
|
|
203
211
|
</div>
|
|
@@ -205,15 +213,12 @@ export function BlueprintNodeConfigSidebar({
|
|
|
205
213
|
|
|
206
214
|
{configSource === "blueprint" ? (
|
|
207
215
|
<div className="space-y-2 rounded-md border border-border/70 bg-muted/20 p-2.5">
|
|
208
|
-
<div className="font-medium text-foreground"
|
|
216
|
+
<div className="font-medium text-foreground">{t("blueprint.config.blueprintAttrs")}</div>
|
|
209
217
|
<p className="text-[11px] text-muted-foreground">
|
|
210
|
-
|
|
211
|
-
时才会执行该蓝图;执行完成后从输出端发出
|
|
212
|
-
<strong>真信号</strong>(含嵌套蓝图输出值与当前节点信息),执行失败则发出
|
|
213
|
-
<strong>假信号</strong>。
|
|
218
|
+
{t("blueprint.config.blueprintAttrsHint")}
|
|
214
219
|
</p>
|
|
215
220
|
<label className="block space-y-1">
|
|
216
|
-
<span className="text-muted-foreground"
|
|
221
|
+
<span className="text-muted-foreground">{t("blueprint.config.refLibrary")}</span>
|
|
217
222
|
<Select
|
|
218
223
|
value={node.libraryBlueprintId ?? "__none__"}
|
|
219
224
|
onValueChange={(value: string) => {
|
|
@@ -224,10 +229,10 @@ export function BlueprintNodeConfigSidebar({
|
|
|
224
229
|
}}
|
|
225
230
|
>
|
|
226
231
|
<SelectTrigger className="h-8">
|
|
227
|
-
<SelectValue placeholder="
|
|
232
|
+
<SelectValue placeholder={t("blueprint.config.selectLibraryBlueprint")} />
|
|
228
233
|
</SelectTrigger>
|
|
229
234
|
<SelectContent className="z-[10100]">
|
|
230
|
-
<SelectItem value="__none__"
|
|
235
|
+
<SelectItem value="__none__">{t("blueprint.config.unlinked")}</SelectItem>
|
|
231
236
|
{blueprintLibraryOptions.map((opt) => (
|
|
232
237
|
<SelectItem key={opt.id} value={opt.id}>
|
|
233
238
|
{opt.label}
|
|
@@ -238,7 +243,7 @@ export function BlueprintNodeConfigSidebar({
|
|
|
238
243
|
</label>
|
|
239
244
|
{!node.libraryBlueprintId ? (
|
|
240
245
|
<p className="text-[11px] text-muted-foreground">
|
|
241
|
-
|
|
246
|
+
{t("blueprint.config.selectLibraryFirst")}
|
|
242
247
|
</p>
|
|
243
248
|
) : null}
|
|
244
249
|
<label className="flex items-start gap-2 pt-1">
|
|
@@ -251,7 +256,7 @@ export function BlueprintNodeConfigSidebar({
|
|
|
251
256
|
className="mt-0.5 h-3.5 w-3.5 shrink-0 rounded border border-input"
|
|
252
257
|
/>
|
|
253
258
|
<span className="text-[11px] leading-relaxed text-muted-foreground">
|
|
254
|
-
|
|
259
|
+
{t("blueprint.config.allowFalsePropagateBlueprint")}
|
|
255
260
|
</span>
|
|
256
261
|
</label>
|
|
257
262
|
</div>
|
|
@@ -259,15 +264,15 @@ export function BlueprintNodeConfigSidebar({
|
|
|
259
264
|
|
|
260
265
|
{configSource === "lifecycle" ? (
|
|
261
266
|
<div className="space-y-2 rounded-md border border-border/70 bg-muted/20 p-2.5">
|
|
262
|
-
<div className="font-medium text-foreground"
|
|
267
|
+
<div className="font-medium text-foreground">{t("blueprint.config.lifecycleHook")}</div>
|
|
263
268
|
<p className="text-[11px] text-muted-foreground">
|
|
264
|
-
|
|
269
|
+
{t("blueprint.config.lifecycleNoInput")}
|
|
265
270
|
{node.lifecyclePhase === "blueprintActivated"
|
|
266
|
-
? "
|
|
267
|
-
: "
|
|
271
|
+
? t("blueprint.config.lifecycleBlueprintActivatedHint")
|
|
272
|
+
: t("blueprint.config.lifecyclePageHint")}
|
|
268
273
|
</p>
|
|
269
274
|
<label className="block space-y-1">
|
|
270
|
-
<span className="text-muted-foreground"
|
|
275
|
+
<span className="text-muted-foreground">{t("blueprint.config.listenPhase")}</span>
|
|
271
276
|
<Select
|
|
272
277
|
value={node.lifecyclePhase ?? "mounted"}
|
|
273
278
|
onValueChange={(value: string) =>
|
|
@@ -285,7 +290,7 @@ export function BlueprintNodeConfigSidebar({
|
|
|
285
290
|
<SelectContent>
|
|
286
291
|
{PAGE_LIFECYCLE_PHASES.map((phase) => (
|
|
287
292
|
<SelectItem key={phase} value={phase}>
|
|
288
|
-
{
|
|
293
|
+
{getLifecyclePhaseLabel(t, phase)}
|
|
289
294
|
</SelectItem>
|
|
290
295
|
))}
|
|
291
296
|
</SelectContent>
|
|
@@ -293,7 +298,7 @@ export function BlueprintNodeConfigSidebar({
|
|
|
293
298
|
</label>
|
|
294
299
|
{node.parentId ? (
|
|
295
300
|
<p className="text-[11px] text-muted-foreground">
|
|
296
|
-
|
|
301
|
+
{t("blueprint.config.parentBlueprintNode", { id: node.parentId })}
|
|
297
302
|
</p>
|
|
298
303
|
) : null}
|
|
299
304
|
</div>
|
|
@@ -313,12 +318,9 @@ export function BlueprintNodeConfigSidebar({
|
|
|
313
318
|
|
|
314
319
|
{configSource === "and" ? (
|
|
315
320
|
<div className="space-y-2 rounded-md border border-border/70 bg-muted/20 p-2.5">
|
|
316
|
-
<div className="font-medium text-foreground"
|
|
321
|
+
<div className="font-medium text-foreground">{t("blueprint.config.andTitle")}</div>
|
|
317
322
|
<p className="text-[11px] text-muted-foreground">
|
|
318
|
-
|
|
319
|
-
每个输入口可连 n 条线,同端口任一为真则该端口视为真(或)。
|
|
320
|
-
仅当 <strong>inA 与 inB 均为真信号</strong> 时,从输出口发出真信号;
|
|
321
|
-
否则发出假信号。
|
|
323
|
+
{t("blueprint.config.andHint")}
|
|
322
324
|
</p>
|
|
323
325
|
</div>
|
|
324
326
|
) : null}
|
|
@@ -328,7 +330,7 @@ export function BlueprintNodeConfigSidebar({
|
|
|
328
330
|
<LogicNodeConfigPanel node={node} onUpdateNode={onUpdateNode} />
|
|
329
331
|
{node.parentId ? (
|
|
330
332
|
<p className="text-[11px] text-muted-foreground">
|
|
331
|
-
|
|
333
|
+
{t("blueprint.config.parentBlueprintNode", { id: node.parentId })}
|
|
332
334
|
</p>
|
|
333
335
|
) : null}
|
|
334
336
|
</>
|
|
@@ -336,7 +338,7 @@ export function BlueprintNodeConfigSidebar({
|
|
|
336
338
|
|
|
337
339
|
{configSource !== "blueprint" && configSource !== "and" ? (
|
|
338
340
|
<div className="space-y-2 rounded-md border border-border/70 bg-muted/20 p-2.5">
|
|
339
|
-
<div className="font-medium text-foreground"
|
|
341
|
+
<div className="font-medium text-foreground">{t("blueprint.config.taskChain")}</div>
|
|
340
342
|
<label className="flex items-start gap-2">
|
|
341
343
|
<input
|
|
342
344
|
type="checkbox"
|
|
@@ -347,7 +349,7 @@ export function BlueprintNodeConfigSidebar({
|
|
|
347
349
|
className="mt-0.5 h-3.5 w-3.5 shrink-0 rounded border border-input"
|
|
348
350
|
/>
|
|
349
351
|
<span className="text-[11px] leading-relaxed text-muted-foreground">
|
|
350
|
-
|
|
352
|
+
{t("blueprint.config.allowFalsePropagateDefault")}
|
|
351
353
|
</span>
|
|
352
354
|
</label>
|
|
353
355
|
</div>
|
package/src/blueprint.css
CHANGED
|
@@ -19,6 +19,8 @@
|
|
|
19
19
|
--xy-edge-stroke-selected: #1d4ed8;
|
|
20
20
|
--xy-connectionline-stroke: #2563eb;
|
|
21
21
|
--xy-connectionline-stroke-width: 2.5;
|
|
22
|
+
--xy-handle-background-color: hsl(var(--primary));
|
|
23
|
+
--xy-handle-border-color: hsl(var(--background));
|
|
22
24
|
background-color: transparent;
|
|
23
25
|
}
|
|
24
26
|
|
|
@@ -174,11 +176,11 @@
|
|
|
174
176
|
height: 10px;
|
|
175
177
|
min-width: 10px;
|
|
176
178
|
min-height: 10px;
|
|
177
|
-
border: 2px solid hsl(var(--background));
|
|
179
|
+
border: 2px solid hsl(var(--background)) !important;
|
|
178
180
|
border-radius: 9999px;
|
|
179
|
-
background: hsl(var(--
|
|
181
|
+
background-color: hsl(var(--foreground) / 0.42) !important;
|
|
180
182
|
transition:
|
|
181
|
-
background 0.15s ease,
|
|
183
|
+
background-color 0.15s ease,
|
|
182
184
|
border-color 0.15s ease,
|
|
183
185
|
box-shadow 0.15s ease;
|
|
184
186
|
}
|
|
@@ -186,30 +188,34 @@
|
|
|
186
188
|
.bp-canvas .react-flow__handle.bp-flow-handle:hover,
|
|
187
189
|
.bp-flow .react-flow__handle.bp-flow-handle:hover,
|
|
188
190
|
[data-workspace-region="blueprint"] .react-flow__handle.bp-flow-handle:hover {
|
|
189
|
-
background: hsl(var(--primary));
|
|
190
|
-
border-color: hsl(var(--background));
|
|
191
|
+
background-color: hsl(var(--primary)) !important;
|
|
192
|
+
border-color: hsl(var(--background)) !important;
|
|
191
193
|
box-shadow: 0 0 0 3px hsl(var(--primary) / 0.28);
|
|
192
194
|
}
|
|
193
195
|
|
|
194
196
|
.bp-canvas .react-flow__handle.bp-flow-handle--target,
|
|
195
197
|
.bp-flow .react-flow__handle.bp-flow-handle--target,
|
|
196
198
|
[data-workspace-region="blueprint"] .react-flow__handle.bp-flow-handle--target {
|
|
197
|
-
background: hsl(var(--
|
|
199
|
+
background-color: hsl(var(--foreground) / 0.48) !important;
|
|
198
200
|
}
|
|
199
201
|
|
|
200
202
|
.bp-canvas .react-flow__handle.bp-flow-handle--source,
|
|
201
203
|
.bp-flow .react-flow__handle.bp-flow-handle--source,
|
|
202
204
|
[data-workspace-region="blueprint"] .react-flow__handle.bp-flow-handle--source {
|
|
203
|
-
background: hsl(var(--primary) / 0.
|
|
205
|
+
background-color: hsl(var(--primary) / 0.72) !important;
|
|
204
206
|
}
|
|
205
207
|
|
|
206
208
|
.bp-node--selected .bp-flow-handle {
|
|
207
|
-
border-color: hsl(var(--card));
|
|
209
|
+
border-color: hsl(var(--card)) !important;
|
|
208
210
|
box-shadow: 0 0 0 2px hsl(var(--primary) / 0.35);
|
|
209
211
|
}
|
|
210
212
|
|
|
213
|
+
.bp-node--selected .bp-flow-handle--target {
|
|
214
|
+
background-color: hsl(var(--foreground) / 0.55) !important;
|
|
215
|
+
}
|
|
216
|
+
|
|
211
217
|
.bp-node--selected .bp-flow-handle--source {
|
|
212
|
-
background: hsl(var(--primary));
|
|
218
|
+
background-color: hsl(var(--primary)) !important;
|
|
213
219
|
}
|
|
214
220
|
|
|
215
221
|
.bp-node--execution-true .bp-node-card {
|
|
@@ -225,11 +231,11 @@
|
|
|
225
231
|
}
|
|
226
232
|
|
|
227
233
|
.bp-node--execution-true .bp-flow-handle--source {
|
|
228
|
-
background: #16a34a;
|
|
234
|
+
background-color: #16a34a !important;
|
|
229
235
|
}
|
|
230
236
|
|
|
231
237
|
.bp-node--execution-true .bp-flow-handle--target {
|
|
232
|
-
background: rgb(22 163 74 / 0.55);
|
|
238
|
+
background-color: rgb(22 163 74 / 0.55) !important;
|
|
233
239
|
}
|
|
234
240
|
|
|
235
241
|
.bp-node--execution-false .bp-node-card {
|
|
@@ -245,11 +251,11 @@
|
|
|
245
251
|
}
|
|
246
252
|
|
|
247
253
|
.bp-node--execution-false .bp-flow-handle--source {
|
|
248
|
-
background: hsl(var(--destructive));
|
|
254
|
+
background-color: hsl(var(--destructive)) !important;
|
|
249
255
|
}
|
|
250
256
|
|
|
251
257
|
.bp-node--execution-false .bp-flow-handle--target {
|
|
252
|
-
background: hsl(var(--destructive) / 0.55);
|
|
258
|
+
background-color: hsl(var(--destructive) / 0.55) !important;
|
|
253
259
|
}
|
|
254
260
|
|
|
255
261
|
/* 生命周期节点无输入口:隐藏任何左侧/目标连接点(含旧数据产生的幽灵 handle) */
|
|
@@ -267,13 +273,13 @@
|
|
|
267
273
|
.bp-canvas .react-flow__handle.bp-flow-handle--signal,
|
|
268
274
|
.bp-flow .react-flow__handle.bp-flow-handle--signal,
|
|
269
275
|
[data-workspace-region="blueprint"] .react-flow__handle.bp-flow-handle--signal {
|
|
270
|
-
background: hsl(38 92% 50% / 0.75);
|
|
276
|
+
background-color: hsl(38 92% 50% / 0.75) !important;
|
|
271
277
|
width: 8px;
|
|
272
278
|
height: 8px;
|
|
273
279
|
}
|
|
274
280
|
|
|
275
281
|
.bp-node--selected .bp-flow-handle--signal {
|
|
276
|
-
background: hsl(38 92% 50%);
|
|
282
|
+
background-color: hsl(38 92% 50%) !important;
|
|
277
283
|
}
|
|
278
284
|
|
|
279
285
|
/* 右键菜单 */
|