From 5f04277badd0008bbf75b7401a5f85aac962b7ba Mon Sep 17 00:00:00 2001 From: Fu Diwei Date: Mon, 25 Aug 2025 19:53:23 +0800 Subject: [PATCH] feat: import/export workflow graph --- internal/workflow/engine/state.go | 1 - ui/package-lock.json | 694 +++++++++--------- ui/package.json | 29 +- ui/src/components/ModalForm.tsx | 2 +- ui/src/components/access/AccessEditDrawer.tsx | 2 +- .../certificate/CertificateDetailDrawer.tsx | 2 +- .../workflow/WorkflowGraphExportBox.tsx | 143 ++++ .../workflow/WorkflowGraphExportModal.tsx | 95 +++ .../workflow/WorkflowGraphImportInputBox.tsx | 249 +++++++ .../workflow/WorkflowGraphImportModal.tsx | 119 +++ .../workflow/WorkflowRunDetailDrawer.tsx | 2 +- .../workflow/designer/NodeDrawer.tsx | 2 +- ... => BranchBlockNodeConfigExprInputBox.tsx} | 17 +- .../forms/BranchBlockNodeConfigForm.tsx | 8 +- .../workflow/designer/forms/_shared.tsx | 7 +- ui/src/domain/workflow.ts | 1 + ui/src/i18n/locales/en/nls.common.json | 4 +- ui/src/i18n/locales/en/nls.workflow.json | 18 + ui/src/i18n/locales/zh/nls.common.json | 4 +- ui/src/i18n/locales/zh/nls.workflow.json | 18 + ui/src/pages/workflows/WorkflowDetail.tsx | 3 +- .../pages/workflows/WorkflowDetailDesign.tsx | 47 +- ui/src/utils/validators.ts | 21 +- 23 files changed, 1081 insertions(+), 407 deletions(-) create mode 100644 ui/src/components/workflow/WorkflowGraphExportBox.tsx create mode 100644 ui/src/components/workflow/WorkflowGraphExportModal.tsx create mode 100644 ui/src/components/workflow/WorkflowGraphImportInputBox.tsx create mode 100644 ui/src/components/workflow/WorkflowGraphImportModal.tsx rename ui/src/components/workflow/designer/forms/{BranchBlockNodeConfigFormExpressionEditor.tsx => BranchBlockNodeConfigExprInputBox.tsx} (96%) diff --git a/internal/workflow/engine/state.go b/internal/workflow/engine/state.go index ee4e92816..5b3b282e7 100644 --- a/internal/workflow/engine/state.go +++ b/internal/workflow/engine/state.go @@ -273,7 +273,6 @@ func newInOutManager() InOutManager { const ( stateIOTypeRef = "ref" - stateIOType = "recordRef" ) const ( diff --git a/ui/package-lock.json b/ui/package-lock.json index ea3764c85..69c285c91 100644 --- a/ui/package-lock.json +++ b/ui/package-lock.json @@ -11,20 +11,20 @@ "@codemirror/lang-yaml": "^6.1.2", "@codemirror/language": "^6.11.3", "@codemirror/legacy-modes": "^6.5.1", - "@flowgram.ai/document": "^0.4.0", - "@flowgram.ai/fixed-layout-editor": "^0.4.0", - "@flowgram.ai/minimap-plugin": "^0.4.0", + "@flowgram.ai/document": "0.4.3", + "@flowgram.ai/fixed-layout-editor": "0.4.3", + "@flowgram.ai/minimap-plugin": "0.4.3", "@tabler/icons-react": "^3.34.1", "@uiw/codemirror-extensions-basic-setup": "^4.25.1", "@uiw/codemirror-theme-vscode": "^4.25.1", "@uiw/react-codemirror": "^4.25.1", - "ahooks": "^3.9.0", - "antd": "^5.27.0", + "ahooks": "^3.9.4", + "antd": "^5.27.1", "antd-zod": "^7.0.0", "clsx": "^2.1.1", "cron-parser": "^5.3.0", "file-saver": "^2.0.5", - "i18next": "^25.3.6", + "i18next": "^25.4.2", "i18next-browser-languagedetector": "^8.2.0", "immer": "^10.1.1", "nanoid": "^5.1.5", @@ -33,14 +33,15 @@ "react": "^18.3.1", "react-copy-to-clipboard": "^5.1.0", "react-dom": "^18.3.1", - "react-i18next": "^15.6.1", - "react-router-dom": "^7.8.1", + "react-i18next": "^15.7.2", + "react-router-dom": "^7.8.2", "tailwind-merge": "^3.3.1", - "zod": "^4.0.17", - "zustand": "^5.0.7" + "yaml": "^2.8.1", + "zod": "^4.1.1", + "zustand": "^5.0.8" }, "devDependencies": { - "@eslint/js": "^9.33.0", + "@eslint/js": "^9.34.0", "@tailwindcss/postcss": "^4.1.12", "@tailwindcss/vite": "^4.1.12", "@types/file-saver": "^2.0.7", @@ -51,10 +52,10 @@ "@types/react-dom": "^18.3.7", "@vitejs/plugin-legacy": "^6.1.1", "@vitejs/plugin-react": "^4.7.0", - "eslint": "^9.33.0", + "eslint": "^9.34.0", "eslint-config-prettier": "^10.1.8", "eslint-import-resolver-typescript": "^4.4.4", - "eslint-plugin-better-tailwindcss": "^3.7.4", + "eslint-plugin-better-tailwindcss": "^3.7.5", "eslint-plugin-import": "^2.32.0", "eslint-plugin-prettier": "^5.5.4", "eslint-plugin-react-hooks": "^5.2.0", @@ -63,7 +64,7 @@ "prettier": "^3.6.2", "tailwindcss": "^4.1.12", "typescript": "^5.8.3", - "typescript-eslint": "^8.39.1", + "typescript-eslint": "^8.40.0", "vite": "^6.3.5" } }, @@ -2374,9 +2375,9 @@ } }, "node_modules/@eslint/js": { - "version": "9.33.0", - "resolved": "https://registry.npmmirror.com/@eslint/js/-/js-9.33.0.tgz", - "integrity": "sha512-5K1/mKhWaMfreBGJTwval43JJmkip0RmM+3+IuqupeSKNC/Th2Kc7ucaq5ovTSra/OOKB9c58CGSz3QMVbWt0A==", + "version": "9.34.0", + "resolved": "https://registry.npmmirror.com/@eslint/js/-/js-9.34.0.tgz", + "integrity": "sha512-EoyvqQnBNsV1CWaEJ559rxXL4c8V92gxirbawSmVUOWXlsRxxQXl6LmCpdUblgxgSkDIqKnhzba2SjRTI/A5Rw==", "dev": true, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -2408,12 +2409,12 @@ } }, "node_modules/@flowgram.ai/background-plugin": { - "version": "0.4.0", - "resolved": "https://registry.npmmirror.com/@flowgram.ai/background-plugin/-/background-plugin-0.4.0.tgz", - "integrity": "sha512-aHyIZ0O87L2CLrEUZQf3MyWDyHqpjchg9CUpZETUFYIeAXGP0Uk0/31C3slLPKdsYFGuW5y54E0ighII0tbTBQ==", + "version": "0.4.3", + "resolved": "https://registry.npmmirror.com/@flowgram.ai/background-plugin/-/background-plugin-0.4.3.tgz", + "integrity": "sha512-evXup2fikpqMfzi8WUfRwadahQjauLtcO2MRSy7aqgkjx4cd5nE8Y2FWLZNEi1EKOz1PeDigVZqsWXKoB+icwg==", "dependencies": { - "@flowgram.ai/core": "0.4.0", - "@flowgram.ai/utils": "0.4.0", + "@flowgram.ai/core": "0.4.3", + "@flowgram.ai/utils": "0.4.3", "inversify": "^6.0.1", "reflect-metadata": "~0.2.2" }, @@ -2423,11 +2424,11 @@ } }, "node_modules/@flowgram.ai/command": { - "version": "0.4.0", - "resolved": "https://registry.npmmirror.com/@flowgram.ai/command/-/command-0.4.0.tgz", - "integrity": "sha512-sRro1cAG84qIvx04DzN79Wv1Ldv2CtUEKL1TjG6URQ0wYPEcF89mZF5vUG3U4sU9bXBYvfa9hSsJpSOofDaF8Q==", + "version": "0.4.3", + "resolved": "https://registry.npmmirror.com/@flowgram.ai/command/-/command-0.4.3.tgz", + "integrity": "sha512-Wua7WFd6Kdd3Mu5En6WXeokj16PWBmIKBnxz4wLIxtFl8AQekLj0Cx4hliLmia1yJI+M9RsCSqRPnbIcI05qNw==", "dependencies": { - "@flowgram.ai/utils": "0.4.0", + "@flowgram.ai/utils": "0.4.3", "inversify": "^6.0.1", "reflect-metadata": "~0.2.2" }, @@ -2437,12 +2438,12 @@ } }, "node_modules/@flowgram.ai/core": { - "version": "0.4.0", - "resolved": "https://registry.npmmirror.com/@flowgram.ai/core/-/core-0.4.0.tgz", - "integrity": "sha512-/dgEGYJt/hQNYBViRXdv8zoUorm8vXhH3n/1p1W9xdM076mkTjIfesJHEFU+LLy4/ZZVH+G200O76XqGGK5zog==", + "version": "0.4.3", + "resolved": "https://registry.npmmirror.com/@flowgram.ai/core/-/core-0.4.3.tgz", + "integrity": "sha512-ttkg9lk4PIH9cUpoPqJYvlCo4uyRja3484B+Ml6/P6wPM33cpvbbHVC6LqgLhtg760fLqhTHRPNw5qTMk3bk7w==", "dependencies": { - "@flowgram.ai/command": "0.4.0", - "@flowgram.ai/utils": "0.4.0", + "@flowgram.ai/command": "0.4.3", + "@flowgram.ai/utils": "0.4.3", "@phosphor/messaging": "^1.3.0", "@tweenjs/tween.js": "^18", "clsx": "^1.1.1", @@ -2482,12 +2483,12 @@ } }, "node_modules/@flowgram.ai/document": { - "version": "0.4.0", - "resolved": "https://registry.npmmirror.com/@flowgram.ai/document/-/document-0.4.0.tgz", - "integrity": "sha512-LXy3gTGusK+e4qJxRl1DhfwGS6p5rQnRJq/XFXhHMnEnE/fwG6ZSJOsFDcVnNdU8Z5pwW7393FwNfyqIZoabIA==", + "version": "0.4.3", + "resolved": "https://registry.npmmirror.com/@flowgram.ai/document/-/document-0.4.3.tgz", + "integrity": "sha512-vvnm+FHOt9b36vQJGnFa6US3oJTvVUrK5HzYG4ujb7NwEzBWb80+ELK/m0U8VB+aFPPwlFZBgsBIrMAgq/xrgg==", "dependencies": { - "@flowgram.ai/core": "0.4.0", - "@flowgram.ai/utils": "0.4.0", + "@flowgram.ai/core": "0.4.3", + "@flowgram.ai/utils": "0.4.3", "inversify": "^6.0.1", "lodash": "^4.17.21", "nanoid": "^4.0.2", @@ -2512,29 +2513,29 @@ } }, "node_modules/@flowgram.ai/editor": { - "version": "0.4.0", - "resolved": "https://registry.npmmirror.com/@flowgram.ai/editor/-/editor-0.4.0.tgz", - "integrity": "sha512-/ATStbs+jtwMErOv/7XMbsLs6yjvb8r9t8xcwfhOrqYGGEgWpaIX1jadifcIyr1T6V1yjZdiWTUIjZvVqyPm+A==", + "version": "0.4.3", + "resolved": "https://registry.npmmirror.com/@flowgram.ai/editor/-/editor-0.4.3.tgz", + "integrity": "sha512-LJ9Z2Mk8xuFdHnSHw/pQ7oPYEqMmKArQld3rb7C6o6RJjBxTKkwnm542wmVBGL24Nv50M9AbwDQaUPKJQkgqpA==", "dependencies": { - "@flowgram.ai/background-plugin": "0.4.0", - "@flowgram.ai/core": "0.4.0", - "@flowgram.ai/document": "0.4.0", - "@flowgram.ai/form": "0.4.0", - "@flowgram.ai/form-core": "0.4.0", - "@flowgram.ai/group-plugin": "0.4.0", - "@flowgram.ai/history": "0.4.0", - "@flowgram.ai/history-node-plugin": "0.4.0", - "@flowgram.ai/i18n-plugin": "0.4.0", - "@flowgram.ai/materials-plugin": "0.4.0", - "@flowgram.ai/node": "0.4.0", - "@flowgram.ai/node-core-plugin": "0.4.0", - "@flowgram.ai/node-variable-plugin": "0.4.0", - "@flowgram.ai/playground-react": "0.4.0", - "@flowgram.ai/redux-devtool-plugin": "0.4.0", - "@flowgram.ai/renderer": "0.4.0", - "@flowgram.ai/shortcuts-plugin": "0.4.0", - "@flowgram.ai/utils": "0.4.0", - "@flowgram.ai/variable-plugin": "0.4.0", + "@flowgram.ai/background-plugin": "0.4.3", + "@flowgram.ai/core": "0.4.3", + "@flowgram.ai/document": "0.4.3", + "@flowgram.ai/form": "0.4.3", + "@flowgram.ai/form-core": "0.4.3", + "@flowgram.ai/group-plugin": "0.4.3", + "@flowgram.ai/history": "0.4.3", + "@flowgram.ai/history-node-plugin": "0.4.3", + "@flowgram.ai/i18n-plugin": "0.4.3", + "@flowgram.ai/materials-plugin": "0.4.3", + "@flowgram.ai/node": "0.4.3", + "@flowgram.ai/node-core-plugin": "0.4.3", + "@flowgram.ai/node-variable-plugin": "0.4.3", + "@flowgram.ai/playground-react": "0.4.3", + "@flowgram.ai/redux-devtool-plugin": "0.4.3", + "@flowgram.ai/renderer": "0.4.3", + "@flowgram.ai/shortcuts-plugin": "0.4.3", + "@flowgram.ai/utils": "0.4.3", + "@flowgram.ai/variable-plugin": "0.4.3", "inversify": "^6.0.1", "reflect-metadata": "~0.2.2" }, @@ -2544,13 +2545,13 @@ } }, "node_modules/@flowgram.ai/fixed-drag-plugin": { - "version": "0.4.0", - "resolved": "https://registry.npmmirror.com/@flowgram.ai/fixed-drag-plugin/-/fixed-drag-plugin-0.4.0.tgz", - "integrity": "sha512-Wl1CPlY4vTDFU5FBdyVcSWh+WpKTSk21PztErmqKseFgkSJAFDfKFyNIy/Fq0WzpIJoNLi3Zf6FGCySWk8+d7g==", + "version": "0.4.3", + "resolved": "https://registry.npmmirror.com/@flowgram.ai/fixed-drag-plugin/-/fixed-drag-plugin-0.4.3.tgz", + "integrity": "sha512-uoIrbAatETrTI1BIhtrQGSpIIvuqsa8qax6WGoOspUbATqZxSwoMTrJfEkOOqBwdAGsVBdjm68sQcG7U40WBEA==", "dependencies": { - "@flowgram.ai/core": "0.4.0", - "@flowgram.ai/document": "0.4.0", - "@flowgram.ai/renderer": "0.4.0", + "@flowgram.ai/core": "0.4.3", + "@flowgram.ai/document": "0.4.3", + "@flowgram.ai/renderer": "0.4.3", "inversify": "^6.0.1", "reflect-metadata": "~0.2.2" }, @@ -2560,29 +2561,29 @@ } }, "node_modules/@flowgram.ai/fixed-history-plugin": { - "version": "0.4.0", - "resolved": "https://registry.npmmirror.com/@flowgram.ai/fixed-history-plugin/-/fixed-history-plugin-0.4.0.tgz", - "integrity": "sha512-1QMe3A28loPHOEurOW+vhSXffVJ1D4uZHp/g46IlOCxRY4WHvL4np9PegAd65rvG1D+Wud237k+AIJAM8/LbvQ==", + "version": "0.4.3", + "resolved": "https://registry.npmmirror.com/@flowgram.ai/fixed-history-plugin/-/fixed-history-plugin-0.4.3.tgz", + "integrity": "sha512-rCHwUGYyckan8D+G21e/m5aFJ7DaQW+kZAcciaxRG15OMTFxhk/uFz2AqLj6VnkW/D9uV3g4hIoGgbf4dy+sxg==", "dependencies": { - "@flowgram.ai/core": "0.4.0", - "@flowgram.ai/document": "0.4.0", - "@flowgram.ai/form-core": "0.4.0", - "@flowgram.ai/history": "0.4.0", - "@flowgram.ai/utils": "0.4.0", + "@flowgram.ai/core": "0.4.3", + "@flowgram.ai/document": "0.4.3", + "@flowgram.ai/form-core": "0.4.3", + "@flowgram.ai/history": "0.4.3", + "@flowgram.ai/utils": "0.4.3", "inversify": "^6.0.1", "lodash": "^4.17.21", "reflect-metadata": "~0.2.2" } }, "node_modules/@flowgram.ai/fixed-layout-core": { - "version": "0.4.0", - "resolved": "https://registry.npmmirror.com/@flowgram.ai/fixed-layout-core/-/fixed-layout-core-0.4.0.tgz", - "integrity": "sha512-sc6IQHlYIq6xpiQvEved8e1OUaIU+hkCUQPWRt8t6AvmCzahjLcHso34p7IdOoDT0bbXHWTUzLQk6bgRF+wyEQ==", + "version": "0.4.3", + "resolved": "https://registry.npmmirror.com/@flowgram.ai/fixed-layout-core/-/fixed-layout-core-0.4.3.tgz", + "integrity": "sha512-uT8di5eVKjv9cPcrye1c5hlczJ+sA3zf5PVkrvSHaq2o9837gVzoPUoASLPKYXB6ztUWsMe2/qzcWcQmvJibfQ==", "dependencies": { - "@flowgram.ai/core": "0.4.0", - "@flowgram.ai/document": "0.4.0", - "@flowgram.ai/renderer": "0.4.0", - "@flowgram.ai/utils": "0.4.0", + "@flowgram.ai/core": "0.4.3", + "@flowgram.ai/document": "0.4.3", + "@flowgram.ai/renderer": "0.4.3", + "@flowgram.ai/utils": "0.4.3", "inversify": "^6.0.1", "lodash": "^4.17.21", "reflect-metadata": "~0.2.2" @@ -2593,19 +2594,19 @@ } }, "node_modules/@flowgram.ai/fixed-layout-editor": { - "version": "0.4.0", - "resolved": "https://registry.npmmirror.com/@flowgram.ai/fixed-layout-editor/-/fixed-layout-editor-0.4.0.tgz", - "integrity": "sha512-sxq9JM0GwB4EVRP8B2f84gZXpUgFoswcVgMBt+CdxecZV5gJzURmKl6Xe6Fh3y+7tOoWEo0e5/mK4MElLSeYeA==", + "version": "0.4.3", + "resolved": "https://registry.npmmirror.com/@flowgram.ai/fixed-layout-editor/-/fixed-layout-editor-0.4.3.tgz", + "integrity": "sha512-L/8NqrCOdnln2oRImWgcWfblEI9GxzS+XuuYoULnkApWXk6l7OGAZiR3d+UACy7jGzhaFX5lcq8fzE2XuKhSpw==", "dependencies": { - "@flowgram.ai/core": "0.4.0", - "@flowgram.ai/editor": "0.4.0", - "@flowgram.ai/fixed-drag-plugin": "0.4.0", - "@flowgram.ai/fixed-history-plugin": "0.4.0", - "@flowgram.ai/fixed-layout-core": "0.4.0", - "@flowgram.ai/history": "0.4.0", - "@flowgram.ai/reactive": "0.4.0", - "@flowgram.ai/select-box-plugin": "0.4.0", - "@flowgram.ai/utils": "0.4.0", + "@flowgram.ai/core": "0.4.3", + "@flowgram.ai/editor": "0.4.3", + "@flowgram.ai/fixed-drag-plugin": "0.4.3", + "@flowgram.ai/fixed-history-plugin": "0.4.3", + "@flowgram.ai/fixed-layout-core": "0.4.3", + "@flowgram.ai/history": "0.4.3", + "@flowgram.ai/reactive": "0.4.3", + "@flowgram.ai/select-box-plugin": "0.4.3", + "@flowgram.ai/utils": "0.4.3", "inversify": "^6.0.1", "reflect-metadata": "~0.2.2" }, @@ -2615,12 +2616,12 @@ } }, "node_modules/@flowgram.ai/form": { - "version": "0.4.0", - "resolved": "https://registry.npmmirror.com/@flowgram.ai/form/-/form-0.4.0.tgz", - "integrity": "sha512-DU4n7RY4v+WYwPHd0IIFUS7c/kqWBF+/G7p4ocWOP/PR+SCo1vwNHDK9NHWYxXmQdt2JWrM9l55HKGHUiOPYdA==", + "version": "0.4.3", + "resolved": "https://registry.npmmirror.com/@flowgram.ai/form/-/form-0.4.3.tgz", + "integrity": "sha512-YYfG9DiWEDxseA4hFkzx2TKzo69uMmff3CxMXjrlVDYVTL0at24ZNBqV+/Hs6pee8FwMXK4257JgZax5zKDZ4A==", "dependencies": { - "@flowgram.ai/reactive": "0.4.0", - "@flowgram.ai/utils": "0.4.0", + "@flowgram.ai/reactive": "0.4.3", + "@flowgram.ai/utils": "0.4.3", "fast-equals": "^2.0.0", "lodash": "^4.17.21", "nanoid": "^4.0.2" @@ -2631,13 +2632,13 @@ } }, "node_modules/@flowgram.ai/form-core": { - "version": "0.4.0", - "resolved": "https://registry.npmmirror.com/@flowgram.ai/form-core/-/form-core-0.4.0.tgz", - "integrity": "sha512-xoS0T5PGkwtvQfs9Xf+7rLVmIZzOkeMBWHN+ERRUQMFRjquZnkEeBrkIB2ZBG0i/Uh0dq0Lq8b+vjsRFpemj+Q==", + "version": "0.4.3", + "resolved": "https://registry.npmmirror.com/@flowgram.ai/form-core/-/form-core-0.4.3.tgz", + "integrity": "sha512-FmPuWiY/0PSZJv3yZMs/377O5jcJP9M4kg5/ks63M1N1WnABsbJNyspp6mJbm7edvpiUiiqWIeywyCCtYW5rTw==", "dependencies": { - "@flowgram.ai/core": "0.4.0", - "@flowgram.ai/document": "0.4.0", - "@flowgram.ai/utils": "0.4.0", + "@flowgram.ai/core": "0.4.3", + "@flowgram.ai/document": "0.4.3", + "@flowgram.ai/utils": "0.4.3", "inversify": "^6.0.1", "lodash": "^4.17.21", "reflect-metadata": "~0.2.2" @@ -2665,16 +2666,16 @@ } }, "node_modules/@flowgram.ai/free-layout-core": { - "version": "0.4.0", - "resolved": "https://registry.npmmirror.com/@flowgram.ai/free-layout-core/-/free-layout-core-0.4.0.tgz", - "integrity": "sha512-wPa2lB32rHI6SS7BWHpE7OU6mk0hlpcpbW50d5oBekAMHu1ygOLH2oKEDaWAae2wJmdfQqMrTMWmBgXos5epLw==", + "version": "0.4.3", + "resolved": "https://registry.npmmirror.com/@flowgram.ai/free-layout-core/-/free-layout-core-0.4.3.tgz", + "integrity": "sha512-mYEQuxeeSvkbUZfHo/XIDjR7zSunX8MN1uTVG3lO0dDtxNRblzkhDLc/RV1YoqX2P+8ybwXNtQvFXjhS80GfiA==", "dependencies": { - "@flowgram.ai/core": "0.4.0", - "@flowgram.ai/document": "0.4.0", - "@flowgram.ai/form-core": "0.4.0", - "@flowgram.ai/node": "0.4.0", - "@flowgram.ai/reactive": "0.4.0", - "@flowgram.ai/utils": "0.4.0", + "@flowgram.ai/core": "0.4.3", + "@flowgram.ai/document": "0.4.3", + "@flowgram.ai/form-core": "0.4.3", + "@flowgram.ai/node": "0.4.3", + "@flowgram.ai/reactive": "0.4.3", + "@flowgram.ai/utils": "0.4.3", "inversify": "^6.0.1", "lodash-es": "^4.17.21", "nanoid": "^4.0.2", @@ -2703,14 +2704,14 @@ } }, "node_modules/@flowgram.ai/group-plugin": { - "version": "0.4.0", - "resolved": "https://registry.npmmirror.com/@flowgram.ai/group-plugin/-/group-plugin-0.4.0.tgz", - "integrity": "sha512-Sqcf4uTINRiQ/k9uBcI4WjAoIpHa3XH+M+LgUzRNYDTLN90iSk7YN3/7+hXkqTyg4cgQt+pyv8rG7HRneEm2jA==", + "version": "0.4.3", + "resolved": "https://registry.npmmirror.com/@flowgram.ai/group-plugin/-/group-plugin-0.4.3.tgz", + "integrity": "sha512-jPMr3fKbJBnozJ8PI36ouxJltR6EajstWCe4GXpvsxJjTa1flFfs7KEzDb/odrweO7LlcF6Wx7Ibhni5WRQ7+A==", "dependencies": { - "@flowgram.ai/core": "0.4.0", - "@flowgram.ai/document": "0.4.0", - "@flowgram.ai/renderer": "0.4.0", - "@flowgram.ai/utils": "0.4.0", + "@flowgram.ai/core": "0.4.3", + "@flowgram.ai/document": "0.4.3", + "@flowgram.ai/renderer": "0.4.3", + "@flowgram.ai/utils": "0.4.3", "inversify": "^6.0.1", "reflect-metadata": "~0.2.2" }, @@ -2720,12 +2721,12 @@ } }, "node_modules/@flowgram.ai/history": { - "version": "0.4.0", - "resolved": "https://registry.npmmirror.com/@flowgram.ai/history/-/history-0.4.0.tgz", - "integrity": "sha512-XTKkakg5X+64fnBCSSYSaY276z3F11DWNHcvSD7aKG+DcM4f219dFh4Uy78ceHlEVB1Z7IqWJexCtDVOtoIl1A==", + "version": "0.4.3", + "resolved": "https://registry.npmmirror.com/@flowgram.ai/history/-/history-0.4.3.tgz", + "integrity": "sha512-1ED2+FrulnonH5FXTpzg9C0iWSC5/nc6fP4+GJLe9WhcVNA6mlIG9D4Gtqjf0x/M+IQm0Vr6ewS73CTY4+KnjQ==", "dependencies": { - "@flowgram.ai/core": "0.4.0", - "@flowgram.ai/utils": "0.4.0", + "@flowgram.ai/core": "0.4.3", + "@flowgram.ai/utils": "0.4.3", "inversify": "^6.0.1", "lodash": "^4.17.21", "nanoid": "^4.0.2", @@ -2733,17 +2734,17 @@ } }, "node_modules/@flowgram.ai/history-node-plugin": { - "version": "0.4.0", - "resolved": "https://registry.npmmirror.com/@flowgram.ai/history-node-plugin/-/history-node-plugin-0.4.0.tgz", - "integrity": "sha512-rsFmJJGBDg2ZYLFD8SKYoQ1YpJ+apNGsqmc4krGefDJXW8QAFEm5KKBt5Yr+PvxNgz1QmGB+sguPHqnMoSPcCA==", + "version": "0.4.3", + "resolved": "https://registry.npmmirror.com/@flowgram.ai/history-node-plugin/-/history-node-plugin-0.4.3.tgz", + "integrity": "sha512-gSpdFbZeyvf39Cl4KEKwx9ft6E+0fW0V+kPSA+Wk0ruSdTBSWxpXCqYD2wUt74LkDuy5xcun5EhP0kQtaVIO4g==", "dependencies": { - "@flowgram.ai/core": "0.4.0", - "@flowgram.ai/document": "0.4.0", - "@flowgram.ai/form": "0.4.0", - "@flowgram.ai/form-core": "0.4.0", - "@flowgram.ai/history": "0.4.0", - "@flowgram.ai/node": "0.4.0", - "@flowgram.ai/utils": "0.4.0", + "@flowgram.ai/core": "0.4.3", + "@flowgram.ai/document": "0.4.3", + "@flowgram.ai/form": "0.4.3", + "@flowgram.ai/form-core": "0.4.3", + "@flowgram.ai/history": "0.4.3", + "@flowgram.ai/node": "0.4.3", + "@flowgram.ai/utils": "0.4.3", "inversify": "^6.0.1", "lodash": "^4.17.21", "reflect-metadata": "~0.2.2" @@ -2772,32 +2773,32 @@ } }, "node_modules/@flowgram.ai/i18n": { - "version": "0.4.0", - "resolved": "https://registry.npmmirror.com/@flowgram.ai/i18n/-/i18n-0.4.0.tgz", - "integrity": "sha512-TMSeRy9Ds5Ha5qorkICPRr1hGEiGPx7UwEXrR4JF7bYsSbMAGZ74BXdHt8dxsaFLgPamtNAvvH6ifJP/6V+wLg==", + "version": "0.4.3", + "resolved": "https://registry.npmmirror.com/@flowgram.ai/i18n/-/i18n-0.4.3.tgz", + "integrity": "sha512-Dh69c29OI1QG7sRtVEpzKFFTIKSMDevt5SLg1o9eZGfqZQ+16zGaRnYeAz0ycnII4JWflORe7LZJtyS72wbI0A==", "dependencies": { - "@flowgram.ai/utils": "0.4.0", + "@flowgram.ai/utils": "0.4.3", "i18n-js": "^4.5.1" } }, "node_modules/@flowgram.ai/i18n-plugin": { - "version": "0.4.0", - "resolved": "https://registry.npmmirror.com/@flowgram.ai/i18n-plugin/-/i18n-plugin-0.4.0.tgz", - "integrity": "sha512-aeQt/c5LbsdL5OVsUQxVbUu40K0M1sUalnqrgY9iaGYrr0mYlXPGL0iXp44MBJPgxsbfLfo7HGbP7FEq3U8CbA==", + "version": "0.4.3", + "resolved": "https://registry.npmmirror.com/@flowgram.ai/i18n-plugin/-/i18n-plugin-0.4.3.tgz", + "integrity": "sha512-n41NdNxeixqYw1oT6Jl5PMFHGHE6mcLFctbWZ3UKiZ4769WnaqrhRfglyxp/bHCsiLHp3z2+xMgCtuQAfl1DCg==", "dependencies": { - "@flowgram.ai/core": "0.4.0", - "@flowgram.ai/i18n": "0.4.0" + "@flowgram.ai/core": "0.4.3", + "@flowgram.ai/i18n": "0.4.3" } }, "node_modules/@flowgram.ai/materials-plugin": { - "version": "0.4.0", - "resolved": "https://registry.npmmirror.com/@flowgram.ai/materials-plugin/-/materials-plugin-0.4.0.tgz", - "integrity": "sha512-TYkWIu/iZ2aQDfXQWdRxEvRE5DHQonMlB3t9YshdAvyrg7N5sCVTjrw+3mqgm5tX8NoCEAQ4YDDQ7lEY5+wjrA==", + "version": "0.4.3", + "resolved": "https://registry.npmmirror.com/@flowgram.ai/materials-plugin/-/materials-plugin-0.4.3.tgz", + "integrity": "sha512-+RgUAFxMxfNERJistMfMgqs+wwvHGdQwuFZU0JpoTiKTV8wnIR5HQ0/pak3EbZOfQRxhm7Akp8iGMHHTTNs/wA==", "dependencies": { - "@flowgram.ai/core": "0.4.0", - "@flowgram.ai/form-core": "0.4.0", - "@flowgram.ai/renderer": "0.4.0", - "@flowgram.ai/utils": "0.4.0", + "@flowgram.ai/core": "0.4.3", + "@flowgram.ai/form-core": "0.4.3", + "@flowgram.ai/renderer": "0.4.3", + "@flowgram.ai/utils": "0.4.3", "inversify": "^6.0.1", "reflect-metadata": "~0.2.2" }, @@ -2808,14 +2809,14 @@ } }, "node_modules/@flowgram.ai/minimap-plugin": { - "version": "0.4.0", - "resolved": "https://registry.npmmirror.com/@flowgram.ai/minimap-plugin/-/minimap-plugin-0.4.0.tgz", - "integrity": "sha512-0icvzo0hWBQksLZO9Fs2F/wbRoox14Q3gHkIjaCsPIkLJi9YFSYxfQcmxdeq6Jm6p6mACiBrK7pfWKxcDl3ZoA==", + "version": "0.4.3", + "resolved": "https://registry.npmmirror.com/@flowgram.ai/minimap-plugin/-/minimap-plugin-0.4.3.tgz", + "integrity": "sha512-WVlzxQ+oNYGj7OV270C0SFRAQoTiYh7aV1dnCIDZj8NvKG/OGNXP6y28bD6atDSlIKK2EQ7deua/oIROXJIxcg==", "dependencies": { - "@flowgram.ai/core": "0.4.0", - "@flowgram.ai/document": "0.4.0", - "@flowgram.ai/renderer": "0.4.0", - "@flowgram.ai/utils": "0.4.0", + "@flowgram.ai/core": "0.4.3", + "@flowgram.ai/document": "0.4.3", + "@flowgram.ai/renderer": "0.4.3", + "@flowgram.ai/utils": "0.4.3", "inversify": "^6.0.1", "lodash": "^4.17.21", "reflect-metadata": "~0.2.2" @@ -2827,15 +2828,15 @@ } }, "node_modules/@flowgram.ai/node": { - "version": "0.4.0", - "resolved": "https://registry.npmmirror.com/@flowgram.ai/node/-/node-0.4.0.tgz", - "integrity": "sha512-y0GK95LKtZelLicyLO1kkYtpSij2qJVsewAJ4ASSqAKrB3Ukqx7Q5hM66p8nXGLcsE/0r2o2tmBiWf7Pk5u6lQ==", + "version": "0.4.3", + "resolved": "https://registry.npmmirror.com/@flowgram.ai/node/-/node-0.4.3.tgz", + "integrity": "sha512-B6ex6A9nhjf/Q1Vz6lsIFzToNaupPrFLZ6hxmwEfkGny+3DBmG/4Wvr/JKi3vdSRC0jwPM62FQe0mBrMSCG9qA==", "dependencies": { - "@flowgram.ai/core": "0.4.0", - "@flowgram.ai/document": "0.4.0", - "@flowgram.ai/form": "0.4.0", - "@flowgram.ai/form-core": "0.4.0", - "@flowgram.ai/utils": "0.4.0", + "@flowgram.ai/core": "0.4.3", + "@flowgram.ai/document": "0.4.3", + "@flowgram.ai/form": "0.4.3", + "@flowgram.ai/form-core": "0.4.3", + "@flowgram.ai/utils": "0.4.3", "inversify": "^6.0.1", "lodash": "^4.17.21", "nanoid": "^4.0.2", @@ -2847,16 +2848,16 @@ } }, "node_modules/@flowgram.ai/node-core-plugin": { - "version": "0.4.0", - "resolved": "https://registry.npmmirror.com/@flowgram.ai/node-core-plugin/-/node-core-plugin-0.4.0.tgz", - "integrity": "sha512-9HSrjvGQRu3k/IzmXz06XrerDuTZ8ZqDfMhdFXmV5eXm0aLhUH1SqDVcTDOT1d0lyoDjZKmBShgrBGG7ECDhMQ==", + "version": "0.4.3", + "resolved": "https://registry.npmmirror.com/@flowgram.ai/node-core-plugin/-/node-core-plugin-0.4.3.tgz", + "integrity": "sha512-ISvfuPnuBbFbG6NE2eTbLAQO8D37ck9sOyEDIOEyRf/41nO88HIFhXMyA2beAAZODXXUlaVnvUwSRC5TiWnb8g==", "dependencies": { - "@flowgram.ai/core": "0.4.0", - "@flowgram.ai/document": "0.4.0", - "@flowgram.ai/form-core": "0.4.0", - "@flowgram.ai/node": "0.4.0", - "@flowgram.ai/renderer": "0.4.0", - "@flowgram.ai/utils": "0.4.0", + "@flowgram.ai/core": "0.4.3", + "@flowgram.ai/document": "0.4.3", + "@flowgram.ai/form-core": "0.4.3", + "@flowgram.ai/node": "0.4.3", + "@flowgram.ai/renderer": "0.4.3", + "@flowgram.ai/utils": "0.4.3", "inversify": "^6.0.1", "reflect-metadata": "~0.2.2" }, @@ -2867,16 +2868,16 @@ } }, "node_modules/@flowgram.ai/node-variable-plugin": { - "version": "0.4.0", - "resolved": "https://registry.npmmirror.com/@flowgram.ai/node-variable-plugin/-/node-variable-plugin-0.4.0.tgz", - "integrity": "sha512-2Am/R0xRxhG4TDRgcTAMwp0yRI7SvK2vRgFMvEe6mjmnAZJooD19scyPkt7/TYlFmdCNGLasgFDhF7ORZ8qUtg==", + "version": "0.4.3", + "resolved": "https://registry.npmmirror.com/@flowgram.ai/node-variable-plugin/-/node-variable-plugin-0.4.3.tgz", + "integrity": "sha512-VYNex4a5NNULXGoiMOJMzu64eTjKJjx/krfsyKq53yngODNtezKbJ1nsJD8b9NT2haPW4R6xWY27Fy/Mji/oIg==", "dependencies": { - "@flowgram.ai/core": "0.4.0", - "@flowgram.ai/document": "0.4.0", - "@flowgram.ai/form-core": "0.4.0", - "@flowgram.ai/node": "0.4.0", - "@flowgram.ai/utils": "0.4.0", - "@flowgram.ai/variable-plugin": "0.4.0", + "@flowgram.ai/core": "0.4.3", + "@flowgram.ai/document": "0.4.3", + "@flowgram.ai/form-core": "0.4.3", + "@flowgram.ai/node": "0.4.3", + "@flowgram.ai/utils": "0.4.3", + "@flowgram.ai/variable-plugin": "0.4.3", "inversify": "^6.0.1", "lodash": "^4.17.21", "reflect-metadata": "~0.2.2" @@ -2905,14 +2906,14 @@ } }, "node_modules/@flowgram.ai/playground-react": { - "version": "0.4.0", - "resolved": "https://registry.npmmirror.com/@flowgram.ai/playground-react/-/playground-react-0.4.0.tgz", - "integrity": "sha512-8B6vfIRdPcfAj8WbYBeIC+PJS34QH44BF5Xfux21KqhcqYjyjn+S46KYo6WuGi4MMIx8MUheSivyKc4kKbhW+Q==", + "version": "0.4.3", + "resolved": "https://registry.npmmirror.com/@flowgram.ai/playground-react/-/playground-react-0.4.3.tgz", + "integrity": "sha512-qPbjegMfZAvtuI633dD46ZL0GoZD3573Hom4LnUF/Impa7Wvn290PSBr3M3oRZ3yorCX2UQKUhmgopkn2Tk2mg==", "dependencies": { - "@flowgram.ai/background-plugin": "0.4.0", - "@flowgram.ai/core": "0.4.0", - "@flowgram.ai/shortcuts-plugin": "0.4.0", - "@flowgram.ai/utils": "0.4.0", + "@flowgram.ai/background-plugin": "0.4.3", + "@flowgram.ai/core": "0.4.3", + "@flowgram.ai/shortcuts-plugin": "0.4.3", + "@flowgram.ai/utils": "0.4.3", "inversify": "^6.0.1", "reflect-metadata": "~0.2.2" }, @@ -2922,11 +2923,11 @@ } }, "node_modules/@flowgram.ai/reactive": { - "version": "0.4.0", - "resolved": "https://registry.npmmirror.com/@flowgram.ai/reactive/-/reactive-0.4.0.tgz", - "integrity": "sha512-wdMQCyEYS+hCHWo3zWEmUjJl1lJylf4oVbn/f6LTRTqZPSNqL02k1o5xVHo/L43GhbhRZzyK2/JCaGpowzF4Kg==", + "version": "0.4.3", + "resolved": "https://registry.npmmirror.com/@flowgram.ai/reactive/-/reactive-0.4.3.tgz", + "integrity": "sha512-1OUduNKzkHpQkRIkS0s58GXJ8HTj38wB2odcBjKnPSHovGCGTldHVz/AQmsqtKth4FpMRPMZOJ3sdtbGhY4mdQ==", "dependencies": { - "@flowgram.ai/utils": "0.4.0" + "@flowgram.ai/utils": "0.4.3" }, "peerDependencies": { "react": ">=16.8", @@ -2934,12 +2935,12 @@ } }, "node_modules/@flowgram.ai/redux-devtool-plugin": { - "version": "0.4.0", - "resolved": "https://registry.npmmirror.com/@flowgram.ai/redux-devtool-plugin/-/redux-devtool-plugin-0.4.0.tgz", - "integrity": "sha512-EDNpr6fLXT/OnoUkjanv2Cs0vzU93NLXtUMxYa1JWwm3mjXGZIiVeIve1FGfarMzJcnky3xv3CjhzZnLxd0rkg==", + "version": "0.4.3", + "resolved": "https://registry.npmmirror.com/@flowgram.ai/redux-devtool-plugin/-/redux-devtool-plugin-0.4.3.tgz", + "integrity": "sha512-K+fuKV1t3UTtJQx6sk7Z1LsWFhLQsLOR2zLz8msXtCis2AsIj6tEZYMT1YOli3PwKJosgcmZFW24qY8pwU9RDQ==", "dependencies": { - "@flowgram.ai/core": "0.4.0", - "@flowgram.ai/variable-core": "0.4.0", + "@flowgram.ai/core": "0.4.3", + "@flowgram.ai/variable-core": "0.4.3", "inversify": "^6.0.1", "reflect-metadata": "~0.2.2", "styled-components": "^5" @@ -2994,14 +2995,14 @@ } }, "node_modules/@flowgram.ai/renderer": { - "version": "0.4.0", - "resolved": "https://registry.npmmirror.com/@flowgram.ai/renderer/-/renderer-0.4.0.tgz", - "integrity": "sha512-Qhpsh6tE8x+9ZTo1/rrCakaPIHgKCcX/8QE1ovHxHH9gRsxknihPHXA6t1sKRE0vyXcS6ZT5MxJ2zoAAY/vYjA==", + "version": "0.4.3", + "resolved": "https://registry.npmmirror.com/@flowgram.ai/renderer/-/renderer-0.4.3.tgz", + "integrity": "sha512-xvPbSLGhL2EXBUc0fV0nME5+VzBTtYlykWkxB30tiURcN5e/tsTbomO4/sh3MmO+pGHzsFfJsVMSStdgW5BzCg==", "dependencies": { - "@flowgram.ai/core": "0.4.0", - "@flowgram.ai/document": "0.4.0", - "@flowgram.ai/i18n": "0.4.0", - "@flowgram.ai/utils": "0.4.0", + "@flowgram.ai/core": "0.4.3", + "@flowgram.ai/document": "0.4.3", + "@flowgram.ai/i18n": "0.4.3", + "@flowgram.ai/utils": "0.4.3", "inversify": "^6.0.1", "lodash": "^4.17.21", "reflect-metadata": "~0.2.2" @@ -3012,12 +3013,12 @@ } }, "node_modules/@flowgram.ai/select-box-plugin": { - "version": "0.4.0", - "resolved": "https://registry.npmmirror.com/@flowgram.ai/select-box-plugin/-/select-box-plugin-0.4.0.tgz", - "integrity": "sha512-p9donmLCrw1JSo1gLKetzBJvE6huIgXI+fVjkHzR4/WKACDXQ7IsILW63ZNprfHGvvhaLanKWhAoPTabEfsWEg==", + "version": "0.4.3", + "resolved": "https://registry.npmmirror.com/@flowgram.ai/select-box-plugin/-/select-box-plugin-0.4.3.tgz", + "integrity": "sha512-kfx2CXDq+1yNq835MieVXRh4sqO4lUtHLRhknDJqt5nscijqEONuWTbgczkxSznjHrh7xYZbK0FrE4Qm5yXv3A==", "dependencies": { - "@flowgram.ai/core": "0.4.0", - "@flowgram.ai/renderer": "0.4.0", + "@flowgram.ai/core": "0.4.3", + "@flowgram.ai/renderer": "0.4.3", "inversify": "^6.0.1", "reflect-metadata": "~0.2.2" }, @@ -3027,13 +3028,13 @@ } }, "node_modules/@flowgram.ai/shortcuts-plugin": { - "version": "0.4.0", - "resolved": "https://registry.npmmirror.com/@flowgram.ai/shortcuts-plugin/-/shortcuts-plugin-0.4.0.tgz", - "integrity": "sha512-paU8n4/ZW1qHK3SIJkFXdeD/gcwkQmcJaJOhTIsfRAmKftF3TJ/YBunld53vAaHnvZ7nxHzrcofg1oYxWpPCmQ==", + "version": "0.4.3", + "resolved": "https://registry.npmmirror.com/@flowgram.ai/shortcuts-plugin/-/shortcuts-plugin-0.4.3.tgz", + "integrity": "sha512-k4zCR0LsvyB/eUoRll9d8YkWcwzJ/8sGyNel+oNOdge3qohmNS466U6bs0LhVvyPZnm0ZHEwN9VVlT9A9ErYwg==", "dependencies": { - "@flowgram.ai/core": "0.4.0", - "@flowgram.ai/document": "0.4.0", - "@flowgram.ai/utils": "0.4.0", + "@flowgram.ai/core": "0.4.3", + "@flowgram.ai/document": "0.4.3", + "@flowgram.ai/utils": "0.4.3", "inversify": "^6.0.1", "reflect-metadata": "~0.2.2", "styled-components": "^5" @@ -3088,9 +3089,9 @@ } }, "node_modules/@flowgram.ai/utils": { - "version": "0.4.0", - "resolved": "https://registry.npmmirror.com/@flowgram.ai/utils/-/utils-0.4.0.tgz", - "integrity": "sha512-WqfXPJCD3wElHBMHfPd8PjKWAZ7WYDpC/l74oCNMttSWGCQbgnnNUTu+Iux6Ifv33wn75tFnh+XbUEnnHPv8HA==", + "version": "0.4.3", + "resolved": "https://registry.npmmirror.com/@flowgram.ai/utils/-/utils-0.4.3.tgz", + "integrity": "sha512-BOeB39OrkmhdRVXbnpt8d+sdq9GbY8yBiz7VZvuuV9u96OmU9FxX1kImSbT9JBkSpHHfzsTuXUq/5MRyENlNlw==", "dependencies": { "clsx": "^1.1.1", "inversify": "^6.0.1", @@ -3128,12 +3129,12 @@ } }, "node_modules/@flowgram.ai/variable-core": { - "version": "0.4.0", - "resolved": "https://registry.npmmirror.com/@flowgram.ai/variable-core/-/variable-core-0.4.0.tgz", - "integrity": "sha512-LcjZw1oTibottQpq/C5+FtYAJz/GVZg1nQ0p+f2v91LFqm7LyTO0sVISDqtUbt++OWeGWdolMhhpFXgVgOw26Q==", + "version": "0.4.3", + "resolved": "https://registry.npmmirror.com/@flowgram.ai/variable-core/-/variable-core-0.4.3.tgz", + "integrity": "sha512-6umtNMGgUVpu/kxWYhaX1ZAr/iasESSwi07XZvWoIbu/iyCRdFLUURuMkVXC8gnQoqbDMIUJCobLT8qO9t2eWg==", "dependencies": { - "@flowgram.ai/core": "0.4.0", - "@flowgram.ai/utils": "0.4.0", + "@flowgram.ai/core": "0.4.3", + "@flowgram.ai/utils": "0.4.3", "fast-equals": "^2.0.0", "inversify": "^6.0.1", "lodash": "^4.17.21", @@ -3164,15 +3165,15 @@ } }, "node_modules/@flowgram.ai/variable-layout": { - "version": "0.4.0", - "resolved": "https://registry.npmmirror.com/@flowgram.ai/variable-layout/-/variable-layout-0.4.0.tgz", - "integrity": "sha512-TIZvr7hzzt5B/m0YAHhtLQIZQ87tXbzqegQv82n0oTjOWMXd9Mis/4GWkoQC94BsPBoDPPzYG45WZ3QDIulBng==", + "version": "0.4.3", + "resolved": "https://registry.npmmirror.com/@flowgram.ai/variable-layout/-/variable-layout-0.4.3.tgz", + "integrity": "sha512-a6vG1B6WeVaUrI/7TbNW7//z3IwubHIxqa3KOGCIdE9W2VX5KU/Y/CjxglXgWQdBnjRlstzi6iS6yw7y6Ic/tw==", "dependencies": { - "@flowgram.ai/core": "0.4.0", - "@flowgram.ai/document": "0.4.0", - "@flowgram.ai/free-layout-core": "0.4.0", - "@flowgram.ai/utils": "0.4.0", - "@flowgram.ai/variable-core": "0.4.0", + "@flowgram.ai/core": "0.4.3", + "@flowgram.ai/document": "0.4.3", + "@flowgram.ai/free-layout-core": "0.4.3", + "@flowgram.ai/utils": "0.4.3", + "@flowgram.ai/variable-core": "0.4.3", "inversify": "^6.0.1", "reflect-metadata": "~0.2.2", "styled-components": "^5" @@ -3227,16 +3228,16 @@ } }, "node_modules/@flowgram.ai/variable-plugin": { - "version": "0.4.0", - "resolved": "https://registry.npmmirror.com/@flowgram.ai/variable-plugin/-/variable-plugin-0.4.0.tgz", - "integrity": "sha512-N46yICT7oGHjZ0T821xOs+hlwW9/pdUK7qNXbSLSZL7gX4HoXCREQSKWVtqfpfMkwTnOeIKQ2VudN7gzXrrVMw==", + "version": "0.4.3", + "resolved": "https://registry.npmmirror.com/@flowgram.ai/variable-plugin/-/variable-plugin-0.4.3.tgz", + "integrity": "sha512-VnfRs0ehgSn6D77+EA6snzkPtWqJMDrpWc5mbSrWw5HgZZ3qbij5NQBL89yME7mp5brakAVX/DPW+BTu5NVmCA==", "dependencies": { - "@flowgram.ai/core": "0.4.0", - "@flowgram.ai/document": "0.4.0", - "@flowgram.ai/free-layout-core": "0.4.0", - "@flowgram.ai/utils": "0.4.0", - "@flowgram.ai/variable-core": "0.4.0", - "@flowgram.ai/variable-layout": "0.4.0", + "@flowgram.ai/core": "0.4.3", + "@flowgram.ai/document": "0.4.3", + "@flowgram.ai/free-layout-core": "0.4.3", + "@flowgram.ai/utils": "0.4.3", + "@flowgram.ai/variable-core": "0.4.3", + "@flowgram.ai/variable-layout": "0.4.3", "inversify": "^6.0.1", "reflect-metadata": "~0.2.2", "styled-components": "^5" @@ -4420,16 +4421,16 @@ "peer": true }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.39.1", - "resolved": "https://registry.npmmirror.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.39.1.tgz", - "integrity": "sha512-yYegZ5n3Yr6eOcqgj2nJH8cH/ZZgF+l0YIdKILSDjYFRjgYQMgv/lRjV5Z7Up04b9VYUondt8EPMqg7kTWgJ2g==", + "version": "8.40.0", + "resolved": "https://registry.npmmirror.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.40.0.tgz", + "integrity": "sha512-w/EboPlBwnmOBtRbiOvzjD+wdiZdgFeo17lkltrtn7X37vagKKWJABvyfsJXTlHe6XBzugmYgd4A4nW+k8Mixw==", "dev": true, "dependencies": { "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "8.39.1", - "@typescript-eslint/type-utils": "8.39.1", - "@typescript-eslint/utils": "8.39.1", - "@typescript-eslint/visitor-keys": "8.39.1", + "@typescript-eslint/scope-manager": "8.40.0", + "@typescript-eslint/type-utils": "8.40.0", + "@typescript-eslint/utils": "8.40.0", + "@typescript-eslint/visitor-keys": "8.40.0", "graphemer": "^1.4.0", "ignore": "^7.0.0", "natural-compare": "^1.4.0", @@ -4443,7 +4444,7 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "@typescript-eslint/parser": "^8.39.1", + "@typescript-eslint/parser": "^8.40.0", "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } @@ -4458,15 +4459,15 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "8.39.1", - "resolved": "https://registry.npmmirror.com/@typescript-eslint/parser/-/parser-8.39.1.tgz", - "integrity": "sha512-pUXGCuHnnKw6PyYq93lLRiZm3vjuslIy7tus1lIQTYVK9bL8XBgJnCWm8a0KcTtHC84Yya1Q6rtll+duSMj0dg==", + "version": "8.40.0", + "resolved": "https://registry.npmmirror.com/@typescript-eslint/parser/-/parser-8.40.0.tgz", + "integrity": "sha512-jCNyAuXx8dr5KJMkecGmZ8KI61KBUhkCob+SD+C+I5+Y1FWI2Y3QmY4/cxMCC5WAsZqoEtEETVhUiUMIGCf6Bw==", "dev": true, "dependencies": { - "@typescript-eslint/scope-manager": "8.39.1", - "@typescript-eslint/types": "8.39.1", - "@typescript-eslint/typescript-estree": "8.39.1", - "@typescript-eslint/visitor-keys": "8.39.1", + "@typescript-eslint/scope-manager": "8.40.0", + "@typescript-eslint/types": "8.40.0", + "@typescript-eslint/typescript-estree": "8.40.0", + "@typescript-eslint/visitor-keys": "8.40.0", "debug": "^4.3.4" }, "engines": { @@ -4482,13 +4483,13 @@ } }, "node_modules/@typescript-eslint/project-service": { - "version": "8.39.1", - "resolved": "https://registry.npmmirror.com/@typescript-eslint/project-service/-/project-service-8.39.1.tgz", - "integrity": "sha512-8fZxek3ONTwBu9ptw5nCKqZOSkXshZB7uAxuFF0J/wTMkKydjXCzqqga7MlFMpHi9DoG4BadhmTkITBcg8Aybw==", + "version": "8.40.0", + "resolved": "https://registry.npmmirror.com/@typescript-eslint/project-service/-/project-service-8.40.0.tgz", + "integrity": "sha512-/A89vz7Wf5DEXsGVvcGdYKbVM9F7DyFXj52lNYUDS1L9yJfqjW/fIp5PgMuEJL/KeqVTe2QSbXAGUZljDUpArw==", "dev": true, "dependencies": { - "@typescript-eslint/tsconfig-utils": "^8.39.1", - "@typescript-eslint/types": "^8.39.1", + "@typescript-eslint/tsconfig-utils": "^8.40.0", + "@typescript-eslint/types": "^8.40.0", "debug": "^4.3.4" }, "engines": { @@ -4503,13 +4504,13 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.39.1", - "resolved": "https://registry.npmmirror.com/@typescript-eslint/scope-manager/-/scope-manager-8.39.1.tgz", - "integrity": "sha512-RkBKGBrjgskFGWuyUGz/EtD8AF/GW49S21J8dvMzpJitOF1slLEbbHnNEtAHtnDAnx8qDEdRrULRnWVx27wGBw==", + "version": "8.40.0", + "resolved": "https://registry.npmmirror.com/@typescript-eslint/scope-manager/-/scope-manager-8.40.0.tgz", + "integrity": "sha512-y9ObStCcdCiZKzwqsE8CcpyuVMwRouJbbSrNuThDpv16dFAj429IkM6LNb1dZ2m7hK5fHyzNcErZf7CEeKXR4w==", "dev": true, "dependencies": { - "@typescript-eslint/types": "8.39.1", - "@typescript-eslint/visitor-keys": "8.39.1" + "@typescript-eslint/types": "8.40.0", + "@typescript-eslint/visitor-keys": "8.40.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -4520,9 +4521,9 @@ } }, "node_modules/@typescript-eslint/tsconfig-utils": { - "version": "8.39.1", - "resolved": "https://registry.npmmirror.com/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.39.1.tgz", - "integrity": "sha512-ePUPGVtTMR8XMU2Hee8kD0Pu4NDE1CN9Q1sxGSGd/mbOtGZDM7pnhXNJnzW63zk/q+Z54zVzj44HtwXln5CvHA==", + "version": "8.40.0", + "resolved": "https://registry.npmmirror.com/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.40.0.tgz", + "integrity": "sha512-jtMytmUaG9d/9kqSl/W3E3xaWESo4hFDxAIHGVW/WKKtQhesnRIJSAJO6XckluuJ6KDB5woD1EiqknriCtAmcw==", "dev": true, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -4536,14 +4537,14 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.39.1", - "resolved": "https://registry.npmmirror.com/@typescript-eslint/type-utils/-/type-utils-8.39.1.tgz", - "integrity": "sha512-gu9/ahyatyAdQbKeHnhT4R+y3YLtqqHyvkfDxaBYk97EcbfChSJXyaJnIL3ygUv7OuZatePHmQvuH5ru0lnVeA==", + "version": "8.40.0", + "resolved": "https://registry.npmmirror.com/@typescript-eslint/type-utils/-/type-utils-8.40.0.tgz", + "integrity": "sha512-eE60cK4KzAc6ZrzlJnflXdrMqOBaugeukWICO2rB0KNvwdIMaEaYiywwHMzA1qFpTxrLhN9Lp4E/00EgWcD3Ow==", "dev": true, "dependencies": { - "@typescript-eslint/types": "8.39.1", - "@typescript-eslint/typescript-estree": "8.39.1", - "@typescript-eslint/utils": "8.39.1", + "@typescript-eslint/types": "8.40.0", + "@typescript-eslint/typescript-estree": "8.40.0", + "@typescript-eslint/utils": "8.40.0", "debug": "^4.3.4", "ts-api-utils": "^2.1.0" }, @@ -4560,9 +4561,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "8.39.1", - "resolved": "https://registry.npmmirror.com/@typescript-eslint/types/-/types-8.39.1.tgz", - "integrity": "sha512-7sPDKQQp+S11laqTrhHqeAbsCfMkwJMrV7oTDvtDds4mEofJYir414bYKUEb8YPUm9QL3U+8f6L6YExSoAGdQw==", + "version": "8.40.0", + "resolved": "https://registry.npmmirror.com/@typescript-eslint/types/-/types-8.40.0.tgz", + "integrity": "sha512-ETdbFlgbAmXHyFPwqUIYrfc12ArvpBhEVgGAxVYSwli26dn8Ko+lIo4Su9vI9ykTZdJn+vJprs/0eZU0YMAEQg==", "dev": true, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -4573,15 +4574,15 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.39.1", - "resolved": "https://registry.npmmirror.com/@typescript-eslint/typescript-estree/-/typescript-estree-8.39.1.tgz", - "integrity": "sha512-EKkpcPuIux48dddVDXyQBlKdeTPMmALqBUbEk38McWv0qVEZwOpVJBi7ugK5qVNgeuYjGNQxrrnoM/5+TI/BPw==", + "version": "8.40.0", + "resolved": "https://registry.npmmirror.com/@typescript-eslint/typescript-estree/-/typescript-estree-8.40.0.tgz", + "integrity": "sha512-k1z9+GJReVVOkc1WfVKs1vBrR5MIKKbdAjDTPvIK3L8De6KbFfPFt6BKpdkdk7rZS2GtC/m6yI5MYX+UsuvVYQ==", "dev": true, "dependencies": { - "@typescript-eslint/project-service": "8.39.1", - "@typescript-eslint/tsconfig-utils": "8.39.1", - "@typescript-eslint/types": "8.39.1", - "@typescript-eslint/visitor-keys": "8.39.1", + "@typescript-eslint/project-service": "8.40.0", + "@typescript-eslint/tsconfig-utils": "8.40.0", + "@typescript-eslint/types": "8.40.0", + "@typescript-eslint/visitor-keys": "8.40.0", "debug": "^4.3.4", "fast-glob": "^3.3.2", "is-glob": "^4.0.3", @@ -4601,15 +4602,15 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "8.39.1", - "resolved": "https://registry.npmmirror.com/@typescript-eslint/utils/-/utils-8.39.1.tgz", - "integrity": "sha512-VF5tZ2XnUSTuiqZFXCZfZs1cgkdd3O/sSYmdo2EpSyDlC86UM/8YytTmKnehOW3TGAlivqTDT6bS87B/GQ/jyg==", + "version": "8.40.0", + "resolved": "https://registry.npmmirror.com/@typescript-eslint/utils/-/utils-8.40.0.tgz", + "integrity": "sha512-Cgzi2MXSZyAUOY+BFwGs17s7ad/7L+gKt6Y8rAVVWS+7o6wrjeFN4nVfTpbE25MNcxyJ+iYUXflbs2xR9h4UBg==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.7.0", - "@typescript-eslint/scope-manager": "8.39.1", - "@typescript-eslint/types": "8.39.1", - "@typescript-eslint/typescript-estree": "8.39.1" + "@typescript-eslint/scope-manager": "8.40.0", + "@typescript-eslint/types": "8.40.0", + "@typescript-eslint/typescript-estree": "8.40.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -4624,12 +4625,12 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.39.1", - "resolved": "https://registry.npmmirror.com/@typescript-eslint/visitor-keys/-/visitor-keys-8.39.1.tgz", - "integrity": "sha512-W8FQi6kEh2e8zVhQ0eeRnxdvIoOkAp/CPAahcNio6nO9dsIwb9b34z90KOlheoyuVf6LSOEdjlkxSkapNEc+4A==", + "version": "8.40.0", + "resolved": "https://registry.npmmirror.com/@typescript-eslint/visitor-keys/-/visitor-keys-8.40.0.tgz", + "integrity": "sha512-8CZ47QwalyRjsypfwnbI3hKy5gJDPmrkLjkgMxhi0+DZZ2QNx2naS6/hWoVYUHU7LU2zleF68V9miaVZvhFfTA==", "dev": true, "dependencies": { - "@typescript-eslint/types": "8.39.1", + "@typescript-eslint/types": "8.40.0", "eslint-visitor-keys": "^4.2.1" }, "engines": { @@ -5050,9 +5051,9 @@ } }, "node_modules/ahooks": { - "version": "3.9.0", - "resolved": "https://registry.npmmirror.com/ahooks/-/ahooks-3.9.0.tgz", - "integrity": "sha512-r20/C38aFyU3Zqp3620gkdLnxmQhnmWORB3eGGTDlM4i/fOc0GUvM+f2oleMzEu7b3+pHXyzz+FB6ojxsUdYdw==", + "version": "3.9.4", + "resolved": "https://registry.npmmirror.com/ahooks/-/ahooks-3.9.4.tgz", + "integrity": "sha512-NkbX0mamCz4aBX27mZnObbzqcM9S4fzpjVf/6yOvmHh+McBo74xQw5Yz5ry4q2cLMkfNUjhe2q3M5RpjfMVu4g==", "dependencies": { "@babel/runtime": "^7.21.0", "dayjs": "^1.9.1", @@ -5065,7 +5066,7 @@ "tslib": "^2.4.1" }, "engines": { - "node": ">=8.0.0" + "node": ">=18" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", @@ -5104,9 +5105,9 @@ } }, "node_modules/antd": { - "version": "5.27.0", - "resolved": "https://registry.npmmirror.com/antd/-/antd-5.27.0.tgz", - "integrity": "sha512-o54dmpooLOc08RSGCkeEQBYAGPxUSmnhmYJKCNTHH46vzjOVxdteu+wPTRVkRbAkDTbs2VcNr5VL7Lu67rPIiA==", + "version": "5.27.1", + "resolved": "https://registry.npmmirror.com/antd/-/antd-5.27.1.tgz", + "integrity": "sha512-jGMSdBN7hAMvPV27B4RhzZfL6n6yu8yDbo7oXrlJasaOqB7bSDPcjdEy1kXy3JPsny/Qazb1ykzRI4EfcByAPQ==", "dependencies": { "@ant-design/colors": "^7.2.1", "@ant-design/cssinjs": "^1.23.0", @@ -6112,9 +6113,9 @@ } }, "node_modules/eslint": { - "version": "9.33.0", - "resolved": "https://registry.npmmirror.com/eslint/-/eslint-9.33.0.tgz", - "integrity": "sha512-TS9bTNIryDzStCpJN93aC5VRSW3uTx9sClUn4B87pwiCaJh220otoI0X8mJKr+VcPtniMdN8GKjlwgWGUv5ZKA==", + "version": "9.34.0", + "resolved": "https://registry.npmmirror.com/eslint/-/eslint-9.34.0.tgz", + "integrity": "sha512-RNCHRX5EwdrESy3Jc9o8ie8Bog+PeYvvSR8sDGoZxNFTvZ4dlxUB3WzQ3bQMztFrSRODGrLLj8g6OFuGY/aiQg==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", @@ -6123,7 +6124,7 @@ "@eslint/config-helpers": "^0.3.1", "@eslint/core": "^0.15.2", "@eslint/eslintrc": "^3.3.1", - "@eslint/js": "9.33.0", + "@eslint/js": "9.34.0", "@eslint/plugin-kit": "^0.3.5", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", @@ -6291,18 +6292,18 @@ } }, "node_modules/eslint-plugin-better-tailwindcss": { - "version": "3.7.4", - "resolved": "https://registry.npmmirror.com/eslint-plugin-better-tailwindcss/-/eslint-plugin-better-tailwindcss-3.7.4.tgz", - "integrity": "sha512-jlLHLoqrNbcqqROVjFojGDv1m2LGiJwFqynbARbyeRj9rc1Hmh46EeQhmYVQihhD4j+DSxG/bcwoA9PABIpLmw==", + "version": "3.7.5", + "resolved": "https://registry.npmmirror.com/eslint-plugin-better-tailwindcss/-/eslint-plugin-better-tailwindcss-3.7.5.tgz", + "integrity": "sha512-O6xqnXe7hCMMBecFHVmSUg3oER/Olt0EAiAxe6V7MfLG2Kys3MC56RAbgfv0tIRE0xiL7AoYydlxQ1iIMU8qKQ==", "dev": true, "dependencies": { "@eslint/css-tree": "^3.6.3", - "enhanced-resolve": "^5.18.2", + "enhanced-resolve": "^5.18.3", "jiti": "^2.5.1", "postcss": "^8.5.6", "postcss-import": "^16.1.1", "synckit": "^0.11.11", - "tailwind-csstree": "^0.1.2", + "tailwind-csstree": "^0.1.3", "tsconfig-paths-webpack-plugin": "^4.2.0" }, "engines": { @@ -7059,9 +7060,9 @@ } }, "node_modules/i18next": { - "version": "25.3.6", - "resolved": "https://registry.npmmirror.com/i18next/-/i18next-25.3.6.tgz", - "integrity": "sha512-dThZ0CTCM3sUG/qS0ZtQYZQcUI6DtBN8yBHK+SKEqihPcEYmjVWh/YJ4luic73Iq6Uxhp6q7LJJntRK5+1t7jQ==", + "version": "25.4.2", + "resolved": "https://registry.npmmirror.com/i18next/-/i18next-25.4.2.tgz", + "integrity": "sha512-gD4T25a6ovNXsfXY1TwHXXXLnD/K2t99jyYMCSimSCBnBRJVQr5j+VAaU83RJCPzrTGhVQ6dqIga66xO2rtd5g==", "funding": [ { "type": "individual", @@ -9129,15 +9130,15 @@ "integrity": "sha512-nsO+KSNgo1SbJqJEYRE9ERzo7YtYbou/OqjSQKxV7jcKox7+usiUVZOAC+XnDOABXggQTno0Y1CpVnuWEc1boQ==" }, "node_modules/react-i18next": { - "version": "15.6.1", - "resolved": "https://registry.npmmirror.com/react-i18next/-/react-i18next-15.6.1.tgz", - "integrity": "sha512-uGrzSsOUUe2sDBG/+FJq2J1MM+Y4368/QW8OLEKSFvnDflHBbZhSd1u3UkW0Z06rMhZmnB/AQrhCpYfE5/5XNg==", + "version": "15.7.2", + "resolved": "https://registry.npmmirror.com/react-i18next/-/react-i18next-15.7.2.tgz", + "integrity": "sha512-xJxq7ibnhUlMvd82lNC4te1GxGUMoM1A05KKyqoqsBXVZtEvZg/fz/fnVzdlY/hhQ3SpP/79qCocZOtICGhd3g==", "dependencies": { "@babel/runtime": "^7.27.6", "html-parse-stringify": "^3.0.1" }, "peerDependencies": { - "i18next": ">= 23.2.3", + "i18next": ">= 25.4.1", "react": ">= 16.8.0", "typescript": "^5" }, @@ -9168,9 +9169,9 @@ } }, "node_modules/react-router": { - "version": "7.8.1", - "resolved": "https://registry.npmmirror.com/react-router/-/react-router-7.8.1.tgz", - "integrity": "sha512-5cy/M8DHcG51/KUIka1nfZ2QeylS4PJRs6TT8I4PF5axVsI5JUxp0hC0NZ/AEEj8Vw7xsEoD7L/6FY+zoYaOGA==", + "version": "7.8.2", + "resolved": "https://registry.npmmirror.com/react-router/-/react-router-7.8.2.tgz", + "integrity": "sha512-7M2fR1JbIZ/jFWqelpvSZx+7vd7UlBTfdZqf6OSdF9g6+sfdqJDAWcak6ervbHph200ePlu+7G8LdoiC3ReyAQ==", "dependencies": { "cookie": "^1.0.1", "set-cookie-parser": "^2.6.0" @@ -9189,11 +9190,11 @@ } }, "node_modules/react-router-dom": { - "version": "7.8.1", - "resolved": "https://registry.npmmirror.com/react-router-dom/-/react-router-dom-7.8.1.tgz", - "integrity": "sha512-NkgBCF3sVgCiAWIlSt89GR2PLaksMpoo3HDCorpRfnCEfdtRPLiuTf+CNXvqZMI5SJLZCLpVCvcZrTdtGW64xQ==", + "version": "7.8.2", + "resolved": "https://registry.npmmirror.com/react-router-dom/-/react-router-dom-7.8.2.tgz", + "integrity": "sha512-Z4VM5mKDipal2jQ385H6UBhiiEDlnJPx6jyWsTYoZQdl5TrjxEV2a9yl3Fi60NBJxYzOTGTTHXPi0pdizvTwow==", "dependencies": { - "react-router": "7.8.1" + "react-router": "7.8.2" }, "engines": { "node": ">=20.0.0" @@ -9992,9 +9993,9 @@ "dev": true }, "node_modules/tailwind-csstree": { - "version": "0.1.2", - "resolved": "https://registry.npmmirror.com/tailwind-csstree/-/tailwind-csstree-0.1.2.tgz", - "integrity": "sha512-SP36MjhkSGa0Yux/B863QyxljQku+WbGH5f+dqyFJXvdexemcC0Fwj/2du2DoXVkboO5Rc1Pd4i45EmFJual6w==", + "version": "0.1.3", + "resolved": "https://registry.npmmirror.com/tailwind-csstree/-/tailwind-csstree-0.1.3.tgz", + "integrity": "sha512-LfOT807005OVfyxAjHpOajlIgoEaE894jqjkrhONC/HqBLS8OAhhNifnNs3Y5wD26eIdf0vk1zu9gja2oI3/1Q==", "dev": true, "engines": { "node": ">=18.18" @@ -10317,15 +10318,15 @@ } }, "node_modules/typescript-eslint": { - "version": "8.39.1", - "resolved": "https://registry.npmmirror.com/typescript-eslint/-/typescript-eslint-8.39.1.tgz", - "integrity": "sha512-GDUv6/NDYngUlNvwaHM1RamYftxf782IyEDbdj3SeaIHHv8fNQVRC++fITT7kUJV/5rIA/tkoRSSskt6osEfqg==", + "version": "8.40.0", + "resolved": "https://registry.npmmirror.com/typescript-eslint/-/typescript-eslint-8.40.0.tgz", + "integrity": "sha512-Xvd2l+ZmFDPEt4oj1QEXzA4A2uUK6opvKu3eGN9aGjB8au02lIVcLyi375w94hHyejTOmzIU77L8ol2sRg9n7Q==", "dev": true, "dependencies": { - "@typescript-eslint/eslint-plugin": "8.39.1", - "@typescript-eslint/parser": "8.39.1", - "@typescript-eslint/typescript-estree": "8.39.1", - "@typescript-eslint/utils": "8.39.1" + "@typescript-eslint/eslint-plugin": "8.40.0", + "@typescript-eslint/parser": "8.40.0", + "@typescript-eslint/typescript-estree": "8.40.0", + "@typescript-eslint/utils": "8.40.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -10728,17 +10729,14 @@ "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==" }, "node_modules/yaml": { - "version": "2.5.0", - "resolved": "https://registry.npmmirror.com/yaml/-/yaml-2.5.0.tgz", - "integrity": "sha512-2wWLbGbYDiSqqIKoPjar3MPgB94ErzCtrNE1FdqGuaO0pi2JGjmE8aW8TDZwzU7vuxcGRdL/4gPQwQ7hD5AMSw==", - "dev": true, - "optional": true, - "peer": true, + "version": "2.8.1", + "resolved": "https://registry.npmmirror.com/yaml/-/yaml-2.8.1.tgz", + "integrity": "sha512-lcYcMxX2PO9XMGvAJkJ3OsNMw+/7FKes7/hgerGUYWIoWu5j/+YQqcZr5JnPZWzOsEBgMbSbiSTn/dv/69Mkpw==", "bin": { "yaml": "bin.mjs" }, "engines": { - "node": ">= 14" + "node": ">= 14.6" } }, "node_modules/yocto-queue": { @@ -10754,17 +10752,17 @@ } }, "node_modules/zod": { - "version": "4.0.17", - "resolved": "https://registry.npmmirror.com/zod/-/zod-4.0.17.tgz", - "integrity": "sha512-1PHjlYRevNxxdy2JZ8JcNAw7rX8V9P1AKkP+x/xZfxB0K5FYfuV+Ug6P/6NVSR2jHQ+FzDDoDHS04nYUsOIyLQ==", + "version": "4.1.1", + "resolved": "https://registry.npmmirror.com/zod/-/zod-4.1.1.tgz", + "integrity": "sha512-SgMZK/h8Tigt9nnKkfJMvB/mKjiJXaX26xegP4sa+0wHIFVFWVlsQGdhklDmuargBD3Hsi3rsQRIzwJIhTPJHA==", "funding": { "url": "https://github.com/sponsors/colinhacks" } }, "node_modules/zustand": { - "version": "5.0.7", - "resolved": "https://registry.npmmirror.com/zustand/-/zustand-5.0.7.tgz", - "integrity": "sha512-Ot6uqHDW/O2VdYsKLLU8GQu8sCOM1LcoE8RwvLv9uuRT9s6SOHCKs0ZEOhxg+I1Ld+A1Q5lwx+UlKXXUoCZITg==", + "version": "5.0.8", + "resolved": "https://registry.npmmirror.com/zustand/-/zustand-5.0.8.tgz", + "integrity": "sha512-gyPKpIaxY9XcO2vSMrLbiER7QMAMGOQZVRdJ6Zi782jkbzZygq5GI9nG8g+sMgitRtndwaBSl7uiqC49o1SSiw==", "engines": { "node": ">=12.20.0" }, diff --git a/ui/package.json b/ui/package.json index e8dffe533..b9cd911be 100644 --- a/ui/package.json +++ b/ui/package.json @@ -13,20 +13,20 @@ "@codemirror/lang-yaml": "^6.1.2", "@codemirror/language": "^6.11.3", "@codemirror/legacy-modes": "^6.5.1", - "@flowgram.ai/document": "^0.4.0", - "@flowgram.ai/fixed-layout-editor": "^0.4.0", - "@flowgram.ai/minimap-plugin": "^0.4.0", + "@flowgram.ai/document": "0.4.3", + "@flowgram.ai/fixed-layout-editor": "0.4.3", + "@flowgram.ai/minimap-plugin": "0.4.3", "@tabler/icons-react": "^3.34.1", "@uiw/codemirror-extensions-basic-setup": "^4.25.1", "@uiw/codemirror-theme-vscode": "^4.25.1", "@uiw/react-codemirror": "^4.25.1", - "ahooks": "^3.9.0", - "antd": "^5.27.0", + "ahooks": "^3.9.4", + "antd": "^5.27.1", "antd-zod": "^7.0.0", "clsx": "^2.1.1", "cron-parser": "^5.3.0", "file-saver": "^2.0.5", - "i18next": "^25.3.6", + "i18next": "^25.4.2", "i18next-browser-languagedetector": "^8.2.0", "immer": "^10.1.1", "nanoid": "^5.1.5", @@ -35,14 +35,15 @@ "react": "^18.3.1", "react-copy-to-clipboard": "^5.1.0", "react-dom": "^18.3.1", - "react-i18next": "^15.6.1", - "react-router-dom": "^7.8.1", + "react-i18next": "^15.7.2", + "react-router-dom": "^7.8.2", "tailwind-merge": "^3.3.1", - "zod": "^4.0.17", - "zustand": "^5.0.7" + "yaml": "^2.8.1", + "zod": "^4.1.1", + "zustand": "^5.0.8" }, "devDependencies": { - "@eslint/js": "^9.33.0", + "@eslint/js": "^9.34.0", "@tailwindcss/postcss": "^4.1.12", "@tailwindcss/vite": "^4.1.12", "@types/file-saver": "^2.0.7", @@ -53,10 +54,10 @@ "@types/react-dom": "^18.3.7", "@vitejs/plugin-legacy": "^6.1.1", "@vitejs/plugin-react": "^4.7.0", - "eslint": "^9.33.0", + "eslint": "^9.34.0", "eslint-config-prettier": "^10.1.8", "eslint-import-resolver-typescript": "^4.4.4", - "eslint-plugin-better-tailwindcss": "^3.7.4", + "eslint-plugin-better-tailwindcss": "^3.7.5", "eslint-plugin-import": "^2.32.0", "eslint-plugin-prettier": "^5.5.4", "eslint-plugin-react-hooks": "^5.2.0", @@ -65,7 +66,7 @@ "prettier": "^3.6.2", "tailwindcss": "^4.1.12", "typescript": "^5.8.3", - "typescript-eslint": "^8.39.1", + "typescript-eslint": "^8.40.0", "vite": "^6.3.5" } } diff --git a/ui/src/components/ModalForm.tsx b/ui/src/components/ModalForm.tsx index 9a7ba759a..2e24d8dfb 100644 --- a/ui/src/components/ModalForm.tsx +++ b/ui/src/components/ModalForm.tsx @@ -122,7 +122,7 @@ const ModalForm = = any>({ onOk={handleOkClick} onCancel={handleCancelClick} > -
+
{children}
diff --git a/ui/src/components/access/AccessEditDrawer.tsx b/ui/src/components/access/AccessEditDrawer.tsx index c091f3144..0dc6bf1f6 100644 --- a/ui/src/components/access/AccessEditDrawer.tsx +++ b/ui/src/components/access/AccessEditDrawer.tsx @@ -146,7 +146,7 @@ const AccessEditDrawer = ({ afterClose, afterSubmit, mode, data, loading, trigge const useDrawer = () => { type DataType = AccessEditDrawerProps["data"]; const [data, setData] = useState(); - const [open, setOpen] = useState(false); + const [open, setOpen] = useState(false); const onOpenChange = useCallback((open: boolean) => { setOpen(open); diff --git a/ui/src/components/certificate/CertificateDetailDrawer.tsx b/ui/src/components/certificate/CertificateDetailDrawer.tsx index 7a48724bc..788143790 100644 --- a/ui/src/components/certificate/CertificateDetailDrawer.tsx +++ b/ui/src/components/certificate/CertificateDetailDrawer.tsx @@ -65,7 +65,7 @@ const CertificateDetailDrawer = ({ afterClose, data, loading, trigger, ...props const useDrawer = () => { type DataType = CertificateDetailDrawerProps["data"]; const [data, setData] = useState(); - const [open, setOpen] = useState(false); + const [open, setOpen] = useState(false); const onOpenChange = useCallback((open: boolean) => { setOpen(open); diff --git a/ui/src/components/workflow/WorkflowGraphExportBox.tsx b/ui/src/components/workflow/WorkflowGraphExportBox.tsx new file mode 100644 index 000000000..06a8c74c7 --- /dev/null +++ b/ui/src/components/workflow/WorkflowGraphExportBox.tsx @@ -0,0 +1,143 @@ +import { useEffect, useState } from "react"; +import { CopyToClipboard } from "react-copy-to-clipboard"; +import { useTranslation } from "react-i18next"; +import { FlowNodeBaseType, FlowNodeSplitType } from "@flowgram.ai/fixed-layout-editor"; +import { IconClipboard } from "@tabler/icons-react"; +import { App, Button, Form, Radio, Tooltip } from "antd"; +import { stringify as stringifyYaml } from "yaml"; + +import CodeInput from "@/components/CodeInput"; +import { type WorkflowGraph, type WorkflowNode } from "@/domain/workflow"; + +import { getAllNodeRegistries } from "./designer/nodes"; + +export type WorkflowGraphExportBoxFormats = "json" | "yaml"; + +export interface WorkflowGraphExportBoxProps { + className?: string; + style?: React.CSSProperties; + data: WorkflowGraph; +} + +const serialize = (graph: WorkflowGraph | undefined, format: WorkflowGraphExportBoxFormats): string | undefined => { + if (!graph) return; + + const nodeRegistries = getAllNodeRegistries(); + + const deepConvert = (node: WorkflowNode): Map => { + // 利用 Map 来保证字段序列化的有序性 + const map = new Map([ + ["id", node.id], + ["type", node.type], + ["name", node.data.name], + ]); + + if (node.data.disabled != null) { + if (node.data.disabled) { + map.set("disabled", node.data.disabled); + } + } + + if (node.data.config != null) { + map.set("config", node.data.config); + } + + if (node.blocks != null) { + const branchLikeNodeTypes = [ + FlowNodeBaseType.BLOCK, + FlowNodeSplitType.SIMPLE_SPLIT, + FlowNodeSplitType.DYNAMIC_SPLIT, + FlowNodeSplitType.STATIC_SPLIT, + ] as string[]; + const hasChildren = node.blocks.length > 0 || branchLikeNodeTypes.includes(nodeRegistries.find((r) => r.type === node.type)?.extend ?? ""); + if (hasChildren) { + const childern = node.blocks.map((block) => deepConvert(block)); + map.set("blocks", childern); + } + } + + Object.entries(node.data).forEach(([k, v]) => { + if (k === "name" || k === "disabled" || k === "config") return; + map.set(k, v); + }); + + return map; + }; + const nodes = graph.nodes.map((node) => deepConvert(node)); + + let content: string = ""; + switch (format) { + case "json": + content = JSON.stringify( + { ...graph, nodes }, + (_, value) => { + if (value instanceof Map) { + return Object.fromEntries(value.entries()); + } else { + return value; + } + }, + 2 + ); + break; + + case "yaml": + content = stringifyYaml( + { ...graph, nodes }, + { + indent: 2, + defaultKeyType: "PLAIN", + defaultStringType: "QUOTE_DOUBLE", + } + ); + break; + } + + return content; +}; + +const WorkflowGraphExportBox = ({ className, style, data }: WorkflowGraphExportBoxProps) => { + const { t } = useTranslation(); + + const { message } = App.useApp(); + + const [format, setFormat] = useState("yaml"); + const [content, setContent] = useState(); + + useEffect(() => { + setContent(serialize(data, format)); + }, [data, format]); + + return ( +
+ + setFormat(e.target.value)}> + + YAML + + + JSON + + + + + +
+ + { + message.success(t("common.text.copied")); + }} + > +
+ +
+
+ ); +}; + +export default WorkflowGraphExportBox; diff --git a/ui/src/components/workflow/WorkflowGraphExportModal.tsx b/ui/src/components/workflow/WorkflowGraphExportModal.tsx new file mode 100644 index 000000000..9adfbc9cb --- /dev/null +++ b/ui/src/components/workflow/WorkflowGraphExportModal.tsx @@ -0,0 +1,95 @@ +import { startTransition, useCallback, useState } from "react"; +import { useTranslation } from "react-i18next"; +import { useControllableValue } from "ahooks"; +import { Modal } from "antd"; + +import { type WorkflowGraph } from "@/domain/workflow"; +import { useTriggerElement } from "@/hooks"; + +import WorkflowGraphExportBox from "./WorkflowGraphExportBox"; + +export interface WorkflowGraphExportModalProps { + afterClose?: () => void; + data: WorkflowGraph; + loading?: boolean; + open?: boolean; + trigger?: React.ReactNode; + onOpenChange?: (open: boolean) => void; +} + +const WorkflowGraphExportModal = ({ afterClose, data, loading, trigger, ...props }: WorkflowGraphExportModalProps) => { + const { t } = useTranslation(); + + const [open, setOpen] = useControllableValue(props, { + valuePropName: "open", + defaultValuePropName: "defaultOpen", + trigger: "onOpenChange", + }); + + const triggerEl = useTriggerElement(trigger, { onClick: () => setOpen(true) }); + + const handleCancelClick = () => { + setOpen(false); + }; + + return ( + <> + {triggerEl} + + +
+ +
+
+ + ); +}; + +const useModal = () => { + type DataType = WorkflowGraphExportModalProps["data"]; + const [data, setData] = useState(); + const [open, setOpen] = useState(false); + + const onOpenChange = useCallback((open: boolean) => { + setOpen(open); + }, []); + + return { + modalProps: { + afterClose: () => { + startTransition(() => { + if (!open) { + setData(void 0); + } + }); + }, + data: data!, + open, + onOpenChange, + }, + + open: (data: DataType) => { + setData(data); + setOpen(true); + }, + close: () => { + setOpen(false); + }, + }; +}; + +const _default = Object.assign(WorkflowGraphExportModal, { + useModal, +}); + +export default _default; diff --git a/ui/src/components/workflow/WorkflowGraphImportInputBox.tsx b/ui/src/components/workflow/WorkflowGraphImportInputBox.tsx new file mode 100644 index 000000000..8a951cb3a --- /dev/null +++ b/ui/src/components/workflow/WorkflowGraphImportInputBox.tsx @@ -0,0 +1,249 @@ +import { forwardRef, useImperativeHandle } from "react"; +import { useTranslation } from "react-i18next"; +import { Form, Radio } from "antd"; +import { createSchemaFieldRule } from "antd-zod"; +import { parse as parseYaml } from "yaml"; +import { z } from "zod"; + +import CodeInput from "@/components/CodeInput"; +import { WORKFLOW_NODE_TYPES, type WorkflowGraph, type WorkflowNode, type WorkflowNodeType } from "@/domain/workflow"; +import { useAntdForm } from "@/hooks"; + +export type WorkflowGraphImportInputBoxFormats = "json" | "yaml"; + +export interface WorkflowGraphImportInputBoxProps { + className?: string; + style?: React.CSSProperties; +} + +export interface WorkflowGraphImportInputBoxInstance { + validate: () => Promise; +} + +const deserialize = (content: string | undefined, format: WorkflowGraphImportInputBoxFormats): WorkflowGraph | undefined => { + if (!content?.trim()) return; + + let temp: any; + switch (format) { + case "json": + temp = JSON.parse(content); + break; + + case "yaml": + temp = parseYaml(content); + break; + } + + const deepParse = (item: any): WorkflowNode => { + item = item ?? {}; + + const node: WorkflowNode = { + id: item.id?.toString() ?? "", + type: (item.type?.toString() ?? "") as WorkflowNodeType, + data: { + name: item.name?.toString() ?? "", + disabled: item.disabled === true || item.disabled === "true", + config: item.config, + }, + blocks: Array.isArray(item.blocks) ? item.blocks.map((block: any) => deepParse(block)) : [], + }; + + if (item.data != null) { + Object.entries(item.data).forEach(([k, v]) => { + if (k === "id" || k === "type" || k === "meta" || k === "blocks") return; + if (k === "name" || k === "disabled" || k === "config") return; + node.data[k] = v; + }); + } + + return node; + }; + const nodes = Array.from(temp.nodes ?? []).map((item) => deepParse(item)); + + return { nodes: nodes }; +}; + +const WorkflowGraphImportInputBox = forwardRef(({ className, style }, ref) => { + const { t } = useTranslation(); + + const formSchema = z + .object({ + format: z.enum(["json", "yaml"]), + content: z.string().refine((v) => !!v?.trim(), t("workflow.detail.design.action.import.form.content.errmsg.invalid")), + }) + .superRefine((values, ctx) => { + let graph: WorkflowGraph | undefined; + try { + graph = deserialize(values.content, values.format); + } catch { + ctx.addIssue({ + code: "custom", + message: t("workflow.detail.design.action.import.form.content.errmsg.invalid"), + path: ["content"], + }); + return; + } + + if (graph) { + const errmsgs: string[] = []; + + if (graph.nodes.at(0)?.type !== WORKFLOW_NODE_TYPES.START) { + errmsgs.push(t("workflow.detail.design.action.import.form.content.errmsg.first_node_start")); + } + + if (graph.nodes.at(-1)?.type !== WORKFLOW_NODE_TYPES.END) { + errmsgs.push(t("workflow.detail.design.action.import.form.content.errmsg.last_node_end")); + } + + let startNodeId: string | undefined; + let startNodeDuplicated: boolean = false; + const nodeIds = new Set(); + const deepValidate = (node: WorkflowNode) => { + // 验证字段:ID + if (!/^(?![_-])[a-zA-Z0-9_-]{1,32}$/.test(node.id)) { + errmsgs.push(t("workflow.detail.design.action.import.form.content.errmsg.invalid_id", { nodeId: node.id })); + } + + // 验证字段:配置项 + if (node.data.config != null) { + if (typeof node.data.config !== "object" || Array.isArray(node.data.config)) { + errmsgs.push(t("workflow.detail.design.action.import.form.content.errmsg.invalid_config", { nodeId: node.id })); + } + } + + // 验证节点 ID 是否冲突 + if (nodeIds.has(node.id)) { + errmsgs.push(t("workflow.detail.design.action.import.form.content.errmsg.conflict_id", { nodeId: node.id })); + } else { + nodeIds.add(node.id); + } + + // 验证开始节点是否重复 + if (node.type === WORKFLOW_NODE_TYPES.START) { + if (startNodeId) { + if (!startNodeDuplicated) { + startNodeDuplicated = true; + errmsgs.push(t("workflow.detail.design.action.import.form.content.errmsg.duplicate_start")); + } + } else { + startNodeId = node.id; + } + } + + // 验证 Condition 分支结构 + if (node.type === WORKFLOW_NODE_TYPES.CONDITION) { + const blocks = node.blocks ?? []; + const f1 = blocks.length > 0; + const f2 = blocks.every((block) => block.type === WORKFLOW_NODE_TYPES.BRANCHBLOCK); + if (!f1 || !f2) { + errmsgs.push(t("workflow.detail.design.action.import.form.content.errmsg.abnormal_condition_branch", { nodeId: node.id })); + } + } else if (node.type === WORKFLOW_NODE_TYPES.BRANCHBLOCK) { + const blocks = node.blocks ?? []; + const f1 = blocks.length > 0; + const f2 = blocks.every((block) => block.type !== WORKFLOW_NODE_TYPES.BRANCHBLOCK); + if (!f1 || !f2) { + errmsgs.push(t("workflow.detail.design.action.import.form.content.errmsg.abnormal_condition_branch", { nodeId: node.id })); + } + } + + // 验证 TryCatch 分支结构 + if (node.type === WORKFLOW_NODE_TYPES.TRYCATCH) { + const blocks = node.blocks ?? []; + const f1 = blocks.length >= 2; + const f2 = blocks[0]?.type === WORKFLOW_NODE_TYPES.TRYBLOCK; + const f3 = blocks.some((block) => block.type === WORKFLOW_NODE_TYPES.CATCHBLOCK); + if (!f1 || !f2 || !f3) { + errmsgs.push(t("workflow.detail.design.action.import.form.content.errmsg.abnormal_try_catch_branch", { nodeId: node.id })); + } + } else if (node.type === WORKFLOW_NODE_TYPES.TRYBLOCK || node.type === WORKFLOW_NODE_TYPES.CATCHBLOCK) { + const blocks = node.blocks ?? []; + const f1 = blocks.length > 0; + const f2 = blocks.every((block) => block.type !== WORKFLOW_NODE_TYPES.TRYBLOCK); + const f3 = blocks.every((block) => block.type !== WORKFLOW_NODE_TYPES.CATCHBLOCK); + if (!f1 || !f2 || !f3) { + errmsgs.push(t("workflow.detail.design.action.import.form.content.errmsg.abnormal_try_catch_branch", { nodeId: node.id })); + } + } + + // 验证子节点 + if (Array.isArray(node.blocks)) { + node.blocks.forEach((block) => deepValidate(block)); + } + }; + graph.nodes.forEach((node) => deepValidate(node)); + + const MAX_ISSUE_COUNT = 5; + for (let i = 0; i < Math.min(MAX_ISSUE_COUNT, errmsgs.length); i++) { + ctx.addIssue({ + code: "custom", + message: errmsgs[i], + path: ["content"], + }); + } + } + }); + const formRule = createSchemaFieldRule(formSchema); + const { form: formInst, formProps } = useAntdForm>({ + name: "workflowGraphExportInputBoxForm", + initialValues: { + format: "yaml", + content: "", + }, + }); + + const fieldFormat = Form.useWatch("format", formInst); + const fieldContent = Form.useWatch("content", formInst); + + const handleFormatChange = (format: WorkflowGraphImportInputBoxFormats) => { + formInst.setFieldValue("format", format); + + switch (format) { + case "json": + try { + JSON.parse(fieldContent); + } catch { + formInst.setFieldValue("content", ""); + } + break; + + case "yaml": + try { + parseYaml(fieldContent); + } catch { + formInst.setFieldValue("content", ""); + } + break; + } + }; + + useImperativeHandle(ref, () => { + return { + validate: async () => { + const formValues = await formInst.validateFields(); + return deserialize(formValues.content, formValues.format); + }, + }; + }); + + return ( +
+ + handleFormatChange(e.target.value)}> + + YAML + + + JSON + + + + + + + +
+ ); +}); + +export default WorkflowGraphImportInputBox; diff --git a/ui/src/components/workflow/WorkflowGraphImportModal.tsx b/ui/src/components/workflow/WorkflowGraphImportModal.tsx new file mode 100644 index 000000000..052026c2f --- /dev/null +++ b/ui/src/components/workflow/WorkflowGraphImportModal.tsx @@ -0,0 +1,119 @@ +import { startTransition, useCallback, useRef, useState } from "react"; +import { useTranslation } from "react-i18next"; +import { useControllableValue } from "ahooks"; +import { Button, Flex, Modal } from "antd"; + +import { type WorkflowGraph } from "@/domain/workflow"; +import { useTriggerElement } from "@/hooks"; + +import WorkflowImportExportForm, { type WorkflowGraphImportInputBoxInstance } from "./WorkflowGraphImportInputBox"; + +export interface WorkflowGraphImportModalProps { + afterClose?: () => void; + open?: boolean; + trigger?: React.ReactNode; + onCancel?: () => void; + onOk?: (graph: WorkflowGraph) => void; + onOpenChange?: (open: boolean) => void; +} + +const WorkflowGraphImportModal = ({ afterClose, trigger, onCancel, onOk, ...props }: WorkflowGraphImportModalProps) => { + const { t } = useTranslation(); + + const [open, setOpen] = useControllableValue(props, { + valuePropName: "open", + defaultValuePropName: "defaultOpen", + trigger: "onOpenChange", + }); + + const triggerEl = useTriggerElement(trigger, { onClick: () => setOpen(true) }); + + const graphInputBoxRef = useRef(null); + + const handleCancelClick = () => { + setOpen(false); + + onCancel?.(); + }; + + const handleOkClick = async () => { + const graph = await graphInputBoxRef.current!.validate(); + + setOpen(false); + + if (graph != null) { + onOk?.(graph); + } + }; + + return ( + <> + {triggerEl} + + + + + + } + open={open} + title={t("workflow.detail.design.action.import.modal.title")} + width="768px" + onCancel={handleCancelClick} + > +
+ +
+
+ + ); +}; + +const useModal = () => { + const [open, setOpen] = useState(false); + const [onOkHandler, setOnOkHandler] = useState<{ handler: WorkflowGraphImportModalProps["onOk"] }>(); + + const onOpenChange = useCallback((open: boolean) => { + setOpen(open); + }, []); + + return { + modalProps: { + afterClose: () => { + startTransition(() => { + if (!open) { + setOnOkHandler(void 0); + } + }); + }, + open, + onOk: (graph: WorkflowGraph) => { + onOkHandler?.handler?.(graph); + }, + onOpenChange, + }, + + open: () => { + setOpen(true); + + const { promise, resolve } = Promise.withResolvers(); + setOnOkHandler({ handler: (graph) => resolve(graph) }); + return promise; + }, + close: () => { + setOpen(false); + }, + }; +}; + +const _default = Object.assign(WorkflowGraphImportModal, { + useModal, +}); + +export default _default; diff --git a/ui/src/components/workflow/WorkflowRunDetailDrawer.tsx b/ui/src/components/workflow/WorkflowRunDetailDrawer.tsx index 4419062cc..a667323da 100644 --- a/ui/src/components/workflow/WorkflowRunDetailDrawer.tsx +++ b/ui/src/components/workflow/WorkflowRunDetailDrawer.tsx @@ -65,7 +65,7 @@ const WorkflowRunDetailDrawer = ({ afterClose, data, loading, trigger, ...props const useDrawer = () => { type DataType = WorkflowRunDetailDrawerProps["data"]; const [data, setData] = useState(); - const [open, setOpen] = useState(false); + const [open, setOpen] = useState(false); const onOpenChange = useCallback((open: boolean) => { setOpen(open); diff --git a/ui/src/components/workflow/designer/NodeDrawer.tsx b/ui/src/components/workflow/designer/NodeDrawer.tsx index 59f534e53..c33ad67b1 100644 --- a/ui/src/components/workflow/designer/NodeDrawer.tsx +++ b/ui/src/components/workflow/designer/NodeDrawer.tsx @@ -75,7 +75,7 @@ const NodeDrawer = ({ node, trigger, ...props }: NodeDrawerProps) => { const useDrawer = () => { type NodeDataType = NodeDrawerProps["node"]; const [node, setNode] = useState(); - const [open, setOpen] = useState(false); + const [open, setOpen] = useState(false); const onOpenChange = useCallback((open: boolean) => { setOpen(open); diff --git a/ui/src/components/workflow/designer/forms/BranchBlockNodeConfigFormExpressionEditor.tsx b/ui/src/components/workflow/designer/forms/BranchBlockNodeConfigExprInputBox.tsx similarity index 96% rename from ui/src/components/workflow/designer/forms/BranchBlockNodeConfigFormExpressionEditor.tsx rename to ui/src/components/workflow/designer/forms/BranchBlockNodeConfigExprInputBox.tsx index 4240bd03d..3d0f4b9bc 100644 --- a/ui/src/components/workflow/designer/forms/BranchBlockNodeConfigFormExpressionEditor.tsx +++ b/ui/src/components/workflow/designer/forms/BranchBlockNodeConfigExprInputBox.tsx @@ -21,7 +21,7 @@ import { useNodeFormContext } from "./_context"; import { getAllPreviousNodes } from "../_util"; import { NodeType } from "../nodes/typings"; -export interface BranchBlockNodeConfigFormExpressionEditorProps { +export interface BranchBlockNodeConfigExprInputBoxProps { className?: string; style?: React.CSSProperties; defaultValue?: Expr; @@ -29,8 +29,8 @@ export interface BranchBlockNodeConfigFormExpressionEditorProps { onChange?: (value: Expr) => void; } -export interface BranchBlockNodeConfigFormExpressionEditorInstance { - validate: () => Promise; +export interface BranchBlockNodeConfigExprInputBoxInstance { + validate: () => Promise; } // 表单内部使用的扁平结构 @@ -50,7 +50,7 @@ type ConditionFormValues = { logicalOperator: ExprLogicalOperator; }; -const exprToFormValues = (expr?: Expr): ConditionFormValues => { +const exprToFormValues = (expr: Expr | undefined): ConditionFormValues => { if (!expr) return getInitialValues(); const conditions: ConditionItem[] = []; @@ -138,7 +138,7 @@ const formValuesToExpr = (values: ConditionFormValues): Expr | undefined => { return expr; }; -const BranchBlockNodeConfigFormExpressionEditor = forwardRef( +const BranchBlockNodeConfigExprInputBox = forwardRef( ({ className, style, ...props }, ref) => { const { t } = useTranslation(); @@ -153,7 +153,7 @@ const BranchBlockNodeConfigFormExpressionEditor = forwardRef(); - const formName = useAntdFormName({ form: formInst, name: "workflowNodeBranchBlockConfigFormExpressionEditorForm" }); + const formName = useAntdFormName({ form: formInst, name: "workflowNodeBranchBlockConfigExprInputBoxForm" }); const [formModel, setFormModel] = useState(getInitialValues()); useEffect(() => { @@ -253,8 +253,9 @@ const BranchBlockNodeConfigFormExpressionEditor = forwardRef { await formInst.validateFields(); + return formValuesToExpr(formInst.getFieldsValue()); }, - } as BranchBlockNodeConfigFormExpressionEditorInstance; + }; }); return ( @@ -383,4 +384,4 @@ const getInitialValues = (): ConditionFormValues => { }; }; -export default BranchBlockNodeConfigFormExpressionEditor; +export default BranchBlockNodeConfigExprInputBox; diff --git a/ui/src/components/workflow/designer/forms/BranchBlockNodeConfigForm.tsx b/ui/src/components/workflow/designer/forms/BranchBlockNodeConfigForm.tsx index a3929f5ed..4df968c2d 100644 --- a/ui/src/components/workflow/designer/forms/BranchBlockNodeConfigForm.tsx +++ b/ui/src/components/workflow/designer/forms/BranchBlockNodeConfigForm.tsx @@ -18,7 +18,7 @@ import { import { useAntdForm } from "@/hooks"; import { NodeFormContextProvider } from "./_context"; -import BranchBlockNodeConfigFormExpressionEditor, { type BranchBlockNodeConfigFormExpressionEditorInstance } from "./BranchBlockNodeConfigFormExpressionEditor"; +import BranchBlockNodeConfigExprInputBox, { type BranchBlockNodeConfigExprInputBoxInstance } from "./BranchBlockNodeConfigExprInputBox"; import { NodeType } from "../nodes/typings"; @@ -41,7 +41,7 @@ const BranchBlockNodeConfigForm = ({ node, ...props }: BranchBlockNodeConfigForm const formSchema = getSchema({ i18n }).superRefine(async (values, ctx) => { if (values.expression != null) { try { - await exprEditorRef.current!.validate(); + await exprInputBoxRef.current!.validate(); } catch { if (!ctx.issues.some((issue) => issue.path?.[0] === "expression")) { ctx.addIssue({ @@ -60,14 +60,14 @@ const BranchBlockNodeConfigForm = ({ node, ...props }: BranchBlockNodeConfigForm initialValues: initialValues ?? getInitialValues(), }); - const exprEditorRef = useRef(null); + const exprInputBoxRef = useRef(null); return (
- +
diff --git a/ui/src/components/workflow/designer/forms/_shared.tsx b/ui/src/components/workflow/designer/forms/_shared.tsx index 320d1413c..3d09b93c3 100644 --- a/ui/src/components/workflow/designer/forms/_shared.tsx +++ b/ui/src/components/workflow/designer/forms/_shared.tsx @@ -1,6 +1,6 @@ import { useEffect, useRef, useState } from "react"; import { useTranslation } from "react-i18next"; -import { type FlowNodeEntity, getNodeForm, useClientContext, useRefresh, useWatchFormValueIn } from "@flowgram.ai/fixed-layout-editor"; +import { type FlowNodeEntity, getNodeForm, useClientContext, useRefresh } from "@flowgram.ai/fixed-layout-editor"; import { IconEye, IconEyeOff, IconX } from "@tabler/icons-react"; import { useControllableValue } from "ahooks"; import { Anchor, type AnchorProps, App, Button, Drawer, Flex, type FormInstance, Tooltip, Typography } from "antd"; @@ -41,7 +41,7 @@ export const NodeConfigDrawer = ({ children, afterClose, anchor, footer = true, const containerRef = useRef(null); - const [formPending, setFormPending] = useState(false); + const [formPending, setFormPending] = useState(false); const nodeRegistry = node?.getNodeRegistry(); const NodeIcon = nodeRegistry?.meta?.icon; @@ -134,13 +134,12 @@ export const NodeConfigDrawer = ({ children, afterClose, anchor, footer = true, const newValues = picker(formInst.getFieldsValue(true)); const changed = !isEqual(oldValues, {}) && !isEqual(oldValues, newValues); - const { promise, resolve, reject } = Promise.withResolvers(); + const { promise, resolve } = Promise.withResolvers(); if (changed) { modal.confirm({ title: t("common.text.operation_confirm"), content: t("workflow.detail.design.unsaved_changes.confirm"), onOk: () => resolve(void 0), - onCancel: () => reject(), }); } else { resolve(void 0); diff --git a/ui/src/domain/workflow.ts b/ui/src/domain/workflow.ts index 48c4b9738..f57dd7b49 100644 --- a/ui/src/domain/workflow.ts +++ b/ui/src/domain/workflow.ts @@ -59,6 +59,7 @@ export type WorkflowNode = { name?: string; disabled?: boolean; config?: Record; + [key: string]: unknown; }; blocks?: WorkflowNode[]; }; diff --git a/ui/src/i18n/locales/en/nls.common.json b/ui/src/i18n/locales/en/nls.common.json index 41be6024f..829ef007c 100644 --- a/ui/src/i18n/locales/en/nls.common.json +++ b/ui/src/i18n/locales/en/nls.common.json @@ -1,9 +1,10 @@ { "common.button.add": "Add", "common.button.cancel": "Cancel", + "common.button.close": "Close", "common.button.confirm": "Confirm", - "common.button.create": "Create", "common.button.copy": "Copy", + "common.button.create": "Create", "common.button.delete": "Delete", "common.button.download": "Download", "common.button.edit": "Edit", @@ -23,6 +24,7 @@ "common.text.operation_succeeded": "Operation succeeded", "common.text.operation_failed": "Operation failed", "common.text.request_error": "Request error", + "common.text.saving": "Saving ...", "common.text.search": "Search ...", "common.menu.document": "Document", diff --git a/ui/src/i18n/locales/en/nls.workflow.json b/ui/src/i18n/locales/en/nls.workflow.json index 1696b2bfc..8346ee152 100644 --- a/ui/src/i18n/locales/en/nls.workflow.json +++ b/ui/src/i18n/locales/en/nls.workflow.json @@ -82,6 +82,24 @@ "workflow.detail.design.action.rollback.button": "Rollback", "workflow.detail.design.action.rollback.modal.title": "Rollback changes", "workflow.detail.design.action.rollback.modal.content": "Are you sure to rollback your changes to the last published version?", + "workflow.detail.design.action.import.button": "Import", + "workflow.detail.design.action.import.modal.title": "Import workflow", + "workflow.detail.design.action.import.modal.ok_button": "Import", + "workflow.detail.design.action.import.form.format.label": "Format", + "workflow.detail.design.action.import.form.content.label": "Content", + "workflow.detail.design.action.import.form.content.errmsg.invalid": "Please enter a valid content", + "workflow.detail.design.action.import.form.content.errmsg.first_node_start": "The first node must be a start node", + "workflow.detail.design.action.import.form.content.errmsg.last_node_end": "The last node must be an end node", + "workflow.detail.design.action.import.form.content.errmsg.duplicate_start": "The start node must be unique", + "workflow.detail.design.action.import.form.content.errmsg.invalid_id": "Node ID invalid: #{{nodeId}}", + "workflow.detail.design.action.import.form.content.errmsg.invalid_config": "Node config invalid: #{{nodeId}}", + "workflow.detail.design.action.import.form.content.errmsg.conflict_id": "Node ID conflict: #{{nodeId}}", + "workflow.detail.design.action.import.form.content.errmsg.abnormal_condition_branch": "Abnormal parallel/conditional branch: #{{nodeId}}", + "workflow.detail.design.action.import.form.content.errmsg.abnormal_try_catch_branch": "Abnormal execution result branch: #{{nodeId}}", + "workflow.detail.design.action.export.button": "Export", + "workflow.detail.design.action.export.modal.title": "Export workflow", + "workflow.detail.design.action.export.form.format.label": "Format", + "workflow.detail.design.action.export.form.content.label": "Content", "workflow.detail.design.uncompleted_design.alert": "The orchestration is not ready. Please check if there are any nodes not configured.", "workflow.detail.design.unpublished_draft.alert": "The orchestration is not released yet.", "workflow.detail.design.unsaved_changes.confirm": "You have unsaved changes. Do you really want to close the panel and drop those changes?", diff --git a/ui/src/i18n/locales/zh/nls.common.json b/ui/src/i18n/locales/zh/nls.common.json index f55239c64..5bfe67f36 100644 --- a/ui/src/i18n/locales/zh/nls.common.json +++ b/ui/src/i18n/locales/zh/nls.common.json @@ -1,9 +1,10 @@ { "common.button.add": "新增", "common.button.cancel": "取消", + "common.button.close": "关闭", "common.button.confirm": "确定", - "common.button.create": "新建", "common.button.copy": "复制", + "common.button.create": "新建", "common.button.delete": "刪除", "common.button.download": "下载", "common.button.edit": "编辑", @@ -23,6 +24,7 @@ "common.text.operation_succeeded": "操作成功", "common.text.operation_failed": "操作失败", "common.text.request_error": "请求错误", + "common.text.saving": "保存中 ……", "common.text.search": "搜索 ……", "common.menu.document": "文档", diff --git a/ui/src/i18n/locales/zh/nls.workflow.json b/ui/src/i18n/locales/zh/nls.workflow.json index 9bda61118..67e5948b1 100644 --- a/ui/src/i18n/locales/zh/nls.workflow.json +++ b/ui/src/i18n/locales/zh/nls.workflow.json @@ -82,6 +82,24 @@ "workflow.detail.design.action.rollback.button": "回退更改", "workflow.detail.design.action.rollback.modal.title": "回退更改", "workflow.detail.design.action.rollback.modal.content": "确定要回退到最近一次发布的版本吗?", + "workflow.detail.design.action.import.button": "导入", + "workflow.detail.design.action.import.modal.title": "导入工作流", + "workflow.detail.design.action.import.modal.ok_button": "导入", + "workflow.detail.design.action.import.form.format.label": "格式", + "workflow.detail.design.action.import.form.content.label": "内容", + "workflow.detail.design.action.import.form.content.errmsg.invalid": "请输入有效的内容", + "workflow.detail.design.action.import.form.content.errmsg.first_node_start": "第一个节点必须是开始节点", + "workflow.detail.design.action.import.form.content.errmsg.last_node_end": "最后一个节点必须是结束节点", + "workflow.detail.design.action.import.form.content.errmsg.duplicate_start": "有且只能有一个开始节点", + "workflow.detail.design.action.import.form.content.errmsg.invalid_id": "节点 ID 无效:#{{nodeId}}", + "workflow.detail.design.action.import.form.content.errmsg.invalid_config": "节点配置无效:#{{nodeId}}", + "workflow.detail.design.action.import.form.content.errmsg.conflict_id": "节点 ID 冲突:#{{nodeId}}", + "workflow.detail.design.action.import.form.content.errmsg.abnormal_condition_branch": "并行/条件分支结构异常:#{{nodeId}}", + "workflow.detail.design.action.import.form.content.errmsg.abnormal_try_catch_branch": "执行结果分支结构异常:#{{nodeId}}", + "workflow.detail.design.action.export.button": "导出", + "workflow.detail.design.action.export.modal.title": "导出工作流", + "workflow.detail.design.action.export.form.format.label": "格式", + "workflow.detail.design.action.export.form.content.label": "内容", "workflow.detail.design.uncompleted_design.alert": "流程编排未就绪,请检查是否有节点未完成配置。", "workflow.detail.design.unpublished_draft.alert": "当前编排有尚未发布的更改。", "workflow.detail.design.unsaved_changes.confirm": "你有尚未保存的更改。确定要关闭面板吗?", diff --git a/ui/src/pages/workflows/WorkflowDetail.tsx b/ui/src/pages/workflows/WorkflowDetail.tsx index 5ce0f68a1..4c9d7121e 100644 --- a/ui/src/pages/workflows/WorkflowDetail.tsx +++ b/ui/src/pages/workflows/WorkflowDetail.tsx @@ -65,13 +65,12 @@ const WorkflowDetail = () => { }, [workflow.lastRunStatus]); const handleRunClick = () => { - const { promise, resolve, reject } = Promise.withResolvers(); + const { promise, resolve } = Promise.withResolvers(); if (workflow.hasDraft) { modal.confirm({ title: t("workflow.action.run.modal.title"), content: t("workflow.action.run.modal.content"), onOk: () => resolve(void 0), - onCancel: () => reject(), }); } else { resolve(void 0); diff --git a/ui/src/pages/workflows/WorkflowDetailDesign.tsx b/ui/src/pages/workflows/WorkflowDetailDesign.tsx index 2fe36e5ce..bab6d4794 100644 --- a/ui/src/pages/workflows/WorkflowDetailDesign.tsx +++ b/ui/src/pages/workflows/WorkflowDetailDesign.tsx @@ -1,12 +1,14 @@ import { useMemo, useRef, useState } from "react"; import { useTranslation } from "react-i18next"; -import { IconArrowBackUp, IconDots } from "@tabler/icons-react"; +import { IconArrowBackUp, IconDots, IconTransferIn, IconTransferOut } from "@tabler/icons-react"; import { useDeepCompareEffect } from "ahooks"; import { Alert, App, Button, Card, Dropdown, Result, Space, theme } from "antd"; import { debounce } from "radash"; import Show from "@/components/Show"; import { WorkflowDesigner, type WorkflowDesignerInstance, WorkflowNodeDrawer, WorkflowToolbar } from "@/components/workflow/designer"; +import WorkflowGraphExportModal from "@/components/workflow/WorkflowGraphExportModal"; +import WorkflowGraphImportModal from "@/components/workflow/WorkflowGraphImportModal"; import { useZustandShallowSelector } from "@/hooks"; import { useWorkflowStore } from "@/stores/workflow"; import { getErrMsg } from "@/utils/error"; @@ -93,8 +95,33 @@ const WorkflowDetailDesign = () => { }); }; + const handleImportClick = async () => { + graphImportModal.open().then(async (graph) => { + const loadingKey = Math.random().toString(36).substring(0, 8); + message.loading({ key: loadingKey, content: t("common.text.saving"), duration: 0 }); + + try { + await workflowStore.orchestrate(graph); + + message.destroy(loadingKey); + message.success(t("common.text.operation_succeeded")); + } catch (err) { + console.error(err); + message.destroy(loadingKey); + notification.error({ message: t("common.text.request_error"), description: getErrMsg(err) }); + } + }); + }; + + const handleExportClick = () => { + graphExportModal.open(workflow.graphDraft!); + }; + const { drawerProps: designerNodeDrawerProps, ...designerNodeDrawer } = WorkflowNodeDrawer.useDrawer(); + const { modalProps: graphImportModalProps, ...graphImportModal } = WorkflowGraphImportModal.useModal(); + const { modalProps: graphExportModalProps, ...graphExportModal } = WorkflowGraphExportModal.useModal(); + return (
{ icon: , onClick: handleRollbackClick, }, + { + type: "divider", + }, + { + key: "import", + label: t("workflow.detail.design.action.import.button"), + icon: , + onClick: handleImportClick, + }, + { + key: "export", + label: t("workflow.detail.design.action.export.button"), + icon: , + onClick: handleExportClick, + }, ], }} trigger={["click"]} @@ -164,6 +206,9 @@ const WorkflowDetailDesign = () => { + + +
); diff --git a/ui/src/utils/validators.ts b/ui/src/utils/validators.ts index 348d96694..63e431fef 100644 --- a/ui/src/utils/validators.ts +++ b/ui/src/utils/validators.ts @@ -14,30 +14,15 @@ export const validDomainName = (value: string, { allowWildcard = false }: { allo }; export const validEmailAddress = (value: string) => { - try { - z.email().parse(value); - return true; - } catch (_) { - return false; - } + return z.email().safeParse(value).success; }; export const validIPv4Address = (value: string) => { - try { - z.ipv4().parse(value); - return true; - } catch (_) { - return false; - } + return z.ipv4().safeParse(value).success; }; export const validIPv6Address = (value: string) => { - try { - z.ipv6().parse(value); - return true; - } catch (_) { - return false; - } + return z.ipv6().safeParse(value).success; }; export const validHttpOrHttpsUrl = (value: string) => {