@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.
- package/.eslintrc.cjs +5 -0
- package/.pnpm-cache/metadata/registry.npmjs.org/@esbuild/aix-ppc64.json +1 -0
- package/.pnpm-cache/metadata/registry.npmjs.org/@esbuild/android-arm.json +1 -0
- package/.pnpm-cache/metadata/registry.npmjs.org/@esbuild/android-arm64.json +1 -0
- package/.pnpm-cache/metadata/registry.npmjs.org/@esbuild/android-x64.json +1 -0
- package/.pnpm-cache/metadata/registry.npmjs.org/@esbuild/darwin-arm64.json +1 -0
- package/.pnpm-cache/metadata/registry.npmjs.org/@esbuild/darwin-x64.json +1 -0
- package/.pnpm-cache/metadata/registry.npmjs.org/@esbuild/freebsd-arm64.json +1 -0
- package/.pnpm-cache/metadata/registry.npmjs.org/@esbuild/freebsd-x64.json +1 -0
- package/.pnpm-cache/metadata/registry.npmjs.org/@esbuild/linux-arm.json +1 -0
- package/.pnpm-cache/metadata/registry.npmjs.org/@esbuild/linux-arm64.json +1 -0
- package/.pnpm-cache/metadata/registry.npmjs.org/@esbuild/linux-ia32.json +1 -0
- package/.pnpm-cache/metadata/registry.npmjs.org/@esbuild/linux-loong64.json +1 -0
- package/.pnpm-cache/metadata/registry.npmjs.org/@esbuild/linux-mips64el.json +1 -0
- package/.pnpm-cache/metadata/registry.npmjs.org/@esbuild/linux-ppc64.json +1 -0
- package/.pnpm-cache/metadata/registry.npmjs.org/@esbuild/linux-riscv64.json +1 -0
- package/.pnpm-cache/metadata/registry.npmjs.org/@esbuild/linux-s390x.json +1 -0
- package/.pnpm-cache/metadata/registry.npmjs.org/@esbuild/linux-x64.json +1 -0
- package/.pnpm-cache/metadata/registry.npmjs.org/@esbuild/netbsd-x64.json +1 -0
- package/.pnpm-cache/metadata/registry.npmjs.org/@esbuild/openbsd-x64.json +1 -0
- package/.pnpm-cache/metadata/registry.npmjs.org/@esbuild/sunos-x64.json +1 -0
- package/.pnpm-cache/metadata/registry.npmjs.org/@esbuild/win32-arm64.json +1 -0
- package/.pnpm-cache/metadata/registry.npmjs.org/@esbuild/win32-ia32.json +1 -0
- package/.pnpm-cache/metadata/registry.npmjs.org/@esbuild/win32-x64.json +1 -0
- package/.pnpm-cache/metadata/registry.npmjs.org/@rollup/rollup-android-arm-eabi.json +1 -0
- package/.pnpm-cache/metadata/registry.npmjs.org/@rollup/rollup-android-arm64.json +1 -0
- package/.pnpm-cache/metadata/registry.npmjs.org/@rollup/rollup-darwin-arm64.json +1 -0
- package/.pnpm-cache/metadata/registry.npmjs.org/@rollup/rollup-darwin-x64.json +1 -0
- package/.pnpm-cache/metadata/registry.npmjs.org/@rollup/rollup-freebsd-arm64.json +1 -0
- package/.pnpm-cache/metadata/registry.npmjs.org/@rollup/rollup-freebsd-x64.json +1 -0
- package/.pnpm-cache/metadata/registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf.json +1 -0
- package/.pnpm-cache/metadata/registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf.json +1 -0
- package/.pnpm-cache/metadata/registry.npmjs.org/@rollup/rollup-linux-arm64-gnu.json +1 -0
- package/.pnpm-cache/metadata/registry.npmjs.org/@rollup/rollup-linux-arm64-musl.json +1 -0
- package/.pnpm-cache/metadata/registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu.json +1 -0
- package/.pnpm-cache/metadata/registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu.json +1 -0
- package/.pnpm-cache/metadata/registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu.json +1 -0
- package/.pnpm-cache/metadata/registry.npmjs.org/@rollup/rollup-linux-riscv64-musl.json +1 -0
- package/.pnpm-cache/metadata/registry.npmjs.org/@rollup/rollup-linux-s390x-gnu.json +1 -0
- package/.pnpm-cache/metadata/registry.npmjs.org/@rollup/rollup-linux-x64-gnu.json +1 -0
- package/.pnpm-cache/metadata/registry.npmjs.org/@rollup/rollup-linux-x64-musl.json +1 -0
- package/.pnpm-cache/metadata/registry.npmjs.org/@rollup/rollup-win32-arm64-msvc.json +1 -0
- package/.pnpm-cache/metadata/registry.npmjs.org/@rollup/rollup-win32-ia32-msvc.json +1 -0
- package/.pnpm-cache/metadata/registry.npmjs.org/@rollup/rollup-win32-x64-msvc.json +1 -0
- package/.pnpm-cache/metadata/registry.npmjs.org/eslint.json +1 -0
- package/.pnpm-cache/metadata/registry.npmjs.org/fsevents.json +1 -0
- package/.pnpm-cache/metadata/registry.npmjs.org/react-dom.json +1 -0
- package/.pnpm-cache/metadata/registry.npmjs.org/react-is.json +1 -0
- package/.pnpm-cache/metadata/registry.npmjs.org/turbo-darwin-64.json +1 -0
- package/.pnpm-cache/metadata/registry.npmjs.org/turbo-darwin-arm64.json +1 -0
- package/.pnpm-cache/metadata/registry.npmjs.org/turbo-linux-64.json +1 -0
- package/.pnpm-cache/metadata/registry.npmjs.org/turbo-linux-arm64.json +1 -0
- package/.pnpm-cache/metadata/registry.npmjs.org/turbo-windows-64.json +1 -0
- package/.pnpm-cache/metadata/registry.npmjs.org/turbo-windows-arm64.json +1 -0
- package/.pnpm-cache/metadata/registry.npmjs.org/typescript.json +1 -0
- package/.turbo/turbo-lint.log +4 -0
- package/dist/assets/index-CneNgzWb.css +1 -0
- package/dist/assets/index-DsVhHV0-.js +226 -0
- package/dist/index.html +14 -0
- package/index.html +13 -0
- package/index.ts +70 -0
- package/package.json +43 -0
- package/postcss.config.cjs +6 -0
- package/src/BlueprintCanvasContext.tsx +61 -0
- package/src/BlueprintContextMenu.tsx +108 -0
- package/src/BlueprintNodeConfigSidebar.tsx +358 -0
- package/src/blueprint.css +321 -0
- package/src/blueprintNodeTypes.ts +20 -0
- package/src/components/BlueprintExecutionLogPanel.tsx +427 -0
- package/src/components/BlueprintLibrarySelect.tsx +68 -0
- package/src/components/BlueprintMetaDialog.tsx +96 -0
- package/src/components/BlueprintNodeSwitchTaskDialog.tsx +58 -0
- package/src/components/BlueprintPanelToolbar.tsx +664 -0
- package/src/components/ClockNodeConfigPanel.tsx +139 -0
- package/src/components/FetchNodeConfigPanel.tsx +716 -0
- package/src/components/FetchUrlAutocomplete.tsx +190 -0
- package/src/components/JsonNodeConfigPanel.tsx +79 -0
- package/src/components/LogicNodeConfigPanel.tsx +80 -0
- package/src/components/ViewElementMultiSelect.tsx +140 -0
- package/src/createBlueprintEdgeTypes.tsx +10 -0
- package/src/dev-seed.ts +34 -0
- package/src/dev.css +38 -0
- package/src/edges/BlueprintSmoothEdge.tsx +35 -0
- package/src/fetch-config-task-store.ts +197 -0
- package/src/flowCoordinates.ts +19 -0
- package/src/flowDefaults.ts +9 -0
- package/src/graph/blueprint-graph.ts +261 -0
- package/src/graph/document.ts +384 -0
- package/src/graph/index.ts +7 -0
- package/src/graph/node-summary.ts +88 -0
- package/src/graph/node-types.ts +9 -0
- package/src/graph/react-flow-adapter.ts +127 -0
- package/src/graph/sync-edges.ts +69 -0
- package/src/graph/sync-nodes.ts +104 -0
- package/src/hooks/useBlueprintDebugSession.ts +515 -0
- package/src/hooks/useBlueprintFlowColorMode.ts +8 -0
- package/src/hooks/useBlueprintFlowState.ts +486 -0
- package/src/hooks/useBlueprintFlowViewport.ts +54 -0
- package/src/hooks/useBlueprintNodeSelectionGuard.ts +63 -0
- package/src/hooks/useBlueprintPageLifecycle.ts +245 -0
- package/src/index.tsx +271 -0
- package/src/library/blueprint-io.ts +108 -0
- package/src/library/blueprint-library-db.ts +112 -0
- package/src/library/execution-log-db.ts +171 -0
- package/src/library/execution-log-settings.ts +50 -0
- package/src/library/swagger-docs.ts +56 -0
- package/src/library/types.ts +35 -0
- package/src/main.tsx +43 -0
- package/src/nodes/AndFlowNode.tsx +59 -0
- package/src/nodes/BlueprintFlowNode.tsx +26 -0
- package/src/nodes/BlueprintNodeCard.tsx +169 -0
- package/src/nodes/BlueprintNodeShell.tsx +45 -0
- package/src/nodes/ClockFlowNode.tsx +67 -0
- package/src/nodes/FetchFlowNode.tsx +26 -0
- package/src/nodes/JsonFlowNode.tsx +26 -0
- package/src/nodes/LifecycleFlowNode.tsx +44 -0
- package/src/nodes/LogicFlowNode.tsx +26 -0
- package/src/runtime/document-to-runnable-graph.ts +48 -0
- package/src/runtime/execution-overlay.ts +169 -0
- package/src/types.ts +1 -0
- package/src/vite-env.d.ts +1 -0
- package/tailwind.config.ts +6 -0
- package/tsconfig.json +12 -0
- package/vite.config.ts +16 -0
package/dist/index.html
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8" />
|
|
5
|
+
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
|
6
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
7
|
+
<title>blue-print</title>
|
|
8
|
+
<script type="module" crossorigin src="/assets/index-DsVhHV0-.js"></script>
|
|
9
|
+
<link rel="stylesheet" crossorigin href="/assets/index-CneNgzWb.css">
|
|
10
|
+
</head>
|
|
11
|
+
<body>
|
|
12
|
+
<div id="app"></div>
|
|
13
|
+
</body>
|
|
14
|
+
</html>
|
package/index.html
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8" />
|
|
5
|
+
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
|
6
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
7
|
+
<title>blue-print</title>
|
|
8
|
+
</head>
|
|
9
|
+
<body>
|
|
10
|
+
<div id="app"></div>
|
|
11
|
+
<script type="module" src="/src/main.tsx"></script>
|
|
12
|
+
</body>
|
|
13
|
+
</html>
|
package/index.ts
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
export { BluePrintReactRoot } from "./src/index";
|
|
2
|
+
export type { BluePrintReactRootProps } from "./src/index";
|
|
3
|
+
export { BlueprintNodeConfigSidebar } from "./src/BlueprintNodeConfigSidebar";
|
|
4
|
+
export type {
|
|
5
|
+
BlueprintNodeConfigSidebarProps,
|
|
6
|
+
BlueprintViewElementOption,
|
|
7
|
+
BlueprintLibraryOption,
|
|
8
|
+
} from "./src/BlueprintNodeConfigSidebar";
|
|
9
|
+
export * from "./src/graph";
|
|
10
|
+
export { resolveBlueprintConfigSource, resolveBlueprintNodeTypeLabel } from "./src/graph/document";
|
|
11
|
+
export type { BlueprintConfigSource } from "./src/graph/document";
|
|
12
|
+
export { documentToRunnableGraph } from "./src/runtime/document-to-runnable-graph";
|
|
13
|
+
export { BlueprintExecutionLogPanel } from "./src/components/BlueprintExecutionLogPanel";
|
|
14
|
+
export type { BlueprintExecutionLogPanelProps } from "./src/components/BlueprintExecutionLogPanel";
|
|
15
|
+
export { useBlueprintDebugSession } from "./src/hooks/useBlueprintDebugSession";
|
|
16
|
+
export type {
|
|
17
|
+
LifecycleNodeOption,
|
|
18
|
+
UseBlueprintDebugSessionOptions,
|
|
19
|
+
UseBlueprintDebugSessionReturn,
|
|
20
|
+
} from "./src/hooks/useBlueprintDebugSession";
|
|
21
|
+
export {
|
|
22
|
+
listExecutionRunRecords,
|
|
23
|
+
putExecutionRunRecord,
|
|
24
|
+
purgeExecutionRunsOlderThan,
|
|
25
|
+
clearAllExecutionRunRecords,
|
|
26
|
+
trimExecutionRunRecordsToMax,
|
|
27
|
+
downloadExecutionRunExport,
|
|
28
|
+
} from "./src/library/execution-log-db";
|
|
29
|
+
export type { UseBlueprintPageLifecycleOptions } from "./src/hooks/useBlueprintPageLifecycle";
|
|
30
|
+
export { useBlueprintPageLifecycle } from "./src/hooks/useBlueprintPageLifecycle";
|
|
31
|
+
export { stopAllClockSchedules } from "@arronqzy/blueprint-dsl";
|
|
32
|
+
export { BlueprintNodeSwitchTaskDialog } from "./src/components/BlueprintNodeSwitchTaskDialog";
|
|
33
|
+
export type { BlueprintNodeSwitchTaskDialogProps } from "./src/components/BlueprintNodeSwitchTaskDialog";
|
|
34
|
+
export { useBlueprintNodeSelectionGuard } from "./src/hooks/useBlueprintNodeSelectionGuard";
|
|
35
|
+
export type { PendingBlueprintNodeSwitch } from "./src/hooks/useBlueprintNodeSelectionGuard";
|
|
36
|
+
export { BlueprintMetaDialog } from "./src/components/BlueprintMetaDialog";
|
|
37
|
+
export type { BlueprintMetaDialogProps } from "./src/components/BlueprintMetaDialog";
|
|
38
|
+
export { BlueprintLibrarySelect } from "./src/components/BlueprintLibrarySelect";
|
|
39
|
+
export type { BlueprintLibrarySelectProps } from "./src/components/BlueprintLibrarySelect";
|
|
40
|
+
export { BlueprintPanelToolbar, BlueprintRenameDialog, BlueprintDeleteDialog } from "./src/components/BlueprintPanelToolbar";
|
|
41
|
+
export type {
|
|
42
|
+
BlueprintPanelToolbarProps,
|
|
43
|
+
BlueprintRenameDialogProps,
|
|
44
|
+
BlueprintDeleteDialogProps,
|
|
45
|
+
BlueprintDebugToolbarProps,
|
|
46
|
+
} from "./src/components/BlueprintPanelToolbar";
|
|
47
|
+
export type { ExecutionLogSettings } from "./src/library/execution-log-settings";
|
|
48
|
+
export type {
|
|
49
|
+
ExecutionTraceEntry,
|
|
50
|
+
ExecutionRunRecord,
|
|
51
|
+
} from "@arronqzy/blueprint-dsl";
|
|
52
|
+
export type { BlueprintExecutionOverlay } from "./src/runtime/execution-overlay";
|
|
53
|
+
export { buildExecutionOverlay, shouldHaltDebugOnFalseSignal } from "./src/runtime/execution-overlay";
|
|
54
|
+
export * from "./src/library/types";
|
|
55
|
+
export {
|
|
56
|
+
blueprintDocumentsEqual,
|
|
57
|
+
buildBlueprintExportPayload,
|
|
58
|
+
buildLibraryRecord,
|
|
59
|
+
createLibraryBlueprintId,
|
|
60
|
+
downloadBlueprintExport,
|
|
61
|
+
libraryRecordFromImport,
|
|
62
|
+
parseBlueprintImportFile,
|
|
63
|
+
} from "./src/library/blueprint-io";
|
|
64
|
+
export {
|
|
65
|
+
deleteBlueprintLibraryRecord,
|
|
66
|
+
getBlueprintLibraryRecord,
|
|
67
|
+
listBlueprintLibrary,
|
|
68
|
+
putBlueprintLibraryRecord,
|
|
69
|
+
updateBlueprintLibraryMeta,
|
|
70
|
+
} from "./src/library/blueprint-library-db";
|
package/package.json
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@arronqzy/react-blueprint",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "",
|
|
5
|
+
"main": "index.ts",
|
|
6
|
+
"exports": {
|
|
7
|
+
".": "./index.ts",
|
|
8
|
+
"./blueprint.css": "./src/blueprint.css"
|
|
9
|
+
},
|
|
10
|
+
"dependencies": {
|
|
11
|
+
"react": "^19",
|
|
12
|
+
"dayjs": "^1.11.13",
|
|
13
|
+
"react-dom": "^19",
|
|
14
|
+
"@xyflow/react": "^12.9.3",
|
|
15
|
+
"@arronqzy/blueprint-dsl": "0.0.1",
|
|
16
|
+
"@arronqzy/react-rx-store": "1.0.0",
|
|
17
|
+
"@arronqzy/ui": "0.0.0"
|
|
18
|
+
},
|
|
19
|
+
"devDependencies": {
|
|
20
|
+
"@types/react": "^19.0.1",
|
|
21
|
+
"@types/react-dom": "^19.0.2",
|
|
22
|
+
"@vitejs/plugin-react": "^3.1.0",
|
|
23
|
+
"autoprefixer": "^10.4.20",
|
|
24
|
+
"postcss": "^8.4.49",
|
|
25
|
+
"tailwindcss": "^3.4.17",
|
|
26
|
+
"@types/use-sync-external-store": "^1.5.0",
|
|
27
|
+
"eslint": "^8.57.0",
|
|
28
|
+
"typescript": "^5.5.4",
|
|
29
|
+
"vite": "^5.1.4",
|
|
30
|
+
"@arronqzy/tailwind": "0.0.1",
|
|
31
|
+
"@arronqzy/eslint-config": "0.0.0",
|
|
32
|
+
"@arronqzy/typescript-config": "0.0.0"
|
|
33
|
+
},
|
|
34
|
+
"keywords": [],
|
|
35
|
+
"author": "",
|
|
36
|
+
"license": "ISC",
|
|
37
|
+
"scripts": {
|
|
38
|
+
"lint": "eslint \"src/**/*.{ts,tsx}\"",
|
|
39
|
+
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
40
|
+
"build": "pnpm run lint && pnpm run typecheck && vite build",
|
|
41
|
+
"dev": "vite"
|
|
42
|
+
}
|
|
43
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import {
|
|
2
|
+
createContext,
|
|
3
|
+
useContext,
|
|
4
|
+
useRef,
|
|
5
|
+
useSyncExternalStore,
|
|
6
|
+
type ReactNode,
|
|
7
|
+
type RefObject,
|
|
8
|
+
} from "react";
|
|
9
|
+
import { isClockNodeActive, subscribeActiveClockNodes } from "@arronqzy/blueprint-dsl";
|
|
10
|
+
|
|
11
|
+
type BlueprintCanvasContextValue = {
|
|
12
|
+
onSelectNodeRef: RefObject<((nodeId: string | null) => void) | undefined>;
|
|
13
|
+
onAbortClockRef: RefObject<((nodeId: string) => void) | undefined>;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
const BlueprintCanvasContext = createContext<BlueprintCanvasContextValue | null>(
|
|
17
|
+
null
|
|
18
|
+
);
|
|
19
|
+
|
|
20
|
+
export function BlueprintCanvasProvider({
|
|
21
|
+
onSelectNode,
|
|
22
|
+
onAbortClock,
|
|
23
|
+
children,
|
|
24
|
+
}: {
|
|
25
|
+
onSelectNode?: (nodeId: string | null) => void;
|
|
26
|
+
onAbortClock?: (nodeId: string) => void;
|
|
27
|
+
children: ReactNode;
|
|
28
|
+
}) {
|
|
29
|
+
const onSelectNodeRef = useRef(onSelectNode);
|
|
30
|
+
onSelectNodeRef.current = onSelectNode;
|
|
31
|
+
const onAbortClockRef = useRef(onAbortClock);
|
|
32
|
+
onAbortClockRef.current = onAbortClock;
|
|
33
|
+
|
|
34
|
+
return (
|
|
35
|
+
<BlueprintCanvasContext.Provider value={{ onSelectNodeRef, onAbortClockRef }}>
|
|
36
|
+
{children}
|
|
37
|
+
</BlueprintCanvasContext.Provider>
|
|
38
|
+
);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export function useBlueprintNodeSelect() {
|
|
42
|
+
const ctx = useContext(BlueprintCanvasContext);
|
|
43
|
+
return (nodeId: string) => {
|
|
44
|
+
ctx?.onSelectNodeRef.current?.(nodeId);
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export function useBlueprintClockAbort() {
|
|
49
|
+
const ctx = useContext(BlueprintCanvasContext);
|
|
50
|
+
return (nodeId: string) => {
|
|
51
|
+
ctx?.onAbortClockRef.current?.(nodeId);
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export function useClockNodeCanAbort(nodeId: string) {
|
|
56
|
+
return useSyncExternalStore(
|
|
57
|
+
subscribeActiveClockNodes,
|
|
58
|
+
() => isClockNodeActive(nodeId),
|
|
59
|
+
() => false
|
|
60
|
+
);
|
|
61
|
+
}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import { useEffect, useRef } from "react";
|
|
2
|
+
|
|
3
|
+
export type BlueprintContextMenuState =
|
|
4
|
+
| {
|
|
5
|
+
kind: "pane";
|
|
6
|
+
clientX: number;
|
|
7
|
+
clientY: number;
|
|
8
|
+
}
|
|
9
|
+
| {
|
|
10
|
+
kind: "node";
|
|
11
|
+
clientX: number;
|
|
12
|
+
clientY: number;
|
|
13
|
+
nodeId: string;
|
|
14
|
+
role: "blueprint" | "logic" | "and" | "lifecycle" | "fetch" | "json" | "clock";
|
|
15
|
+
}
|
|
16
|
+
| {
|
|
17
|
+
kind: "edge";
|
|
18
|
+
clientX: number;
|
|
19
|
+
clientY: number;
|
|
20
|
+
edgeId: string;
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
type BlueprintContextMenuProps = {
|
|
24
|
+
menu: BlueprintContextMenuState | null;
|
|
25
|
+
onClose: () => void;
|
|
26
|
+
onAddBlueprintNode: (clientX: number, clientY: number) => void;
|
|
27
|
+
onDeleteNode: (nodeId: string) => void;
|
|
28
|
+
onDeleteEdge: (edgeId: string) => void;
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
export function BlueprintContextMenu({
|
|
32
|
+
menu,
|
|
33
|
+
onClose,
|
|
34
|
+
onAddBlueprintNode,
|
|
35
|
+
onDeleteNode,
|
|
36
|
+
onDeleteEdge,
|
|
37
|
+
}: BlueprintContextMenuProps) {
|
|
38
|
+
const menuRef = useRef<HTMLDivElement>(null);
|
|
39
|
+
|
|
40
|
+
useEffect(() => {
|
|
41
|
+
if (!menu) return;
|
|
42
|
+
const closeOnPointerDown = (event: Event) => {
|
|
43
|
+
const target = event.target as globalThis.Node | null;
|
|
44
|
+
if (target && menuRef.current?.contains(target)) return;
|
|
45
|
+
onClose();
|
|
46
|
+
};
|
|
47
|
+
const close = () => onClose();
|
|
48
|
+
window.addEventListener("pointerdown", closeOnPointerDown);
|
|
49
|
+
window.addEventListener("scroll", close, true);
|
|
50
|
+
window.addEventListener("resize", close);
|
|
51
|
+
return () => {
|
|
52
|
+
window.removeEventListener("pointerdown", closeOnPointerDown);
|
|
53
|
+
window.removeEventListener("scroll", close, true);
|
|
54
|
+
window.removeEventListener("resize", close);
|
|
55
|
+
};
|
|
56
|
+
}, [menu, onClose]);
|
|
57
|
+
|
|
58
|
+
if (!menu) return null;
|
|
59
|
+
|
|
60
|
+
return (
|
|
61
|
+
<div
|
|
62
|
+
ref={menuRef}
|
|
63
|
+
className="bp-context-menu"
|
|
64
|
+
style={{ left: menu.clientX, top: menu.clientY }}
|
|
65
|
+
onMouseDown={(e) => e.stopPropagation()}
|
|
66
|
+
onContextMenu={(e) => e.preventDefault()}
|
|
67
|
+
>
|
|
68
|
+
{menu.kind === "pane" ? (
|
|
69
|
+
<button
|
|
70
|
+
type="button"
|
|
71
|
+
className="bp-context-menu__item"
|
|
72
|
+
onClick={() => {
|
|
73
|
+
onAddBlueprintNode(menu.clientX, menu.clientY);
|
|
74
|
+
onClose();
|
|
75
|
+
}}
|
|
76
|
+
>
|
|
77
|
+
新增节点
|
|
78
|
+
</button>
|
|
79
|
+
) : null}
|
|
80
|
+
|
|
81
|
+
{menu.kind === "node" ? (
|
|
82
|
+
<button
|
|
83
|
+
type="button"
|
|
84
|
+
className="bp-context-menu__item bp-context-menu__item--danger"
|
|
85
|
+
onClick={() => {
|
|
86
|
+
onDeleteNode(menu.nodeId);
|
|
87
|
+
onClose();
|
|
88
|
+
}}
|
|
89
|
+
>
|
|
90
|
+
删除节点
|
|
91
|
+
</button>
|
|
92
|
+
) : null}
|
|
93
|
+
|
|
94
|
+
{menu.kind === "edge" ? (
|
|
95
|
+
<button
|
|
96
|
+
type="button"
|
|
97
|
+
className="bp-context-menu__item bp-context-menu__item--danger"
|
|
98
|
+
onClick={() => {
|
|
99
|
+
onDeleteEdge(menu.edgeId);
|
|
100
|
+
onClose();
|
|
101
|
+
}}
|
|
102
|
+
>
|
|
103
|
+
删除连线
|
|
104
|
+
</button>
|
|
105
|
+
) : null}
|
|
106
|
+
</div>
|
|
107
|
+
);
|
|
108
|
+
}
|
|
@@ -0,0 +1,358 @@
|
|
|
1
|
+
import { useEffect, useMemo, type ChangeEvent } from "react";
|
|
2
|
+
import {
|
|
3
|
+
Input,
|
|
4
|
+
Select,
|
|
5
|
+
SelectContent,
|
|
6
|
+
SelectItem,
|
|
7
|
+
SelectTrigger,
|
|
8
|
+
SelectValue,
|
|
9
|
+
} from "@arronqzy/ui";
|
|
10
|
+
import {
|
|
11
|
+
LIFECYCLE_NODE_TYPE,
|
|
12
|
+
PAGE_LIFECYCLE_LABELS,
|
|
13
|
+
PAGE_LIFECYCLE_PHASES,
|
|
14
|
+
type PageLifecyclePhase,
|
|
15
|
+
} from "@arronqzy/blueprint-dsl";
|
|
16
|
+
|
|
17
|
+
import { FetchNodeConfigPanel } from "./components/FetchNodeConfigPanel";
|
|
18
|
+
import { ClockNodeConfigPanel } from "./components/ClockNodeConfigPanel";
|
|
19
|
+
import { JsonNodeConfigPanel } from "./components/JsonNodeConfigPanel";
|
|
20
|
+
import { LogicNodeConfigPanel } from "./components/LogicNodeConfigPanel";
|
|
21
|
+
import { ViewElementMultiSelect } from "./components/ViewElementMultiSelect";
|
|
22
|
+
import {
|
|
23
|
+
patchNodeConfigSource,
|
|
24
|
+
pruneViewElementIds,
|
|
25
|
+
resolveBlueprintConfigSource,
|
|
26
|
+
resolveViewElementIds,
|
|
27
|
+
type BlueprintConfigSource,
|
|
28
|
+
type BlueprintGraphNode,
|
|
29
|
+
} from "./graph/document";
|
|
30
|
+
|
|
31
|
+
export type BlueprintViewElementOption = {
|
|
32
|
+
id: string;
|
|
33
|
+
label: string;
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
export type BlueprintLibraryOption = {
|
|
37
|
+
id: string;
|
|
38
|
+
label: string;
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
export type BlueprintNodeConfigSidebarProps = {
|
|
42
|
+
node: BlueprintGraphNode;
|
|
43
|
+
viewElementOptions?: BlueprintViewElementOption[];
|
|
44
|
+
blueprintLibraryOptions?: BlueprintLibraryOption[];
|
|
45
|
+
allowFalseSignalPropagation?: boolean;
|
|
46
|
+
onUpdateAllowFalseSignalPropagation?: (value: boolean) => void;
|
|
47
|
+
onUpdateNode: (
|
|
48
|
+
nodeId: string,
|
|
49
|
+
patch: Partial<
|
|
50
|
+
Pick<
|
|
51
|
+
BlueprintGraphNode,
|
|
52
|
+
| "label"
|
|
53
|
+
| "role"
|
|
54
|
+
| "nodeType"
|
|
55
|
+
| "configSource"
|
|
56
|
+
| "viewElementId"
|
|
57
|
+
| "viewElementIds"
|
|
58
|
+
| "nestedBlueprintId"
|
|
59
|
+
| "libraryBlueprintId"
|
|
60
|
+
| "lifecyclePhase"
|
|
61
|
+
| "fetchConfig"
|
|
62
|
+
| "jsonConfig"
|
|
63
|
+
| "logicConfig"
|
|
64
|
+
| "clockConfig"
|
|
65
|
+
>
|
|
66
|
+
>
|
|
67
|
+
) => void;
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
export function BlueprintNodeConfigSidebar({
|
|
71
|
+
node,
|
|
72
|
+
viewElementOptions = [],
|
|
73
|
+
blueprintLibraryOptions = [],
|
|
74
|
+
allowFalseSignalPropagation = false,
|
|
75
|
+
onUpdateAllowFalseSignalPropagation,
|
|
76
|
+
onUpdateNode,
|
|
77
|
+
}: BlueprintNodeConfigSidebarProps) {
|
|
78
|
+
const configSource = resolveBlueprintConfigSource(node);
|
|
79
|
+
const linkedViewElementIds = resolveViewElementIds(node);
|
|
80
|
+
const existingViewElementIdSet = useMemo(
|
|
81
|
+
() => new Set(viewElementOptions.map((opt) => opt.id)),
|
|
82
|
+
[viewElementOptions]
|
|
83
|
+
);
|
|
84
|
+
const viewElementLabelById = new Map(
|
|
85
|
+
viewElementOptions.map((opt) => [opt.id, opt.label])
|
|
86
|
+
);
|
|
87
|
+
|
|
88
|
+
useEffect(() => {
|
|
89
|
+
if (configSource !== "view") return;
|
|
90
|
+
const linked = resolveViewElementIds(node);
|
|
91
|
+
if (linked.length === 0) return;
|
|
92
|
+
const pruned = pruneViewElementIds(linked, existingViewElementIdSet);
|
|
93
|
+
if (pruned.length === linked.length) return;
|
|
94
|
+
onUpdateNode(node.id, {
|
|
95
|
+
viewElementIds: pruned.length > 0 ? pruned : undefined,
|
|
96
|
+
viewElementId: undefined,
|
|
97
|
+
configSource: "view",
|
|
98
|
+
});
|
|
99
|
+
}, [
|
|
100
|
+
configSource,
|
|
101
|
+
existingViewElementIdSet,
|
|
102
|
+
node.id,
|
|
103
|
+
node.viewElementId,
|
|
104
|
+
node.viewElementIds,
|
|
105
|
+
onUpdateNode,
|
|
106
|
+
]);
|
|
107
|
+
|
|
108
|
+
return (
|
|
109
|
+
<div className="flex h-full min-h-0 flex-col overflow-hidden bg-background text-foreground">
|
|
110
|
+
<div className="shrink-0 border-b border-border px-3 py-2">
|
|
111
|
+
<div className="text-xs font-semibold">蓝图节点配置</div>
|
|
112
|
+
<div className="mt-0.5 text-[11px] text-muted-foreground">
|
|
113
|
+
{node.role === "blueprint"
|
|
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}
|
|
125
|
+
</div>
|
|
126
|
+
</div>
|
|
127
|
+
<div className="min-h-0 flex-1 space-y-3 overflow-auto p-3 text-xs">
|
|
128
|
+
<label className="block space-y-1">
|
|
129
|
+
<span className="text-muted-foreground">节点名称</span>
|
|
130
|
+
<Input
|
|
131
|
+
value={node.label}
|
|
132
|
+
onChange={(e: ChangeEvent<HTMLInputElement>) =>
|
|
133
|
+
onUpdateNode(node.id, { label: e.target.value })
|
|
134
|
+
}
|
|
135
|
+
className="h-8"
|
|
136
|
+
/>
|
|
137
|
+
</label>
|
|
138
|
+
|
|
139
|
+
<label className="block space-y-1">
|
|
140
|
+
<span className="text-muted-foreground">配置类型</span>
|
|
141
|
+
<Select
|
|
142
|
+
value={configSource}
|
|
143
|
+
onValueChange={(value: string) => {
|
|
144
|
+
const nextSource = value as BlueprintConfigSource;
|
|
145
|
+
onUpdateNode(node.id, {
|
|
146
|
+
...patchNodeConfigSource(node, nextSource),
|
|
147
|
+
...(nextSource === "view"
|
|
148
|
+
? {
|
|
149
|
+
viewElementIds: linkedViewElementIds,
|
|
150
|
+
viewElementId: undefined,
|
|
151
|
+
}
|
|
152
|
+
: {
|
|
153
|
+
viewElementIds: undefined,
|
|
154
|
+
viewElementId: undefined,
|
|
155
|
+
}),
|
|
156
|
+
});
|
|
157
|
+
}}
|
|
158
|
+
>
|
|
159
|
+
<SelectTrigger className="h-8">
|
|
160
|
+
<SelectValue />
|
|
161
|
+
</SelectTrigger>
|
|
162
|
+
<SelectContent>
|
|
163
|
+
<SelectItem value="blueprint">蓝图配置</SelectItem>
|
|
164
|
+
<SelectItem value="logic">逻辑配置</SelectItem>
|
|
165
|
+
<SelectItem value="and">并运算</SelectItem>
|
|
166
|
+
<SelectItem value="lifecycle">生命周期配置</SelectItem>
|
|
167
|
+
<SelectItem value="fetch">数据源获取</SelectItem>
|
|
168
|
+
<SelectItem value="json">JSON 节点</SelectItem>
|
|
169
|
+
<SelectItem value="clock">时钟</SelectItem>
|
|
170
|
+
<SelectItem value="view">视图节点配置</SelectItem>
|
|
171
|
+
</SelectContent>
|
|
172
|
+
</Select>
|
|
173
|
+
</label>
|
|
174
|
+
|
|
175
|
+
{configSource === "view" ? (
|
|
176
|
+
<div className="block space-y-1">
|
|
177
|
+
<span className="text-muted-foreground">关联视图节点</span>
|
|
178
|
+
<ViewElementMultiSelect
|
|
179
|
+
options={viewElementOptions}
|
|
180
|
+
value={linkedViewElementIds}
|
|
181
|
+
placeholder="选择视图节点"
|
|
182
|
+
onChange={(next) =>
|
|
183
|
+
onUpdateNode(node.id, {
|
|
184
|
+
viewElementIds: next,
|
|
185
|
+
viewElementId: undefined,
|
|
186
|
+
configSource: "view",
|
|
187
|
+
})
|
|
188
|
+
}
|
|
189
|
+
/>
|
|
190
|
+
{linkedViewElementIds.length === 0 ? (
|
|
191
|
+
<p className="text-[11px] text-muted-foreground">
|
|
192
|
+
可多选视图画布节点;关联后仍在此配置蓝图节点,视图属性请在视图面板中编辑。
|
|
193
|
+
</p>
|
|
194
|
+
) : (
|
|
195
|
+
<p className="text-[11px] text-muted-foreground">
|
|
196
|
+
已关联 {linkedViewElementIds.length} 个视图节点:
|
|
197
|
+
{linkedViewElementIds
|
|
198
|
+
.map((id) => viewElementLabelById.get(id) ?? id)
|
|
199
|
+
.join("、")}
|
|
200
|
+
。
|
|
201
|
+
</p>
|
|
202
|
+
)}
|
|
203
|
+
</div>
|
|
204
|
+
) : null}
|
|
205
|
+
|
|
206
|
+
{configSource === "blueprint" ? (
|
|
207
|
+
<div className="space-y-2 rounded-md border border-border/70 bg-muted/20 p-2.5">
|
|
208
|
+
<div className="font-medium text-foreground">蓝图属性</div>
|
|
209
|
+
<p className="text-[11px] text-muted-foreground">
|
|
210
|
+
选中蓝图库中的蓝图后,当输入端收到<strong>真信号</strong>
|
|
211
|
+
时才会执行该蓝图;执行完成后从输出端发出
|
|
212
|
+
<strong>真信号</strong>(含嵌套蓝图输出值与当前节点信息),执行失败则发出
|
|
213
|
+
<strong>假信号</strong>。
|
|
214
|
+
</p>
|
|
215
|
+
<label className="block space-y-1">
|
|
216
|
+
<span className="text-muted-foreground">引用蓝图库</span>
|
|
217
|
+
<Select
|
|
218
|
+
value={node.libraryBlueprintId ?? "__none__"}
|
|
219
|
+
onValueChange={(value: string) => {
|
|
220
|
+
onUpdateNode(node.id, {
|
|
221
|
+
libraryBlueprintId: value === "__none__" ? undefined : value,
|
|
222
|
+
configSource: "blueprint",
|
|
223
|
+
});
|
|
224
|
+
}}
|
|
225
|
+
>
|
|
226
|
+
<SelectTrigger className="h-8">
|
|
227
|
+
<SelectValue placeholder="选择蓝图库中的蓝图" />
|
|
228
|
+
</SelectTrigger>
|
|
229
|
+
<SelectContent className="z-[10100]">
|
|
230
|
+
<SelectItem value="__none__">未关联</SelectItem>
|
|
231
|
+
{blueprintLibraryOptions.map((opt) => (
|
|
232
|
+
<SelectItem key={opt.id} value={opt.id}>
|
|
233
|
+
{opt.label}
|
|
234
|
+
</SelectItem>
|
|
235
|
+
))}
|
|
236
|
+
</SelectContent>
|
|
237
|
+
</Select>
|
|
238
|
+
</label>
|
|
239
|
+
{!node.libraryBlueprintId ? (
|
|
240
|
+
<p className="text-[11px] text-muted-foreground">
|
|
241
|
+
请先从蓝图库选择要引用的蓝图。
|
|
242
|
+
</p>
|
|
243
|
+
) : null}
|
|
244
|
+
<label className="flex items-start gap-2 pt-1">
|
|
245
|
+
<input
|
|
246
|
+
type="checkbox"
|
|
247
|
+
checked={allowFalseSignalPropagation}
|
|
248
|
+
onChange={(e) =>
|
|
249
|
+
onUpdateAllowFalseSignalPropagation?.(e.target.checked)
|
|
250
|
+
}
|
|
251
|
+
className="mt-0.5 h-3.5 w-3.5 shrink-0 rounded border border-input"
|
|
252
|
+
/>
|
|
253
|
+
<span className="text-[11px] leading-relaxed text-muted-foreground">
|
|
254
|
+
允许假信号传递:开启后,节点输出假信号时不会阻塞任务链,错误信息会继续向下游传递。
|
|
255
|
+
</span>
|
|
256
|
+
</label>
|
|
257
|
+
</div>
|
|
258
|
+
) : null}
|
|
259
|
+
|
|
260
|
+
{configSource === "lifecycle" ? (
|
|
261
|
+
<div className="space-y-2 rounded-md border border-border/70 bg-muted/20 p-2.5">
|
|
262
|
+
<div className="font-medium text-foreground">生命周期钩子</div>
|
|
263
|
+
<p className="text-[11px] text-muted-foreground">
|
|
264
|
+
生命周期节点<strong>没有输入口</strong>,仅右侧输出口。
|
|
265
|
+
{node.lifecyclePhase === "blueprintActivated"
|
|
266
|
+
? "当本蓝图被其他蓝图的蓝图配置节点引用且收到真信号时,自动向下游发出真信号,输出值为父级传入的输入数据。"
|
|
267
|
+
: "当页面进入对应生命周期时,自动向下游发出真/假信号。"}
|
|
268
|
+
</p>
|
|
269
|
+
<label className="block space-y-1">
|
|
270
|
+
<span className="text-muted-foreground">监听阶段</span>
|
|
271
|
+
<Select
|
|
272
|
+
value={node.lifecyclePhase ?? "mounted"}
|
|
273
|
+
onValueChange={(value: string) =>
|
|
274
|
+
onUpdateNode(node.id, {
|
|
275
|
+
role: "lifecycle",
|
|
276
|
+
nodeType: LIFECYCLE_NODE_TYPE,
|
|
277
|
+
lifecyclePhase: value as PageLifecyclePhase,
|
|
278
|
+
configSource: "lifecycle",
|
|
279
|
+
})
|
|
280
|
+
}
|
|
281
|
+
>
|
|
282
|
+
<SelectTrigger className="h-8">
|
|
283
|
+
<SelectValue />
|
|
284
|
+
</SelectTrigger>
|
|
285
|
+
<SelectContent>
|
|
286
|
+
{PAGE_LIFECYCLE_PHASES.map((phase) => (
|
|
287
|
+
<SelectItem key={phase} value={phase}>
|
|
288
|
+
{PAGE_LIFECYCLE_LABELS[phase]}
|
|
289
|
+
</SelectItem>
|
|
290
|
+
))}
|
|
291
|
+
</SelectContent>
|
|
292
|
+
</Select>
|
|
293
|
+
</label>
|
|
294
|
+
{node.parentId ? (
|
|
295
|
+
<p className="text-[11px] text-muted-foreground">
|
|
296
|
+
所属蓝图节点:{node.parentId}
|
|
297
|
+
</p>
|
|
298
|
+
) : null}
|
|
299
|
+
</div>
|
|
300
|
+
) : null}
|
|
301
|
+
|
|
302
|
+
{configSource === "fetch" ? (
|
|
303
|
+
<FetchNodeConfigPanel node={node} onUpdateNode={onUpdateNode} />
|
|
304
|
+
) : null}
|
|
305
|
+
|
|
306
|
+
{configSource === "json" ? (
|
|
307
|
+
<JsonNodeConfigPanel node={node} onUpdateNode={onUpdateNode} />
|
|
308
|
+
) : null}
|
|
309
|
+
|
|
310
|
+
{configSource === "clock" ? (
|
|
311
|
+
<ClockNodeConfigPanel node={node} onUpdateNode={onUpdateNode} />
|
|
312
|
+
) : null}
|
|
313
|
+
|
|
314
|
+
{configSource === "and" ? (
|
|
315
|
+
<div className="space-y-2 rounded-md border border-border/70 bg-muted/20 p-2.5">
|
|
316
|
+
<div className="font-medium text-foreground">并运算</div>
|
|
317
|
+
<p className="text-[11px] text-muted-foreground">
|
|
318
|
+
左侧两个输入口 <strong>inA</strong>、<strong>inB</strong>。
|
|
319
|
+
每个输入口可连 n 条线,同端口任一为真则该端口视为真(或)。
|
|
320
|
+
仅当 <strong>inA 与 inB 均为真信号</strong> 时,从输出口发出真信号;
|
|
321
|
+
否则发出假信号。
|
|
322
|
+
</p>
|
|
323
|
+
</div>
|
|
324
|
+
) : null}
|
|
325
|
+
|
|
326
|
+
{configSource === "logic" ? (
|
|
327
|
+
<>
|
|
328
|
+
<LogicNodeConfigPanel node={node} onUpdateNode={onUpdateNode} />
|
|
329
|
+
{node.parentId ? (
|
|
330
|
+
<p className="text-[11px] text-muted-foreground">
|
|
331
|
+
所属蓝图节点:{node.parentId}
|
|
332
|
+
</p>
|
|
333
|
+
) : null}
|
|
334
|
+
</>
|
|
335
|
+
) : null}
|
|
336
|
+
|
|
337
|
+
{configSource !== "blueprint" && configSource !== "and" ? (
|
|
338
|
+
<div className="space-y-2 rounded-md border border-border/70 bg-muted/20 p-2.5">
|
|
339
|
+
<div className="font-medium text-foreground">任务链执行</div>
|
|
340
|
+
<label className="flex items-start gap-2">
|
|
341
|
+
<input
|
|
342
|
+
type="checkbox"
|
|
343
|
+
checked={allowFalseSignalPropagation}
|
|
344
|
+
onChange={(e) =>
|
|
345
|
+
onUpdateAllowFalseSignalPropagation?.(e.target.checked)
|
|
346
|
+
}
|
|
347
|
+
className="mt-0.5 h-3.5 w-3.5 shrink-0 rounded border border-input"
|
|
348
|
+
/>
|
|
349
|
+
<span className="text-[11px] leading-relaxed text-muted-foreground">
|
|
350
|
+
允许假信号传递:默认假信号会阻塞任务链;开启后继续向下游传递假信号与错误信息。
|
|
351
|
+
</span>
|
|
352
|
+
</label>
|
|
353
|
+
</div>
|
|
354
|
+
) : null}
|
|
355
|
+
</div>
|
|
356
|
+
</div>
|
|
357
|
+
);
|
|
358
|
+
}
|