mirror of
https://github.com/certimate-go/certimate.git
synced 2026-09-24 23:10:13 +08:00
feat(ui): new workflow designer
This commit is contained in:
Generated
+282
-265
@@ -1,25 +1,25 @@
|
||||
{
|
||||
"name": "ui",
|
||||
"name": "@certimate/webui",
|
||||
"version": "0.0.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "ui",
|
||||
"version": "0.0.0",
|
||||
"name": "@certimate/webui",
|
||||
"dependencies": {
|
||||
"@ant-design/pro-components": "^2.8.10",
|
||||
"@codemirror/lang-json": "^6.0.2",
|
||||
"@codemirror/lang-yaml": "^6.1.2",
|
||||
"@codemirror/language": "^6.11.2",
|
||||
"@codemirror/legacy-modes": "^6.5.1",
|
||||
"@flowgram.ai/fixed-layout-editor": "^0.2.28",
|
||||
"@flowgram.ai/document": "^0.3.2",
|
||||
"@flowgram.ai/fixed-layout-editor": "^0.3.2",
|
||||
"@tabler/icons-react": "^3.34.1",
|
||||
"@uiw/codemirror-extensions-basic-setup": "^4.24.1",
|
||||
"@uiw/codemirror-theme-vscode": "^4.24.1",
|
||||
"@uiw/react-codemirror": "^4.24.1",
|
||||
"@uiw/codemirror-extensions-basic-setup": "^4.24.2",
|
||||
"@uiw/codemirror-theme-vscode": "^4.24.2",
|
||||
"@uiw/react-codemirror": "^4.24.2",
|
||||
"ahooks": "^3.9.0",
|
||||
"antd": "^5.26.6",
|
||||
"antd": "^5.26.7",
|
||||
"antd-zod": "^7.0.0",
|
||||
"clsx": "^2.1.1",
|
||||
"cron-parser": "^5.3.0",
|
||||
@@ -36,8 +36,8 @@
|
||||
"react-i18next": "^15.6.1",
|
||||
"react-router-dom": "^7.7.1",
|
||||
"tailwind-merge": "^3.3.1",
|
||||
"zod": "^4.0.10",
|
||||
"zustand": "^5.0.6"
|
||||
"zod": "^4.0.14",
|
||||
"zustand": "^5.0.7"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@eslint/js": "^9.32.0",
|
||||
@@ -54,7 +54,7 @@
|
||||
"eslint": "^9.32.0",
|
||||
"eslint-config-prettier": "^10.1.8",
|
||||
"eslint-import-resolver-typescript": "^4.4.4",
|
||||
"eslint-plugin-better-tailwindcss": "^3.7.2",
|
||||
"eslint-plugin-better-tailwindcss": "^3.7.3",
|
||||
"eslint-plugin-import": "^2.32.0",
|
||||
"eslint-plugin-prettier": "^5.5.3",
|
||||
"eslint-plugin-react-hooks": "^5.2.0",
|
||||
@@ -2784,12 +2784,12 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@flowgram.ai/background-plugin": {
|
||||
"version": "0.2.28",
|
||||
"resolved": "https://registry.npmmirror.com/@flowgram.ai/background-plugin/-/background-plugin-0.2.28.tgz",
|
||||
"integrity": "sha512-GEH0e1TioFQuKTZli7UO0yz171xs2T2XAnoWdxGhuWTcZ3PVyJSwJSF6BPDxfWGgN3kL9JeghbCPgElhwEOKGg==",
|
||||
"version": "0.3.2",
|
||||
"resolved": "https://registry.npmmirror.com/@flowgram.ai/background-plugin/-/background-plugin-0.3.2.tgz",
|
||||
"integrity": "sha512-3BeVeZ3bvhqIfsSVuUpxMBdOON1eY9E+7SlAvs1iWbEgzn8LVg7i6lXdl0BIVlUEIthl7aLH7uxrbb/MAj9Fxg==",
|
||||
"dependencies": {
|
||||
"@flowgram.ai/core": "0.2.28",
|
||||
"@flowgram.ai/utils": "0.2.28",
|
||||
"@flowgram.ai/core": "0.3.2",
|
||||
"@flowgram.ai/utils": "0.3.2",
|
||||
"inversify": "^6.0.1",
|
||||
"reflect-metadata": "~0.2.2"
|
||||
},
|
||||
@@ -2799,11 +2799,11 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@flowgram.ai/command": {
|
||||
"version": "0.2.28",
|
||||
"resolved": "https://registry.npmmirror.com/@flowgram.ai/command/-/command-0.2.28.tgz",
|
||||
"integrity": "sha512-/OMswJnXKm6aHIjwOQfWzCeYCr19x6gA8YNY8z9BpWJa3mPpPMUKjigldvOZVq/LZ3UPLj4EtbW7u4yyfsNCgA==",
|
||||
"version": "0.3.2",
|
||||
"resolved": "https://registry.npmmirror.com/@flowgram.ai/command/-/command-0.3.2.tgz",
|
||||
"integrity": "sha512-P6tuALZrSzjGBAB3BttJWumtmN0NcBJ88dHGsWStMMDW81LYCFKCsybB2wU0YoGVV7viRjCe/szSoQAbp8B2NQ==",
|
||||
"dependencies": {
|
||||
"@flowgram.ai/utils": "0.2.28",
|
||||
"@flowgram.ai/utils": "0.3.2",
|
||||
"inversify": "^6.0.1",
|
||||
"reflect-metadata": "~0.2.2"
|
||||
},
|
||||
@@ -2813,12 +2813,12 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@flowgram.ai/core": {
|
||||
"version": "0.2.28",
|
||||
"resolved": "https://registry.npmmirror.com/@flowgram.ai/core/-/core-0.2.28.tgz",
|
||||
"integrity": "sha512-iVHIGHZTjoI0OJCf2m2ch70jkopWu6P+G1wy4cYMWqWI89lz2grtAEd7Qg8vRHQameLAdoQnAHuSkAXpIIKcaA==",
|
||||
"version": "0.3.2",
|
||||
"resolved": "https://registry.npmmirror.com/@flowgram.ai/core/-/core-0.3.2.tgz",
|
||||
"integrity": "sha512-pb+YP2DKIrTP2woQEbt7cKWnqwXObsMtGhS0DuRgbCIN1+6fLXFjvN2M2QhMRsOPiqY60dWnZWLKtagwPVUDqA==",
|
||||
"dependencies": {
|
||||
"@flowgram.ai/command": "0.2.28",
|
||||
"@flowgram.ai/utils": "0.2.28",
|
||||
"@flowgram.ai/command": "0.3.2",
|
||||
"@flowgram.ai/utils": "0.3.2",
|
||||
"@phosphor/messaging": "^1.3.0",
|
||||
"@tweenjs/tween.js": "^18",
|
||||
"clsx": "^1.1.1",
|
||||
@@ -2858,12 +2858,12 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@flowgram.ai/document": {
|
||||
"version": "0.2.28",
|
||||
"resolved": "https://registry.npmmirror.com/@flowgram.ai/document/-/document-0.2.28.tgz",
|
||||
"integrity": "sha512-HzEG5EZNdtooAaGjCCd4k9cjxoUaMs0j2dowVA2/z8CO42ylqLnk+GJqr8jE+gPxS4muwjxdzEa/OXcgsrlqhw==",
|
||||
"version": "0.3.2",
|
||||
"resolved": "https://registry.npmmirror.com/@flowgram.ai/document/-/document-0.3.2.tgz",
|
||||
"integrity": "sha512-N+VAQzZcNt8PUfB/32cGzMFtUUiKGTzCFMFMElB9ffSWG2stHgueRO3kBWCD8yweArfUofhCTk3eERJ345dj3A==",
|
||||
"dependencies": {
|
||||
"@flowgram.ai/core": "0.2.28",
|
||||
"@flowgram.ai/utils": "0.2.28",
|
||||
"@flowgram.ai/core": "0.3.2",
|
||||
"@flowgram.ai/utils": "0.3.2",
|
||||
"inversify": "^6.0.1",
|
||||
"lodash": "^4.17.21",
|
||||
"nanoid": "^4.0.2",
|
||||
@@ -2888,29 +2888,29 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@flowgram.ai/editor": {
|
||||
"version": "0.2.28",
|
||||
"resolved": "https://registry.npmmirror.com/@flowgram.ai/editor/-/editor-0.2.28.tgz",
|
||||
"integrity": "sha512-QMSuOfFXZHHtnjuuX/pDqoGn/iCKjD6I05wcKUn0LqqbFgyYKp49Ju77IS9rGBs6R1Cj0jqFPl3OLW50sFqLEg==",
|
||||
"version": "0.3.2",
|
||||
"resolved": "https://registry.npmmirror.com/@flowgram.ai/editor/-/editor-0.3.2.tgz",
|
||||
"integrity": "sha512-Vdq53nKUoHQKWtVOmMRcI3DgS8U8pW2jFQRklYfAVhmLxyw4636ZplF8F3/AiAbt5tJ7P9OOs5xI456U1U2dqQ==",
|
||||
"dependencies": {
|
||||
"@flowgram.ai/background-plugin": "0.2.28",
|
||||
"@flowgram.ai/core": "0.2.28",
|
||||
"@flowgram.ai/document": "0.2.28",
|
||||
"@flowgram.ai/form": "0.2.28",
|
||||
"@flowgram.ai/form-core": "0.2.28",
|
||||
"@flowgram.ai/group-plugin": "0.2.28",
|
||||
"@flowgram.ai/history": "0.2.28",
|
||||
"@flowgram.ai/history-node-plugin": "0.2.28",
|
||||
"@flowgram.ai/i18n-plugin": "0.2.28",
|
||||
"@flowgram.ai/materials-plugin": "0.2.28",
|
||||
"@flowgram.ai/node": "0.2.28",
|
||||
"@flowgram.ai/node-core-plugin": "0.2.28",
|
||||
"@flowgram.ai/node-variable-plugin": "0.2.28",
|
||||
"@flowgram.ai/playground-react": "0.2.28",
|
||||
"@flowgram.ai/redux-devtool-plugin": "0.2.28",
|
||||
"@flowgram.ai/renderer": "0.2.28",
|
||||
"@flowgram.ai/shortcuts-plugin": "0.2.28",
|
||||
"@flowgram.ai/utils": "0.2.28",
|
||||
"@flowgram.ai/variable-plugin": "0.2.28",
|
||||
"@flowgram.ai/background-plugin": "0.3.2",
|
||||
"@flowgram.ai/core": "0.3.2",
|
||||
"@flowgram.ai/document": "0.3.2",
|
||||
"@flowgram.ai/form": "0.3.2",
|
||||
"@flowgram.ai/form-core": "0.3.2",
|
||||
"@flowgram.ai/group-plugin": "0.3.2",
|
||||
"@flowgram.ai/history": "0.3.2",
|
||||
"@flowgram.ai/history-node-plugin": "0.3.2",
|
||||
"@flowgram.ai/i18n-plugin": "0.3.2",
|
||||
"@flowgram.ai/materials-plugin": "0.3.2",
|
||||
"@flowgram.ai/node": "0.3.2",
|
||||
"@flowgram.ai/node-core-plugin": "0.3.2",
|
||||
"@flowgram.ai/node-variable-plugin": "0.3.2",
|
||||
"@flowgram.ai/playground-react": "0.3.2",
|
||||
"@flowgram.ai/redux-devtool-plugin": "0.3.2",
|
||||
"@flowgram.ai/renderer": "0.3.2",
|
||||
"@flowgram.ai/shortcuts-plugin": "0.3.2",
|
||||
"@flowgram.ai/utils": "0.3.2",
|
||||
"@flowgram.ai/variable-plugin": "0.3.2",
|
||||
"inversify": "^6.0.1",
|
||||
"reflect-metadata": "~0.2.2"
|
||||
},
|
||||
@@ -2920,13 +2920,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@flowgram.ai/fixed-drag-plugin": {
|
||||
"version": "0.2.28",
|
||||
"resolved": "https://registry.npmmirror.com/@flowgram.ai/fixed-drag-plugin/-/fixed-drag-plugin-0.2.28.tgz",
|
||||
"integrity": "sha512-dNrFBd6G1uFgkhf0LpAC9/7vV8kIQCBuBAmbeUi6EQZkmWsp9rVd4SAdKGqhNLrs6klt8n5oMltv6WJsMCRS6Q==",
|
||||
"version": "0.3.2",
|
||||
"resolved": "https://registry.npmmirror.com/@flowgram.ai/fixed-drag-plugin/-/fixed-drag-plugin-0.3.2.tgz",
|
||||
"integrity": "sha512-XK09rklrkU1EzhmqFBsm/zZGcnnb4LXhNMkol2fDviiq2Aq0UumXL2jRjRaWcFQJVjBawlOd3YciTamSY6xNgg==",
|
||||
"dependencies": {
|
||||
"@flowgram.ai/core": "0.2.28",
|
||||
"@flowgram.ai/document": "0.2.28",
|
||||
"@flowgram.ai/renderer": "0.2.28",
|
||||
"@flowgram.ai/core": "0.3.2",
|
||||
"@flowgram.ai/document": "0.3.2",
|
||||
"@flowgram.ai/renderer": "0.3.2",
|
||||
"inversify": "^6.0.1",
|
||||
"reflect-metadata": "~0.2.2"
|
||||
},
|
||||
@@ -2936,29 +2936,29 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@flowgram.ai/fixed-history-plugin": {
|
||||
"version": "0.2.28",
|
||||
"resolved": "https://registry.npmmirror.com/@flowgram.ai/fixed-history-plugin/-/fixed-history-plugin-0.2.28.tgz",
|
||||
"integrity": "sha512-s9ckjS6NS3ds+FfmvTIvO/02N8rSh+EcSDyKwLaGcrYk6X6Fs1T5h/PyBgGTryJ7EzseqlLTFVBqYXzgbymDtg==",
|
||||
"version": "0.3.2",
|
||||
"resolved": "https://registry.npmmirror.com/@flowgram.ai/fixed-history-plugin/-/fixed-history-plugin-0.3.2.tgz",
|
||||
"integrity": "sha512-27+z5s+9LeRBG+sk5e6mPuqu2dw4nBFV49ouNe/l11r+A+w5j6haZdX2kLR0wrWdJWDu7Dn0y9MQyxwPSyVEmg==",
|
||||
"dependencies": {
|
||||
"@flowgram.ai/core": "0.2.28",
|
||||
"@flowgram.ai/document": "0.2.28",
|
||||
"@flowgram.ai/form-core": "0.2.28",
|
||||
"@flowgram.ai/history": "0.2.28",
|
||||
"@flowgram.ai/utils": "0.2.28",
|
||||
"@flowgram.ai/core": "0.3.2",
|
||||
"@flowgram.ai/document": "0.3.2",
|
||||
"@flowgram.ai/form-core": "0.3.2",
|
||||
"@flowgram.ai/history": "0.3.2",
|
||||
"@flowgram.ai/utils": "0.3.2",
|
||||
"inversify": "^6.0.1",
|
||||
"lodash": "^4.17.21",
|
||||
"reflect-metadata": "~0.2.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@flowgram.ai/fixed-layout-core": {
|
||||
"version": "0.2.28",
|
||||
"resolved": "https://registry.npmmirror.com/@flowgram.ai/fixed-layout-core/-/fixed-layout-core-0.2.28.tgz",
|
||||
"integrity": "sha512-pIa7vaksAmBZm3aaDMkpxB8RsS+fHGntDjGBWuP5tKJkjMKSXyPDzA7vGOwcVLyX3OrYiMD8ypuCDOGPruAr2A==",
|
||||
"version": "0.3.2",
|
||||
"resolved": "https://registry.npmmirror.com/@flowgram.ai/fixed-layout-core/-/fixed-layout-core-0.3.2.tgz",
|
||||
"integrity": "sha512-e+J/oxFs4HD8FOdMHh79bXLEp+7gzoCk+42fMTAJ4Or4HEIkep8gRTpFxB0fINnWxpw3CpZZFnhMLIuyR5R46g==",
|
||||
"dependencies": {
|
||||
"@flowgram.ai/core": "0.2.28",
|
||||
"@flowgram.ai/document": "0.2.28",
|
||||
"@flowgram.ai/renderer": "0.2.28",
|
||||
"@flowgram.ai/utils": "0.2.28",
|
||||
"@flowgram.ai/core": "0.3.2",
|
||||
"@flowgram.ai/document": "0.3.2",
|
||||
"@flowgram.ai/renderer": "0.3.2",
|
||||
"@flowgram.ai/utils": "0.3.2",
|
||||
"inversify": "^6.0.1",
|
||||
"lodash": "^4.17.21",
|
||||
"reflect-metadata": "~0.2.2"
|
||||
@@ -2969,19 +2969,19 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@flowgram.ai/fixed-layout-editor": {
|
||||
"version": "0.2.28",
|
||||
"resolved": "https://registry.npmmirror.com/@flowgram.ai/fixed-layout-editor/-/fixed-layout-editor-0.2.28.tgz",
|
||||
"integrity": "sha512-PpLQXWjC2L7eqVzNdt7Z4DxEh6GcIJyTrOQE0FjUB3XuS2IW7ciHogs3gEfAVpoFbZGj8tzIcs6IMUu1GxjeqQ==",
|
||||
"version": "0.3.2",
|
||||
"resolved": "https://registry.npmmirror.com/@flowgram.ai/fixed-layout-editor/-/fixed-layout-editor-0.3.2.tgz",
|
||||
"integrity": "sha512-wZxyYOt4f9CFX+iUiq5J4tyn4K9YmfcprYs26i7CB2D542vvpREVWXzl/FdTiylcW6qTTGcFAsQkdVdmd33xEg==",
|
||||
"dependencies": {
|
||||
"@flowgram.ai/core": "0.2.28",
|
||||
"@flowgram.ai/editor": "0.2.28",
|
||||
"@flowgram.ai/fixed-drag-plugin": "0.2.28",
|
||||
"@flowgram.ai/fixed-history-plugin": "0.2.28",
|
||||
"@flowgram.ai/fixed-layout-core": "0.2.28",
|
||||
"@flowgram.ai/history": "0.2.28",
|
||||
"@flowgram.ai/reactive": "0.2.28",
|
||||
"@flowgram.ai/select-box-plugin": "0.2.28",
|
||||
"@flowgram.ai/utils": "0.2.28",
|
||||
"@flowgram.ai/core": "0.3.2",
|
||||
"@flowgram.ai/editor": "0.3.2",
|
||||
"@flowgram.ai/fixed-drag-plugin": "0.3.2",
|
||||
"@flowgram.ai/fixed-history-plugin": "0.3.2",
|
||||
"@flowgram.ai/fixed-layout-core": "0.3.2",
|
||||
"@flowgram.ai/history": "0.3.2",
|
||||
"@flowgram.ai/reactive": "0.3.2",
|
||||
"@flowgram.ai/select-box-plugin": "0.3.2",
|
||||
"@flowgram.ai/utils": "0.3.2",
|
||||
"inversify": "^6.0.1",
|
||||
"reflect-metadata": "~0.2.2"
|
||||
},
|
||||
@@ -2991,12 +2991,12 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@flowgram.ai/form": {
|
||||
"version": "0.2.28",
|
||||
"resolved": "https://registry.npmmirror.com/@flowgram.ai/form/-/form-0.2.28.tgz",
|
||||
"integrity": "sha512-OgMDayBmHDUEjb6xWZeDmyn8BKZZazEsas3WJKlm2r6zaobygxE5K5NzJ/oy3OyLTgMtpYq3JOWZPgGu16ZRHg==",
|
||||
"version": "0.3.2",
|
||||
"resolved": "https://registry.npmmirror.com/@flowgram.ai/form/-/form-0.3.2.tgz",
|
||||
"integrity": "sha512-OHp6IhOQUcW3Hw/BWrxi4Kn/UTB96BkQgjl2mQ0o+HyqTwzGa9wNtLkNWqIK/CogD/l31kRp39L6rFH71S2AgQ==",
|
||||
"dependencies": {
|
||||
"@flowgram.ai/reactive": "0.2.28",
|
||||
"@flowgram.ai/utils": "0.2.28",
|
||||
"@flowgram.ai/reactive": "0.3.2",
|
||||
"@flowgram.ai/utils": "0.3.2",
|
||||
"fast-equals": "^2.0.0",
|
||||
"lodash": "^4.17.21",
|
||||
"nanoid": "^4.0.2"
|
||||
@@ -3007,13 +3007,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@flowgram.ai/form-core": {
|
||||
"version": "0.2.28",
|
||||
"resolved": "https://registry.npmmirror.com/@flowgram.ai/form-core/-/form-core-0.2.28.tgz",
|
||||
"integrity": "sha512-JpapezHkKEl4hgNnhBo/5mBUcgAv7aUQpp3ypZ0g/0L9qLd9vB3PjBw7mL+K4t+954ckJMLW0AHJyqeWKXpO4g==",
|
||||
"version": "0.3.2",
|
||||
"resolved": "https://registry.npmmirror.com/@flowgram.ai/form-core/-/form-core-0.3.2.tgz",
|
||||
"integrity": "sha512-RjipVRN/pKqDF27vL4GhtRpRtlcDlZZgv2chEehd1tnedJRmgH30REN3ET9ya+cF2Lte9z9ALdAD6KMfCqdySw==",
|
||||
"dependencies": {
|
||||
"@flowgram.ai/core": "0.2.28",
|
||||
"@flowgram.ai/document": "0.2.28",
|
||||
"@flowgram.ai/utils": "0.2.28",
|
||||
"@flowgram.ai/core": "0.3.2",
|
||||
"@flowgram.ai/document": "0.3.2",
|
||||
"@flowgram.ai/utils": "0.3.2",
|
||||
"inversify": "^6.0.1",
|
||||
"lodash": "^4.17.21",
|
||||
"reflect-metadata": "~0.2.2"
|
||||
@@ -3041,16 +3041,16 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@flowgram.ai/free-layout-core": {
|
||||
"version": "0.2.28",
|
||||
"resolved": "https://registry.npmmirror.com/@flowgram.ai/free-layout-core/-/free-layout-core-0.2.28.tgz",
|
||||
"integrity": "sha512-oGLr4dTggl1e7h/usm0Hs4uYjiAKOU8ZY6W9xpEgvXJkRngiGDgh1+S3Sh7WfxvDQeHkaQhGRgcKNqECGqL1LQ==",
|
||||
"version": "0.3.2",
|
||||
"resolved": "https://registry.npmmirror.com/@flowgram.ai/free-layout-core/-/free-layout-core-0.3.2.tgz",
|
||||
"integrity": "sha512-xvoneqfmNqsfzLotqdikBOfMDXC173G0Gqs5XxDZv3fpCuUMyy3eI0KrnoJZMIp6BD5buDmBrm6vk9D8vE1WnQ==",
|
||||
"dependencies": {
|
||||
"@flowgram.ai/core": "0.2.28",
|
||||
"@flowgram.ai/document": "0.2.28",
|
||||
"@flowgram.ai/form-core": "0.2.28",
|
||||
"@flowgram.ai/node": "0.2.28",
|
||||
"@flowgram.ai/reactive": "0.2.28",
|
||||
"@flowgram.ai/utils": "0.2.28",
|
||||
"@flowgram.ai/core": "0.3.2",
|
||||
"@flowgram.ai/document": "0.3.2",
|
||||
"@flowgram.ai/form-core": "0.3.2",
|
||||
"@flowgram.ai/node": "0.3.2",
|
||||
"@flowgram.ai/reactive": "0.3.2",
|
||||
"@flowgram.ai/utils": "0.3.2",
|
||||
"inversify": "^6.0.1",
|
||||
"lodash-es": "^4.17.21",
|
||||
"nanoid": "^4.0.2",
|
||||
@@ -3079,14 +3079,14 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@flowgram.ai/group-plugin": {
|
||||
"version": "0.2.28",
|
||||
"resolved": "https://registry.npmmirror.com/@flowgram.ai/group-plugin/-/group-plugin-0.2.28.tgz",
|
||||
"integrity": "sha512-Gk4A5oCOxF45Htf68OIR2yZyQR2416HMDsDISWCuAZiV/LafGDSNwRWiUv/6tpyUpwT1uHSipkTFSRwAWApO8g==",
|
||||
"version": "0.3.2",
|
||||
"resolved": "https://registry.npmmirror.com/@flowgram.ai/group-plugin/-/group-plugin-0.3.2.tgz",
|
||||
"integrity": "sha512-9fn4Sdu6uDqUPz00VBJu8F4CEdkQECg2ZIDETZLU8iG49eeX1qCGPOai7MRMGY83LcEDIR0Sk5w0Z7i4wM5mXQ==",
|
||||
"dependencies": {
|
||||
"@flowgram.ai/core": "0.2.28",
|
||||
"@flowgram.ai/document": "0.2.28",
|
||||
"@flowgram.ai/renderer": "0.2.28",
|
||||
"@flowgram.ai/utils": "0.2.28",
|
||||
"@flowgram.ai/core": "0.3.2",
|
||||
"@flowgram.ai/document": "0.3.2",
|
||||
"@flowgram.ai/renderer": "0.3.2",
|
||||
"@flowgram.ai/utils": "0.3.2",
|
||||
"inversify": "^6.0.1",
|
||||
"reflect-metadata": "~0.2.2"
|
||||
},
|
||||
@@ -3096,12 +3096,12 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@flowgram.ai/history": {
|
||||
"version": "0.2.28",
|
||||
"resolved": "https://registry.npmmirror.com/@flowgram.ai/history/-/history-0.2.28.tgz",
|
||||
"integrity": "sha512-Em5yq2ZmP5lRglrdIfHe54zt3PSMM3BUj95jge5rC1uxNes7vhu5jaKhVIK6aehu9m4pOY8ZJ7/Dudr3w04aJQ==",
|
||||
"version": "0.3.2",
|
||||
"resolved": "https://registry.npmmirror.com/@flowgram.ai/history/-/history-0.3.2.tgz",
|
||||
"integrity": "sha512-dL4v3tbUkZ4q4ph3LsCHQV5gV+kyYoPfjcR+ok4SyMoLzOg46YsAri7KVhNDR2El77EzGDPiCZ+9ZSQJqOX/eQ==",
|
||||
"dependencies": {
|
||||
"@flowgram.ai/core": "0.2.28",
|
||||
"@flowgram.ai/utils": "0.2.28",
|
||||
"@flowgram.ai/core": "0.3.2",
|
||||
"@flowgram.ai/utils": "0.3.2",
|
||||
"inversify": "^6.0.1",
|
||||
"lodash": "^4.17.21",
|
||||
"nanoid": "^4.0.2",
|
||||
@@ -3109,17 +3109,17 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@flowgram.ai/history-node-plugin": {
|
||||
"version": "0.2.28",
|
||||
"resolved": "https://registry.npmmirror.com/@flowgram.ai/history-node-plugin/-/history-node-plugin-0.2.28.tgz",
|
||||
"integrity": "sha512-NnFt6ocTvGwWjgluUuvJL+Xivj75LrPEFPqhC+GcmciiNmD0HkhW/ojO0aBKv8/Ppemfjo840dhSyxAxyInPsw==",
|
||||
"version": "0.3.2",
|
||||
"resolved": "https://registry.npmmirror.com/@flowgram.ai/history-node-plugin/-/history-node-plugin-0.3.2.tgz",
|
||||
"integrity": "sha512-Q4kMsq11Z7tpHD9mIwPN3jQMrFter7xzbMznr36G4TLRlPV9bfsWNBhLbBto1WEhbiphQ/TTEiBjWQrxlpAkXQ==",
|
||||
"dependencies": {
|
||||
"@flowgram.ai/core": "0.2.28",
|
||||
"@flowgram.ai/document": "0.2.28",
|
||||
"@flowgram.ai/form": "0.2.28",
|
||||
"@flowgram.ai/form-core": "0.2.28",
|
||||
"@flowgram.ai/history": "0.2.28",
|
||||
"@flowgram.ai/node": "0.2.28",
|
||||
"@flowgram.ai/utils": "0.2.28",
|
||||
"@flowgram.ai/core": "0.3.2",
|
||||
"@flowgram.ai/document": "0.3.2",
|
||||
"@flowgram.ai/form": "0.3.2",
|
||||
"@flowgram.ai/form-core": "0.3.2",
|
||||
"@flowgram.ai/history": "0.3.2",
|
||||
"@flowgram.ai/node": "0.3.2",
|
||||
"@flowgram.ai/utils": "0.3.2",
|
||||
"inversify": "^6.0.1",
|
||||
"lodash": "^4.17.21",
|
||||
"reflect-metadata": "~0.2.2"
|
||||
@@ -3148,32 +3148,32 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@flowgram.ai/i18n": {
|
||||
"version": "0.2.28",
|
||||
"resolved": "https://registry.npmmirror.com/@flowgram.ai/i18n/-/i18n-0.2.28.tgz",
|
||||
"integrity": "sha512-FsFPlLa834zTBhLKgw6PWm+SoLvsPp2xL9XiphXdjD3kDgLvAsotiO/mdGGuwC+nNMByo2MsUgf7gS7BIVMuTw==",
|
||||
"version": "0.3.2",
|
||||
"resolved": "https://registry.npmmirror.com/@flowgram.ai/i18n/-/i18n-0.3.2.tgz",
|
||||
"integrity": "sha512-k+9f6gwDh4YTL91BfliRL8IEgzGZ4ALBAuFmGVucpx+01NEYAoqwF1+g4u9udlXX9Ncny5DDMNNka1C3B5m46Q==",
|
||||
"dependencies": {
|
||||
"@flowgram.ai/utils": "0.2.28",
|
||||
"@flowgram.ai/utils": "0.3.2",
|
||||
"i18n-js": "^4.5.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@flowgram.ai/i18n-plugin": {
|
||||
"version": "0.2.28",
|
||||
"resolved": "https://registry.npmmirror.com/@flowgram.ai/i18n-plugin/-/i18n-plugin-0.2.28.tgz",
|
||||
"integrity": "sha512-U0AjDZekzx7Z8ttxSI5JZWE6A2rzQncILYQ4ALTPpaS6nFqna7ju2qEedcUAIf/xo4gZiYugcQc511lvqtnhLg==",
|
||||
"version": "0.3.2",
|
||||
"resolved": "https://registry.npmmirror.com/@flowgram.ai/i18n-plugin/-/i18n-plugin-0.3.2.tgz",
|
||||
"integrity": "sha512-1Zyk6Sh+W62FAF7oP/p+fJ1JxF7iqpoKpOCC65VEHKVQ1TAbHg6fyOoSxNTE7wjnATvt6c3fDw7AYcevIlcRSA==",
|
||||
"dependencies": {
|
||||
"@flowgram.ai/core": "0.2.28",
|
||||
"@flowgram.ai/i18n": "0.2.28"
|
||||
"@flowgram.ai/core": "0.3.2",
|
||||
"@flowgram.ai/i18n": "0.3.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@flowgram.ai/materials-plugin": {
|
||||
"version": "0.2.28",
|
||||
"resolved": "https://registry.npmmirror.com/@flowgram.ai/materials-plugin/-/materials-plugin-0.2.28.tgz",
|
||||
"integrity": "sha512-2s50E0MZwHnE4QVS564d2abd29iMv70r7cnRWeLtc6w91Y+mKHQWM5noeoi/diggIWUmj6w4pFYDnSOcN4DeGA==",
|
||||
"version": "0.3.2",
|
||||
"resolved": "https://registry.npmmirror.com/@flowgram.ai/materials-plugin/-/materials-plugin-0.3.2.tgz",
|
||||
"integrity": "sha512-svG1M1uMIeavXYj8UV5Mv2glcumQ7AYg4/U6xVGhMJFJmnAWNlNVQCqz5PFRkjyIH6FwW/xJVh03TSqpR2Svkw==",
|
||||
"dependencies": {
|
||||
"@flowgram.ai/core": "0.2.28",
|
||||
"@flowgram.ai/form-core": "0.2.28",
|
||||
"@flowgram.ai/renderer": "0.2.28",
|
||||
"@flowgram.ai/utils": "0.2.28",
|
||||
"@flowgram.ai/core": "0.3.2",
|
||||
"@flowgram.ai/form-core": "0.3.2",
|
||||
"@flowgram.ai/renderer": "0.3.2",
|
||||
"@flowgram.ai/utils": "0.3.2",
|
||||
"inversify": "^6.0.1",
|
||||
"reflect-metadata": "~0.2.2"
|
||||
},
|
||||
@@ -3184,15 +3184,15 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@flowgram.ai/node": {
|
||||
"version": "0.2.28",
|
||||
"resolved": "https://registry.npmmirror.com/@flowgram.ai/node/-/node-0.2.28.tgz",
|
||||
"integrity": "sha512-XZ+kKjZdOFYdC8XL2L+BCYAa/wVLnb2dD/lfedMd9Fnf0QaJaQupwhwPnLtmL4HcdB8ro8uBICSIgVGXED66Uw==",
|
||||
"version": "0.3.2",
|
||||
"resolved": "https://registry.npmmirror.com/@flowgram.ai/node/-/node-0.3.2.tgz",
|
||||
"integrity": "sha512-jVr5zlL/vab7g1JSv69fIivKZAb+kPDQJugeNm9Ux9vjaF3nfssFRPeeRCj8JWMw4hqdRUnTms9Mx1udrCLXTg==",
|
||||
"dependencies": {
|
||||
"@flowgram.ai/core": "0.2.28",
|
||||
"@flowgram.ai/document": "0.2.28",
|
||||
"@flowgram.ai/form": "0.2.28",
|
||||
"@flowgram.ai/form-core": "0.2.28",
|
||||
"@flowgram.ai/utils": "0.2.28",
|
||||
"@flowgram.ai/core": "0.3.2",
|
||||
"@flowgram.ai/document": "0.3.2",
|
||||
"@flowgram.ai/form": "0.3.2",
|
||||
"@flowgram.ai/form-core": "0.3.2",
|
||||
"@flowgram.ai/utils": "0.3.2",
|
||||
"inversify": "^6.0.1",
|
||||
"lodash": "^4.17.21",
|
||||
"nanoid": "^4.0.2",
|
||||
@@ -3204,16 +3204,16 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@flowgram.ai/node-core-plugin": {
|
||||
"version": "0.2.28",
|
||||
"resolved": "https://registry.npmmirror.com/@flowgram.ai/node-core-plugin/-/node-core-plugin-0.2.28.tgz",
|
||||
"integrity": "sha512-72R6APzlW2Yy1od2QibIhSW3TW+xz44s0+k4rzJ+EZW0gHEwtmo+OCQLo19WM0CrBFjAomTyK/2J5L6a1mp9OA==",
|
||||
"version": "0.3.2",
|
||||
"resolved": "https://registry.npmmirror.com/@flowgram.ai/node-core-plugin/-/node-core-plugin-0.3.2.tgz",
|
||||
"integrity": "sha512-pSOKiVXSSL4u+7REMfK57n29Kpx7iuH///FAU9/1VdpjtnwLH25F/RBF8fyR+VLqdtwTfhqz2A7OjGMqIardmg==",
|
||||
"dependencies": {
|
||||
"@flowgram.ai/core": "0.2.28",
|
||||
"@flowgram.ai/document": "0.2.28",
|
||||
"@flowgram.ai/form-core": "0.2.28",
|
||||
"@flowgram.ai/node": "0.2.28",
|
||||
"@flowgram.ai/renderer": "0.2.28",
|
||||
"@flowgram.ai/utils": "0.2.28",
|
||||
"@flowgram.ai/core": "0.3.2",
|
||||
"@flowgram.ai/document": "0.3.2",
|
||||
"@flowgram.ai/form-core": "0.3.2",
|
||||
"@flowgram.ai/node": "0.3.2",
|
||||
"@flowgram.ai/renderer": "0.3.2",
|
||||
"@flowgram.ai/utils": "0.3.2",
|
||||
"inversify": "^6.0.1",
|
||||
"reflect-metadata": "~0.2.2"
|
||||
},
|
||||
@@ -3224,16 +3224,16 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@flowgram.ai/node-variable-plugin": {
|
||||
"version": "0.2.28",
|
||||
"resolved": "https://registry.npmmirror.com/@flowgram.ai/node-variable-plugin/-/node-variable-plugin-0.2.28.tgz",
|
||||
"integrity": "sha512-3GpLS6LG1w6Y24TJrXnxSa8BnMhs5sgjqv9VgBdx5DFQRynNtRV52bd3X+eNarNFMMcpEqTGiybrvRqmf84K/w==",
|
||||
"version": "0.3.2",
|
||||
"resolved": "https://registry.npmmirror.com/@flowgram.ai/node-variable-plugin/-/node-variable-plugin-0.3.2.tgz",
|
||||
"integrity": "sha512-GjSqMXv3IqZs6mbiSbHTk/anLQFHl6ozdqM1iCZoOn4V15HW7gJMvXHe3VGwsGxAunsgrbCkkciiZmayWSN9Xg==",
|
||||
"dependencies": {
|
||||
"@flowgram.ai/core": "0.2.28",
|
||||
"@flowgram.ai/document": "0.2.28",
|
||||
"@flowgram.ai/form-core": "0.2.28",
|
||||
"@flowgram.ai/node": "0.2.28",
|
||||
"@flowgram.ai/utils": "0.2.28",
|
||||
"@flowgram.ai/variable-plugin": "0.2.28",
|
||||
"@flowgram.ai/core": "0.3.2",
|
||||
"@flowgram.ai/document": "0.3.2",
|
||||
"@flowgram.ai/form-core": "0.3.2",
|
||||
"@flowgram.ai/node": "0.3.2",
|
||||
"@flowgram.ai/utils": "0.3.2",
|
||||
"@flowgram.ai/variable-plugin": "0.3.2",
|
||||
"inversify": "^6.0.1",
|
||||
"lodash": "^4.17.21",
|
||||
"reflect-metadata": "~0.2.2"
|
||||
@@ -3262,14 +3262,14 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@flowgram.ai/playground-react": {
|
||||
"version": "0.2.28",
|
||||
"resolved": "https://registry.npmmirror.com/@flowgram.ai/playground-react/-/playground-react-0.2.28.tgz",
|
||||
"integrity": "sha512-VvUewy+Av1XHeUa5CBPumkSZR3J/M0m4ME9na6hTfzs7bigKjU8rk+X7397JOWswUBBxGod3Lrw+ujttxxvNag==",
|
||||
"version": "0.3.2",
|
||||
"resolved": "https://registry.npmmirror.com/@flowgram.ai/playground-react/-/playground-react-0.3.2.tgz",
|
||||
"integrity": "sha512-dbuuLF+UKCaIgvlVLd1TbM9t13bePKc+BrFRcYvriXZTAbKI6XvWUD7kQ1hIydU5GgTrhpZEWR6WnrxAvEBlrA==",
|
||||
"dependencies": {
|
||||
"@flowgram.ai/background-plugin": "0.2.28",
|
||||
"@flowgram.ai/core": "0.2.28",
|
||||
"@flowgram.ai/shortcuts-plugin": "0.2.28",
|
||||
"@flowgram.ai/utils": "0.2.28",
|
||||
"@flowgram.ai/background-plugin": "0.3.2",
|
||||
"@flowgram.ai/core": "0.3.2",
|
||||
"@flowgram.ai/shortcuts-plugin": "0.3.2",
|
||||
"@flowgram.ai/utils": "0.3.2",
|
||||
"inversify": "^6.0.1",
|
||||
"reflect-metadata": "~0.2.2"
|
||||
},
|
||||
@@ -3279,11 +3279,11 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@flowgram.ai/reactive": {
|
||||
"version": "0.2.28",
|
||||
"resolved": "https://registry.npmmirror.com/@flowgram.ai/reactive/-/reactive-0.2.28.tgz",
|
||||
"integrity": "sha512-WC8FWI4iBhrrip8If1ghFUhqBxGcHExE4rc6Erx1QQqN7YNFII5/cKF1wAaEgQ99rSVGFxIxjkwrUOjNXnnS6g==",
|
||||
"version": "0.3.2",
|
||||
"resolved": "https://registry.npmmirror.com/@flowgram.ai/reactive/-/reactive-0.3.2.tgz",
|
||||
"integrity": "sha512-U39SOidMenXvcKRgWZ01L7NGCefDwE90S7nil1/1yJ2bqAT9YLpmIY07Sw474sepeZgx2R9HDStuVpLpZOgq0Q==",
|
||||
"dependencies": {
|
||||
"@flowgram.ai/utils": "0.2.28"
|
||||
"@flowgram.ai/utils": "0.3.2"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": ">=16.8",
|
||||
@@ -3291,12 +3291,12 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@flowgram.ai/redux-devtool-plugin": {
|
||||
"version": "0.2.28",
|
||||
"resolved": "https://registry.npmmirror.com/@flowgram.ai/redux-devtool-plugin/-/redux-devtool-plugin-0.2.28.tgz",
|
||||
"integrity": "sha512-D8C0sWzGAFpFElacEHwONRr2vezcLa3jT6SRtYEbQ0poSA6HLPPYSigRjug8A19jd9NayCVulPm1M59GpuZF3Q==",
|
||||
"version": "0.3.2",
|
||||
"resolved": "https://registry.npmmirror.com/@flowgram.ai/redux-devtool-plugin/-/redux-devtool-plugin-0.3.2.tgz",
|
||||
"integrity": "sha512-yPeQYWK3juW+TeSWYDxjcpjSH1MlbXGpumKaoX4dFfoeNatVVzH77z3R345jLeJv2SwB0bHC7djfp6Dlb2vIsQ==",
|
||||
"dependencies": {
|
||||
"@flowgram.ai/core": "0.2.28",
|
||||
"@flowgram.ai/variable-core": "0.2.28",
|
||||
"@flowgram.ai/core": "0.3.2",
|
||||
"@flowgram.ai/variable-core": "0.3.2",
|
||||
"inversify": "^6.0.1",
|
||||
"reflect-metadata": "~0.2.2",
|
||||
"styled-components": "^5"
|
||||
@@ -3351,14 +3351,14 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@flowgram.ai/renderer": {
|
||||
"version": "0.2.28",
|
||||
"resolved": "https://registry.npmmirror.com/@flowgram.ai/renderer/-/renderer-0.2.28.tgz",
|
||||
"integrity": "sha512-7AVVZqsUFeNvq339UfUSohk0ByVDq9ocllODScbeIAVWGZyNjG0tyJyaIEoCYrSqh2l+78C+AsaaBJvOPZG5bg==",
|
||||
"version": "0.3.2",
|
||||
"resolved": "https://registry.npmmirror.com/@flowgram.ai/renderer/-/renderer-0.3.2.tgz",
|
||||
"integrity": "sha512-Pzc43vzAxwpXjJrUkuMopgMMxkplarSlWPw3KdBd9wSIroAslBI6i7z7x3WljfGTJPHGt50EI24tLKTZzGECeQ==",
|
||||
"dependencies": {
|
||||
"@flowgram.ai/core": "0.2.28",
|
||||
"@flowgram.ai/document": "0.2.28",
|
||||
"@flowgram.ai/i18n": "0.2.28",
|
||||
"@flowgram.ai/utils": "0.2.28",
|
||||
"@flowgram.ai/core": "0.3.2",
|
||||
"@flowgram.ai/document": "0.3.2",
|
||||
"@flowgram.ai/i18n": "0.3.2",
|
||||
"@flowgram.ai/utils": "0.3.2",
|
||||
"inversify": "^6.0.1",
|
||||
"lodash": "^4.17.21",
|
||||
"reflect-metadata": "~0.2.2"
|
||||
@@ -3369,12 +3369,12 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@flowgram.ai/select-box-plugin": {
|
||||
"version": "0.2.28",
|
||||
"resolved": "https://registry.npmmirror.com/@flowgram.ai/select-box-plugin/-/select-box-plugin-0.2.28.tgz",
|
||||
"integrity": "sha512-xIPLKpT3hUtlHzvSOqeu8yqhVJDL7En8cRawKWwJjBs0nrFXEGryPnMCrsmbF1JwTFZrvKrznJacocqmiLwI3A==",
|
||||
"version": "0.3.2",
|
||||
"resolved": "https://registry.npmmirror.com/@flowgram.ai/select-box-plugin/-/select-box-plugin-0.3.2.tgz",
|
||||
"integrity": "sha512-mM58wToixwB/lfGtojwYPgip/sh6Go3+caEH4Eo447dlf88R6bSjWxThZG689QFsY6ZSQEfchg4GO1eYERvOwQ==",
|
||||
"dependencies": {
|
||||
"@flowgram.ai/core": "0.2.28",
|
||||
"@flowgram.ai/renderer": "0.2.28",
|
||||
"@flowgram.ai/core": "0.3.2",
|
||||
"@flowgram.ai/renderer": "0.3.2",
|
||||
"inversify": "^6.0.1",
|
||||
"reflect-metadata": "~0.2.2"
|
||||
},
|
||||
@@ -3384,13 +3384,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@flowgram.ai/shortcuts-plugin": {
|
||||
"version": "0.2.28",
|
||||
"resolved": "https://registry.npmmirror.com/@flowgram.ai/shortcuts-plugin/-/shortcuts-plugin-0.2.28.tgz",
|
||||
"integrity": "sha512-S3XkBmZnQ4FZ+A7sD56v6L2UFOhMSwqTmA4S1G8XqMvmbfHjUjR/OxojfQw/MSd5QNygD4ikBesPoj6FMP45FQ==",
|
||||
"version": "0.3.2",
|
||||
"resolved": "https://registry.npmmirror.com/@flowgram.ai/shortcuts-plugin/-/shortcuts-plugin-0.3.2.tgz",
|
||||
"integrity": "sha512-D5Z3CAd61c2e0bQ9n8ULFAK1yFqV9q7UpMQ89IAu+armcT6w0XORC0CP/c7ojoBvGKYaWMH1gZf0NlQHiT6CZQ==",
|
||||
"dependencies": {
|
||||
"@flowgram.ai/core": "0.2.28",
|
||||
"@flowgram.ai/document": "0.2.28",
|
||||
"@flowgram.ai/utils": "0.2.28",
|
||||
"@flowgram.ai/core": "0.3.2",
|
||||
"@flowgram.ai/document": "0.3.2",
|
||||
"@flowgram.ai/utils": "0.3.2",
|
||||
"inversify": "^6.0.1",
|
||||
"reflect-metadata": "~0.2.2",
|
||||
"styled-components": "^5"
|
||||
@@ -3445,9 +3445,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@flowgram.ai/utils": {
|
||||
"version": "0.2.28",
|
||||
"resolved": "https://registry.npmmirror.com/@flowgram.ai/utils/-/utils-0.2.28.tgz",
|
||||
"integrity": "sha512-HG4LpVo3gUcTp/tJCe72vZ9nByRXgkvp2wYyFFD3ZnGvEL8V3PIRwHGvBMP0UqgYBPHOZyqOZhOenP73msvkwg==",
|
||||
"version": "0.3.2",
|
||||
"resolved": "https://registry.npmmirror.com/@flowgram.ai/utils/-/utils-0.3.2.tgz",
|
||||
"integrity": "sha512-XVmiYft8JfrZsV5M2M8i2AMjhz6Fe4Euv0gOuLiSsZL1ZnR95IHzEHuvSd98of4srcUL+b/RcwB2BMXH+/EJ9w==",
|
||||
"dependencies": {
|
||||
"clsx": "^1.1.1",
|
||||
"inversify": "^6.0.1",
|
||||
@@ -3485,18 +3485,18 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@flowgram.ai/variable-core": {
|
||||
"version": "0.2.28",
|
||||
"resolved": "https://registry.npmmirror.com/@flowgram.ai/variable-core/-/variable-core-0.2.28.tgz",
|
||||
"integrity": "sha512-Au6UCupM1d044w++GMv2HC4YGc2nsDNBdem71gjxcDLowGo9FDgKF1Fxh2E+UfCv9QfAcShJzCg0R4EtCpEsuQ==",
|
||||
"version": "0.3.2",
|
||||
"resolved": "https://registry.npmmirror.com/@flowgram.ai/variable-core/-/variable-core-0.3.2.tgz",
|
||||
"integrity": "sha512-95E7akQXzPItx7H/tKT4g5CZ0gg6OCsBou13OfotivZS1Yn5GxtqOeWGz7EHUZwurzB9w6o30VR6Y8vuk049Bg==",
|
||||
"dependencies": {
|
||||
"@flowgram.ai/core": "0.2.28",
|
||||
"@flowgram.ai/utils": "0.2.28",
|
||||
"@flowgram.ai/core": "0.3.2",
|
||||
"@flowgram.ai/utils": "0.3.2",
|
||||
"fast-equals": "^2.0.0",
|
||||
"inversify": "^6.0.1",
|
||||
"lodash": "^4.17.21",
|
||||
"nanoid": "^4.0.2",
|
||||
"reflect-metadata": "~0.2.2",
|
||||
"rxjs": "^7.8.1"
|
||||
"rxjs": "^7.8.2"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": ">=16.8",
|
||||
@@ -3521,15 +3521,15 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@flowgram.ai/variable-layout": {
|
||||
"version": "0.2.28",
|
||||
"resolved": "https://registry.npmmirror.com/@flowgram.ai/variable-layout/-/variable-layout-0.2.28.tgz",
|
||||
"integrity": "sha512-31mxxnLJFanmdMkMvl6WzGV+CO15gPT0jnMsf0Mqp+s7qadtwuHcwzsDEUU9RKR+d/oXuGBpNGrdtQJzBh2JMQ==",
|
||||
"version": "0.3.2",
|
||||
"resolved": "https://registry.npmmirror.com/@flowgram.ai/variable-layout/-/variable-layout-0.3.2.tgz",
|
||||
"integrity": "sha512-vX9lGLl5yY0VAYv5Bua3jqkh2M80+YB/4Qea0crkPAWzg1eVQcG4zbnR+Wc6c3wNz9cTpT+vq+2kiTsP5+95eQ==",
|
||||
"dependencies": {
|
||||
"@flowgram.ai/core": "0.2.28",
|
||||
"@flowgram.ai/document": "0.2.28",
|
||||
"@flowgram.ai/free-layout-core": "0.2.28",
|
||||
"@flowgram.ai/utils": "0.2.28",
|
||||
"@flowgram.ai/variable-core": "0.2.28",
|
||||
"@flowgram.ai/core": "0.3.2",
|
||||
"@flowgram.ai/document": "0.3.2",
|
||||
"@flowgram.ai/free-layout-core": "0.3.2",
|
||||
"@flowgram.ai/utils": "0.3.2",
|
||||
"@flowgram.ai/variable-core": "0.3.2",
|
||||
"inversify": "^6.0.1",
|
||||
"reflect-metadata": "~0.2.2",
|
||||
"styled-components": "^5"
|
||||
@@ -3584,16 +3584,16 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@flowgram.ai/variable-plugin": {
|
||||
"version": "0.2.28",
|
||||
"resolved": "https://registry.npmmirror.com/@flowgram.ai/variable-plugin/-/variable-plugin-0.2.28.tgz",
|
||||
"integrity": "sha512-lPxcLNClR865dQMGAGy+NSbC+80mWbVXRolbN1ScIj7on9ypTtDs86VTdFGlKMzP8BSWIXM1Rjb6oSEShCxuJA==",
|
||||
"version": "0.3.2",
|
||||
"resolved": "https://registry.npmmirror.com/@flowgram.ai/variable-plugin/-/variable-plugin-0.3.2.tgz",
|
||||
"integrity": "sha512-Fm4DS/DrfcyKtvwUyoB4y5dBr8D/JWaSZjhu6kWee3HIV46uJLruufynMBQprWEgSMj9RyYFoFVQWH4hYisNWg==",
|
||||
"dependencies": {
|
||||
"@flowgram.ai/core": "0.2.28",
|
||||
"@flowgram.ai/document": "0.2.28",
|
||||
"@flowgram.ai/free-layout-core": "0.2.28",
|
||||
"@flowgram.ai/utils": "0.2.28",
|
||||
"@flowgram.ai/variable-core": "0.2.28",
|
||||
"@flowgram.ai/variable-layout": "0.2.28",
|
||||
"@flowgram.ai/core": "0.3.2",
|
||||
"@flowgram.ai/document": "0.3.2",
|
||||
"@flowgram.ai/free-layout-core": "0.3.2",
|
||||
"@flowgram.ai/utils": "0.3.2",
|
||||
"@flowgram.ai/variable-core": "0.3.2",
|
||||
"@flowgram.ai/variable-layout": "0.3.2",
|
||||
"inversify": "^6.0.1",
|
||||
"reflect-metadata": "~0.2.2",
|
||||
"styled-components": "^5"
|
||||
@@ -4028,9 +4028,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@rc-component/trigger": {
|
||||
"version": "2.2.7",
|
||||
"resolved": "https://registry.npmmirror.com/@rc-component/trigger/-/trigger-2.2.7.tgz",
|
||||
"integrity": "sha512-Qggj4Z0AA2i5dJhzlfFSmg1Qrziu8dsdHOihROL5Kl18seO2Eh/ZaTYt2c8a/CyGaTChnFry7BEYew1+/fhSbA==",
|
||||
"version": "2.3.0",
|
||||
"resolved": "https://registry.npmmirror.com/@rc-component/trigger/-/trigger-2.3.0.tgz",
|
||||
"integrity": "sha512-iwaxZyzOuK0D7lS+0AQEtW52zUWxoGqTGkke3dRyb8pYiShmRpCjB/8TzPI4R6YySCH7Vm9BZj/31VPiiQTLBg==",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.23.2",
|
||||
"@rc-component/portal": "^1.1.0",
|
||||
@@ -5000,9 +5000,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@uiw/codemirror-extensions-basic-setup": {
|
||||
"version": "4.24.1",
|
||||
"resolved": "https://registry.npmmirror.com/@uiw/codemirror-extensions-basic-setup/-/codemirror-extensions-basic-setup-4.24.1.tgz",
|
||||
"integrity": "sha512-o1m1a8eUS3fWERMbDFvN8t8sZUFPgDKNemmlQ5Ot2vKm+Ax84lKP1dhEFgkiOaZ1bDHk4T5h6SjHuTghrJHKww==",
|
||||
"version": "4.24.2",
|
||||
"resolved": "https://registry.npmmirror.com/@uiw/codemirror-extensions-basic-setup/-/codemirror-extensions-basic-setup-4.24.2.tgz",
|
||||
"integrity": "sha512-wW/gjLRvVUeYyhdh2TApn25cvdcR+Rhg6R/j3eTOvXQzU1HNzNYCVH4YKVIfgtfdM/Xs+N8fkk+rbr1YvBppCg==",
|
||||
"dependencies": {
|
||||
"@codemirror/autocomplete": "^6.0.0",
|
||||
"@codemirror/commands": "^6.0.0",
|
||||
@@ -5026,20 +5026,20 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@uiw/codemirror-theme-vscode": {
|
||||
"version": "4.24.1",
|
||||
"resolved": "https://registry.npmmirror.com/@uiw/codemirror-theme-vscode/-/codemirror-theme-vscode-4.24.1.tgz",
|
||||
"integrity": "sha512-w2/eaPcYsHi03aWoKzVxIWv5dBDKW4YPnjwyYLgFY3uNdZmSfoNkFhqW7GJgNNF77ATixPq2wm9XqYeOTtvuww==",
|
||||
"version": "4.24.2",
|
||||
"resolved": "https://registry.npmmirror.com/@uiw/codemirror-theme-vscode/-/codemirror-theme-vscode-4.24.2.tgz",
|
||||
"integrity": "sha512-5rLvAMKNjqtHSy3BZnI1Q4ZLqUDDJC5fKOp8Mv5VjPqEmAxEAIGrIXBnhAFlRg5VjHbHEMtM1O5ugL3IeHoBdQ==",
|
||||
"dependencies": {
|
||||
"@uiw/codemirror-themes": "4.24.1"
|
||||
"@uiw/codemirror-themes": "4.24.2"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://jaywcjlove.github.io/#/sponsor"
|
||||
}
|
||||
},
|
||||
"node_modules/@uiw/codemirror-themes": {
|
||||
"version": "4.24.1",
|
||||
"resolved": "https://registry.npmmirror.com/@uiw/codemirror-themes/-/codemirror-themes-4.24.1.tgz",
|
||||
"integrity": "sha512-hduBbFNiWNW6nYa2/giKQ9YpzhWNw87BGpCjC+cXYMZ7bCD6q5DC6Hw+7z7ZwSzEaOQvV91lmirOjJ8hn9+pkg==",
|
||||
"version": "4.24.2",
|
||||
"resolved": "https://registry.npmmirror.com/@uiw/codemirror-themes/-/codemirror-themes-4.24.2.tgz",
|
||||
"integrity": "sha512-0fQusJE08APL+1WM0xYqvampVM2RpABHjvZbcJWHdRc+teZbrP8907VW1ZX1tO/7/xgRRt4Fc3RHPCcTs7b0NQ==",
|
||||
"dependencies": {
|
||||
"@codemirror/language": "^6.0.0",
|
||||
"@codemirror/state": "^6.0.0",
|
||||
@@ -5055,15 +5055,15 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@uiw/react-codemirror": {
|
||||
"version": "4.24.1",
|
||||
"resolved": "https://registry.npmmirror.com/@uiw/react-codemirror/-/react-codemirror-4.24.1.tgz",
|
||||
"integrity": "sha512-BivF4NLqbuBQK5gPVhSkOARi9nPXw8X5r25EnInPeY+I9l1dfEX8O9V6+0xHTlGHyUo0cNfGEF9t1KHEicUfJw==",
|
||||
"version": "4.24.2",
|
||||
"resolved": "https://registry.npmmirror.com/@uiw/react-codemirror/-/react-codemirror-4.24.2.tgz",
|
||||
"integrity": "sha512-kp7DhTq4RR+M2zJBQBrHn1dIkBrtOskcwJX4vVsKGByReOvfMrhqRkGTxYMRDTX6x75EG2mvBJPDKYcUQcHWBw==",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.18.6",
|
||||
"@codemirror/commands": "^6.1.0",
|
||||
"@codemirror/state": "^6.1.1",
|
||||
"@codemirror/theme-one-dark": "^6.0.0",
|
||||
"@uiw/codemirror-extensions-basic-setup": "4.24.1",
|
||||
"@uiw/codemirror-extensions-basic-setup": "4.24.2",
|
||||
"codemirror": "^6.0.0"
|
||||
},
|
||||
"funding": {
|
||||
@@ -5075,8 +5075,8 @@
|
||||
"@codemirror/theme-one-dark": ">=6.0.0",
|
||||
"@codemirror/view": ">=6.0.0",
|
||||
"codemirror": ">=6.0.0",
|
||||
"react": ">=16.8.0",
|
||||
"react-dom": ">=16.8.0"
|
||||
"react": ">=17.0.0",
|
||||
"react-dom": ">=17.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@umijs/route-utils": {
|
||||
@@ -5472,9 +5472,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/antd": {
|
||||
"version": "5.26.6",
|
||||
"resolved": "https://registry.npmmirror.com/antd/-/antd-5.26.6.tgz",
|
||||
"integrity": "sha512-k8ipeT+UL2tP/x4jHTXElScAxsD94JgrIEeGHj80nNO4dL9hqcmaOUBpHo3ieCf6MFjhS7gLUthysQeP6e7DUg==",
|
||||
"version": "5.26.7",
|
||||
"resolved": "https://registry.npmmirror.com/antd/-/antd-5.26.7.tgz",
|
||||
"integrity": "sha512-iCyXN6+i2CUVEOSzzJKfbKeg115qoJhGvSkCh5uzAf9hANwHUOJQhsMn+KtN+Lx/2NQ6wfM7nGZ+7NPNO5Pn1w==",
|
||||
"dependencies": {
|
||||
"@ant-design/colors": "^7.2.1",
|
||||
"@ant-design/cssinjs": "^1.23.0",
|
||||
@@ -5487,7 +5487,7 @@
|
||||
"@rc-component/mutate-observer": "^1.1.0",
|
||||
"@rc-component/qrcode": "~1.0.0",
|
||||
"@rc-component/tour": "~1.15.1",
|
||||
"@rc-component/trigger": "^2.2.7",
|
||||
"@rc-component/trigger": "^2.3.0",
|
||||
"classnames": "^2.5.1",
|
||||
"copy-to-clipboard": "^3.3.3",
|
||||
"dayjs": "^1.11.11",
|
||||
@@ -5517,7 +5517,7 @@
|
||||
"rc-switch": "~4.1.0",
|
||||
"rc-table": "~7.51.1",
|
||||
"rc-tabs": "~15.6.1",
|
||||
"rc-textarea": "~1.10.0",
|
||||
"rc-textarea": "~1.10.1",
|
||||
"rc-tooltip": "~6.4.0",
|
||||
"rc-tree": "~5.13.1",
|
||||
"rc-tree-select": "~5.27.0",
|
||||
@@ -6151,6 +6151,23 @@
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/date-fns": {
|
||||
"version": "2.30.0",
|
||||
"resolved": "https://registry.npmmirror.com/date-fns/-/date-fns-2.30.0.tgz",
|
||||
"integrity": "sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw==",
|
||||
"optional": true,
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.21.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.11"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/date-fns"
|
||||
}
|
||||
},
|
||||
"node_modules/dayjs": {
|
||||
"version": "1.11.13",
|
||||
"resolved": "https://registry.npmmirror.com/dayjs/-/dayjs-1.11.13.tgz",
|
||||
@@ -6631,9 +6648,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/eslint-plugin-better-tailwindcss": {
|
||||
"version": "3.7.2",
|
||||
"resolved": "https://registry.npmmirror.com/eslint-plugin-better-tailwindcss/-/eslint-plugin-better-tailwindcss-3.7.2.tgz",
|
||||
"integrity": "sha512-uJnoDGmUPtxHHcIW5njEoh1CYlq39E4J2pWq+D7REDGhW41arSCeeaiiwAT4Ok04kXNvmFBiYmC6Jdd6Bz7o2Q==",
|
||||
"version": "3.7.3",
|
||||
"resolved": "https://registry.npmmirror.com/eslint-plugin-better-tailwindcss/-/eslint-plugin-better-tailwindcss-3.7.3.tgz",
|
||||
"integrity": "sha512-7I2MM1/OetBamiNWGkAetzQtvph6N7B6Iy5nIU9srFkHXnqR5G6lFX7ZT+JZmLrirrYSn8E0THeJg9QTQaI/kQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@eslint/css-tree": "^3.6.2",
|
||||
@@ -9326,9 +9343,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/rc-textarea": {
|
||||
"version": "1.10.0",
|
||||
"resolved": "https://registry.npmmirror.com/rc-textarea/-/rc-textarea-1.10.0.tgz",
|
||||
"integrity": "sha512-ai9IkanNuyBS4x6sOL8qu/Ld40e6cEs6pgk93R+XLYg0mDSjNBGey6/ZpDs5+gNLD7urQ14po3V6Ck2dJLt9SA==",
|
||||
"version": "1.10.2",
|
||||
"resolved": "https://registry.npmmirror.com/rc-textarea/-/rc-textarea-1.10.2.tgz",
|
||||
"integrity": "sha512-HfaeXiaSlpiSp0I/pvWpecFEHpVysZ9tpDLNkxQbMvMz6gsr7aVZ7FpWP9kt4t7DB+jJXesYS0us1uPZnlRnwQ==",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.10.1",
|
||||
"classnames": "^2.2.1",
|
||||
@@ -11146,17 +11163,17 @@
|
||||
}
|
||||
},
|
||||
"node_modules/zod": {
|
||||
"version": "4.0.10",
|
||||
"resolved": "https://registry.npmmirror.com/zod/-/zod-4.0.10.tgz",
|
||||
"integrity": "sha512-3vB+UU3/VmLL2lvwcY/4RV2i9z/YU0DTV/tDuYjrwmx5WeJ7hwy+rGEEx8glHp6Yxw7ibRbKSaIFBgReRPe5KA==",
|
||||
"version": "4.0.14",
|
||||
"resolved": "https://registry.npmmirror.com/zod/-/zod-4.0.14.tgz",
|
||||
"integrity": "sha512-nGFJTnJN6cM2v9kXL+SOBq3AtjQby3Mv5ySGFof5UGRHrRioSJ5iG680cYNjE/yWk671nROcpPj4hAS8nyLhSw==",
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/colinhacks"
|
||||
}
|
||||
},
|
||||
"node_modules/zustand": {
|
||||
"version": "5.0.6",
|
||||
"resolved": "https://registry.npmmirror.com/zustand/-/zustand-5.0.6.tgz",
|
||||
"integrity": "sha512-ihAqNeUVhe0MAD+X8M5UzqyZ9k3FFZLBTtqo6JLPwV53cbRB/mJwBI0PxcIgqhBBHlEs8G45OTDTMq3gNcLq3A==",
|
||||
"version": "5.0.7",
|
||||
"resolved": "https://registry.npmmirror.com/zustand/-/zustand-5.0.7.tgz",
|
||||
"integrity": "sha512-Ot6uqHDW/O2VdYsKLLU8GQu8sCOM1LcoE8RwvLv9uuRT9s6SOHCKs0ZEOhxg+I1Ld+A1Q5lwx+UlKXXUoCZITg==",
|
||||
"engines": {
|
||||
"node": ">=12.20.0"
|
||||
},
|
||||
|
||||
+10
-10
@@ -1,7 +1,6 @@
|
||||
{
|
||||
"name": "ui",
|
||||
"name": "@certimate/webui",
|
||||
"private": true,
|
||||
"version": "0.0.0",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite --host",
|
||||
@@ -15,13 +14,14 @@
|
||||
"@codemirror/lang-yaml": "^6.1.2",
|
||||
"@codemirror/language": "^6.11.2",
|
||||
"@codemirror/legacy-modes": "^6.5.1",
|
||||
"@flowgram.ai/fixed-layout-editor": "^0.2.28",
|
||||
"@flowgram.ai/fixed-layout-editor": "^0.3.2",
|
||||
"@flowgram.ai/document": "^0.3.2",
|
||||
"@tabler/icons-react": "^3.34.1",
|
||||
"@uiw/codemirror-extensions-basic-setup": "^4.24.1",
|
||||
"@uiw/codemirror-theme-vscode": "^4.24.1",
|
||||
"@uiw/react-codemirror": "^4.24.1",
|
||||
"@uiw/codemirror-extensions-basic-setup": "^4.24.2",
|
||||
"@uiw/codemirror-theme-vscode": "^4.24.2",
|
||||
"@uiw/react-codemirror": "^4.24.2",
|
||||
"ahooks": "^3.9.0",
|
||||
"antd": "^5.26.6",
|
||||
"antd": "^5.26.7",
|
||||
"antd-zod": "^7.0.0",
|
||||
"clsx": "^2.1.1",
|
||||
"cron-parser": "^5.3.0",
|
||||
@@ -38,8 +38,8 @@
|
||||
"react-i18next": "^15.6.1",
|
||||
"react-router-dom": "^7.7.1",
|
||||
"tailwind-merge": "^3.3.1",
|
||||
"zod": "^4.0.10",
|
||||
"zustand": "^5.0.6"
|
||||
"zod": "^4.0.14",
|
||||
"zustand": "^5.0.7"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@eslint/js": "^9.32.0",
|
||||
@@ -56,7 +56,7 @@
|
||||
"eslint": "^9.32.0",
|
||||
"eslint-config-prettier": "^10.1.8",
|
||||
"eslint-import-resolver-typescript": "^4.4.4",
|
||||
"eslint-plugin-better-tailwindcss": "^3.7.2",
|
||||
"eslint-plugin-better-tailwindcss": "^3.7.3",
|
||||
"eslint-plugin-import": "^2.32.0",
|
||||
"eslint-plugin-prettier": "^5.5.3",
|
||||
"eslint-plugin-react-hooks": "^5.2.0",
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
import { IconList } from "@tabler/icons-react";
|
||||
import { useControllableValue } from "ahooks";
|
||||
import { Button, Form, Input, type InputProps, Space } from "antd";
|
||||
import { nanoid } from "nanoid";
|
||||
import { nanoid } from "nanoid/non-secure";
|
||||
|
||||
import { useAntdForm } from "@/hooks";
|
||||
import ModalForm from "./ModalForm";
|
||||
|
||||
@@ -30,15 +30,19 @@ const WorkflowElementsContainer = ({ className, style, disabled }: WorkflowEleme
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="absolute right-6 bottom-4 z-2">
|
||||
<Card className="rounded-lg p-2 shadow-lg" styles={{ body: { padding: 0 } }}>
|
||||
<div className="flex items-center gap-2">
|
||||
<Button icon={<IconMinus size="1.25em" />} disabled={scale <= MIN_SCALE} onClick={() => setScale((s) => Math.max(MIN_SCALE, s - STEP_SCALE))} />
|
||||
<Typography.Text className="min-w-12 text-center">{Math.round(scale * 100)}%</Typography.Text>
|
||||
<Button icon={<IconPlus size="1.25em" />} disabled={scale >= MAX_SCALE} onClick={() => setScale((s) => Math.min(MAX_SCALE, s + STEP_SCALE))} />
|
||||
<Button icon={<IconMaximize size="1.25em" />} onClick={() => setScale(1)} />
|
||||
<div className="absolute bottom-4 z-2 w-full px-4">
|
||||
<div className="container">
|
||||
<div className="flex justify-end">
|
||||
<Card className="rounded-lg p-2 shadow-lg" styles={{ body: { padding: 0 } }}>
|
||||
<div className="flex items-center gap-2">
|
||||
<Button icon={<IconMinus size="1.25em" />} disabled={scale <= MIN_SCALE} onClick={() => setScale((s) => Math.max(MIN_SCALE, s - STEP_SCALE))} />
|
||||
<Typography.Text className="min-w-12 text-center">{Math.round(scale * 100)}%</Typography.Text>
|
||||
<Button icon={<IconPlus size="1.25em" />} disabled={scale >= MAX_SCALE} onClick={() => setScale((s) => Math.min(MAX_SCALE, s + STEP_SCALE))} />
|
||||
<Button icon={<IconMaximize size="1.25em" />} onClick={() => setScale(1)} />
|
||||
</div>
|
||||
</Card>
|
||||
</div>
|
||||
</Card>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -0,0 +1,119 @@
|
||||
import { useMemo, useRef } from "react";
|
||||
import {
|
||||
ConstantKeys,
|
||||
EditorRenderer,
|
||||
FixedLayoutEditorProvider,
|
||||
type FixedLayoutPluginContext,
|
||||
type FixedLayoutProps,
|
||||
type FlowDocumentJSON,
|
||||
FlowTextKey,
|
||||
} from "@flowgram.ai/fixed-layout-editor";
|
||||
import "@flowgram.ai/fixed-layout-editor/index.css";
|
||||
import { useDeepCompareEffect } from "ahooks";
|
||||
import { theme } from "antd";
|
||||
|
||||
import { getFlowComponents } from "./components";
|
||||
import NodeRender from "./NodeRender";
|
||||
import { getFlowNodeRegistries } from "./nodes";
|
||||
import { BlockNode } from "./nodes/_shared";
|
||||
import "./flowgram.css";
|
||||
|
||||
export interface EditorProps {
|
||||
className?: string;
|
||||
style?: React.CSSProperties;
|
||||
children?: React.ReactNode;
|
||||
initialData?: FlowDocumentJSON;
|
||||
readonly?: boolean;
|
||||
}
|
||||
|
||||
const Editor = ({ className, style, children, initialData, readonly }: EditorProps) => {
|
||||
const { token: themeToken } = theme.useToken();
|
||||
|
||||
const flowgramEditorRef = useRef<FixedLayoutPluginContext>(null);
|
||||
const flowgramEditorProps = useMemo<FixedLayoutProps>(
|
||||
() => ({
|
||||
initialData: initialData,
|
||||
|
||||
constants: {
|
||||
[ConstantKeys.BASE_COLOR]: themeToken.colorBorder,
|
||||
[ConstantKeys.BASE_ACTIVATED_COLOR]: themeToken.colorPrimary,
|
||||
[ConstantKeys.NODE_SPACING]: 64,
|
||||
[ConstantKeys.BRANCH_SPACING]: 64,
|
||||
// [ConstantKeys.INLINE_BLOCKS_PADDING_TOP]: 48,
|
||||
// [ConstantKeys.INLINE_BLOCKS_PADDING_BOTTOM]: 48,
|
||||
},
|
||||
|
||||
background: {
|
||||
backgroundColor: themeToken.colorBgContainer,
|
||||
dotSize: 0,
|
||||
},
|
||||
|
||||
playground: {
|
||||
autoFocus: true,
|
||||
autoResize: true,
|
||||
preventGlobalGesture: true,
|
||||
},
|
||||
|
||||
scroll: {
|
||||
enableScrollLimit: true,
|
||||
},
|
||||
|
||||
readonly: readonly,
|
||||
|
||||
nodeEngine: {
|
||||
enable: true,
|
||||
},
|
||||
|
||||
variableEngine: {
|
||||
enable: true,
|
||||
},
|
||||
|
||||
materials: {
|
||||
components: getFlowComponents(),
|
||||
renderTexts: {
|
||||
[FlowTextKey.TRY_START_TEXT]: "Try",
|
||||
[FlowTextKey.TRY_END_TEXT]: "Finally",
|
||||
[FlowTextKey.CATCH_TEXT]: "Catch",
|
||||
},
|
||||
renderDefaultNode: NodeRender,
|
||||
},
|
||||
|
||||
nodeRegistries: getFlowNodeRegistries(),
|
||||
|
||||
getNodeDefaultRegistry(type) {
|
||||
return {
|
||||
type,
|
||||
meta: {
|
||||
defaultExpanded: true,
|
||||
},
|
||||
formMeta: {
|
||||
render: () => <BlockNode>{type}</BlockNode>,
|
||||
},
|
||||
};
|
||||
},
|
||||
|
||||
onAllLayersRendered: (ctx) => {
|
||||
// 画布初始化后向下滚动一点,露出可能被 Alert 遮挡的部分
|
||||
setTimeout(() => {
|
||||
ctx.playground.config.scroll({ scrollY: -80 });
|
||||
}, 1);
|
||||
},
|
||||
}),
|
||||
[themeToken, initialData, readonly]
|
||||
);
|
||||
|
||||
useDeepCompareEffect(() => {
|
||||
flowgramEditorRef.current?.document?.fromJSON?.(initialData ?? {});
|
||||
}, [initialData]);
|
||||
|
||||
return (
|
||||
<div className={className} style={style}>
|
||||
<FixedLayoutEditorProvider ref={flowgramEditorRef} {...flowgramEditorProps}>
|
||||
<EditorRenderer />
|
||||
{children}
|
||||
</FixedLayoutEditorProvider>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Editor;
|
||||
@@ -0,0 +1,27 @@
|
||||
import { type NodeRenderProps, useNodeRender } from "@flowgram.ai/fixed-layout-editor";
|
||||
|
||||
export interface NodeProps extends NodeRenderProps {}
|
||||
|
||||
const Node = (_: NodeProps) => {
|
||||
const nodeRender = useNodeRender();
|
||||
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
opacity: nodeRender.dragging ? 0.3 : 1,
|
||||
outline: nodeRender.form?.state?.invalid ? "1px solid var(--color-error)" : "none",
|
||||
...nodeRender.node.getNodeRegistry().meta.style,
|
||||
}}
|
||||
onMouseEnter={nodeRender.onMouseEnter}
|
||||
onMouseLeave={nodeRender.onMouseLeave}
|
||||
onMouseDown={(e) => {
|
||||
nodeRender.startDrag(e);
|
||||
e.stopPropagation();
|
||||
}}
|
||||
>
|
||||
{nodeRender.form?.render()}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Node;
|
||||
@@ -0,0 +1,78 @@
|
||||
import { useCallback, useEffect, useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { EditorState, FlowLayoutDefault, useClientContext, usePlaygroundTools, useRefresh } from "@flowgram.ai/fixed-layout-editor";
|
||||
import { IconHandStop, IconLayoutCards, IconMaximize, IconMinus, IconPlus, IconPointer } from "@tabler/icons-react";
|
||||
import { Button, Tooltip } from "antd";
|
||||
|
||||
import { mergeCls } from "@/utils/css";
|
||||
|
||||
export interface ToolbarProps {
|
||||
className?: string;
|
||||
style?: React.CSSProperties;
|
||||
}
|
||||
|
||||
const Toolbar = ({ className, style }: ToolbarProps) => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const ctx = useClientContext();
|
||||
const { playground } = ctx;
|
||||
|
||||
const tools = usePlaygroundTools({ minZoom: 0.1, maxZoom: 3, padding: 48 });
|
||||
|
||||
const refresh = useRefresh();
|
||||
useEffect(() => {
|
||||
const disposable = playground.config.onReadonlyOrDisabledChange(() => refresh());
|
||||
return () => disposable.dispose();
|
||||
}, [playground]);
|
||||
|
||||
const [isMouseFriendly, setIsMouseFriendly] = useState(playground.editorState.is(EditorState.STATE_MOUSE_FRIENDLY_SELECT.id));
|
||||
|
||||
const handleToggleLayout = useCallback(() => {
|
||||
if (tools.isVertical) {
|
||||
tools.changeLayout(FlowLayoutDefault.HORIZONTAL_FIXED_LAYOUT);
|
||||
} else {
|
||||
tools.changeLayout(FlowLayoutDefault.VERTICAL_FIXED_LAYOUT);
|
||||
}
|
||||
}, [tools.isVertical]);
|
||||
|
||||
const handleToggleMouseFriendly = useCallback(() => {
|
||||
if (isMouseFriendly) {
|
||||
playground.editorState.changeState(EditorState.STATE_SELECT.id);
|
||||
setIsMouseFriendly(false);
|
||||
} else {
|
||||
playground.editorState.changeState(EditorState.STATE_MOUSE_FRIENDLY_SELECT.id);
|
||||
setIsMouseFriendly(true);
|
||||
}
|
||||
}, [isMouseFriendly]);
|
||||
|
||||
return (
|
||||
<div className={className} style={style}>
|
||||
<div className="flex items-center gap-2">
|
||||
<Tooltip title={t("workflow.detail.design.toolbar.zoomout")}>
|
||||
<Button icon={<IconMinus size="1.25em" />} onClick={() => tools.zoomout()} />
|
||||
</Tooltip>
|
||||
<Tooltip title={t("workflow.detail.design.toolbar.zoom")}>
|
||||
<Button className="w-16 text-center" onClick={() => tools.updateZoom(1)}>
|
||||
{Math.round(tools.zoom * 100)}%
|
||||
</Button>
|
||||
</Tooltip>
|
||||
<Tooltip title={t("workflow.detail.design.toolbar.zoomin")}>
|
||||
<Button icon={<IconPlus size="1.25em" />} onClick={() => tools.zoomin()} />
|
||||
</Tooltip>
|
||||
<Tooltip title={t("workflow.detail.design.toolbar.auto_fit")}>
|
||||
<Button icon={<IconMaximize size="1.25em" />} onClick={() => tools.fitView()} />
|
||||
</Tooltip>
|
||||
|
||||
<Tooltip title={tools.isVertical ? t("workflow.detail.design.toolbar.vertical_layout") : t("workflow.detail.design.toolbar.horizontal_layout")}>
|
||||
<Button icon={<IconLayoutCards className={mergeCls({ ["rotate-90"]: tools.isVertical })} size="1.25em" />} onClick={handleToggleLayout} />
|
||||
</Tooltip>
|
||||
|
||||
<Tooltip title={isMouseFriendly ? t("workflow.detail.design.toolbar.hand_mode") : t("workflow.detail.design.toolbar.pointer_mode")}>
|
||||
<Button icon={isMouseFriendly ? <IconHandStop size="1.25em" /> : <IconPointer size="1.25em" />} onClick={handleToggleMouseFriendly} />
|
||||
</Tooltip>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Toolbar;
|
||||
@@ -0,0 +1,65 @@
|
||||
import { type AdderProps as FlowgramAdderProps, useClientContext } from "@flowgram.ai/fixed-layout-editor";
|
||||
|
||||
import { IconPlus } from "@tabler/icons-react";
|
||||
import { Button, Dropdown } from "antd";
|
||||
|
||||
import { getFlowNodeRegistries } from "../nodes";
|
||||
|
||||
export interface AdderProps extends FlowgramAdderProps {}
|
||||
|
||||
const Adder = ({ from, hoverActivated }: AdderProps) => {
|
||||
const ctx = useClientContext();
|
||||
const { operation, playground } = ctx;
|
||||
|
||||
const menuItems = getFlowNodeRegistries()
|
||||
.filter((registry) => {
|
||||
if (registry.canAdd != null) {
|
||||
return registry.canAdd(ctx, from);
|
||||
}
|
||||
if (registry.meta?.addDisable != null) {
|
||||
return registry.meta.addDisable;
|
||||
}
|
||||
return true;
|
||||
})
|
||||
.map((registry) => {
|
||||
const Icon = registry.meta?.icon;
|
||||
|
||||
return {
|
||||
key: registry.type,
|
||||
label: registry.type,
|
||||
icon: (
|
||||
<span className="anticon scale-125">
|
||||
<Icon size="1em" />
|
||||
</span>
|
||||
),
|
||||
onClick: () => {
|
||||
const props = registry.onAdd!(ctx, from);
|
||||
const block = operation.addFromNode(from, {
|
||||
...props,
|
||||
blocks: props?.blocks ?? [],
|
||||
});
|
||||
|
||||
setTimeout(() => {
|
||||
playground.scrollToView({
|
||||
bounds: block.bounds,
|
||||
scrollToCenter: true,
|
||||
});
|
||||
}, 1);
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
return playground.config.readonlyOrDisabled ? null : (
|
||||
<div className="relative">
|
||||
<Dropdown menu={{ items: menuItems }} placement="bottomRight" trigger={["click"]}>
|
||||
{hoverActivated ? (
|
||||
<Button icon={<IconPlus size="1em" stroke={3} />} shape="circle" size="small" type="primary" />
|
||||
) : (
|
||||
<div className="size-2 rounded-full bg-primary opacity-75"></div>
|
||||
)}
|
||||
</Dropdown>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Adder;
|
||||
@@ -0,0 +1,62 @@
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { FlowNodeBaseType, type FlowNodeEntity, type AdderProps as FlowgramAdderProps, useClientContext } from "@flowgram.ai/fixed-layout-editor";
|
||||
import { Button } from "antd";
|
||||
import { nanoid } from "nanoid";
|
||||
|
||||
import { BranchBlockNodeRegistry } from "../nodes/ConditionNode";
|
||||
import { CatchBlockNodeRegistry } from "../nodes/TryCatchNode";
|
||||
import { NodeType } from "../nodes/typings";
|
||||
|
||||
export interface BranchAdderProps extends FlowgramAdderProps {}
|
||||
|
||||
const BranchAdder = ({ node }: BranchAdderProps) => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const ctx = useClientContext();
|
||||
const { operation, playground } = ctx;
|
||||
|
||||
const handleAddBranch = () => {
|
||||
let block: FlowNodeEntity;
|
||||
switch (node.flowNodeType) {
|
||||
case NodeType.Condition:
|
||||
block = operation.addBlock(node, BranchBlockNodeRegistry.onAdd!(ctx, node));
|
||||
break;
|
||||
|
||||
case NodeType.TryCatch:
|
||||
block = operation.addBlock(node, CatchBlockNodeRegistry.onAdd!(ctx, node));
|
||||
break;
|
||||
|
||||
default:
|
||||
console.warn(`[certimate] unsupported node type for adding branch: '${node.flowNodeType}'`);
|
||||
block = operation.addBlock(node, {
|
||||
id: nanoid(),
|
||||
type: FlowNodeBaseType.BLOCK,
|
||||
data: {
|
||||
name: "Branch",
|
||||
},
|
||||
});
|
||||
break;
|
||||
}
|
||||
|
||||
setTimeout(() => {
|
||||
playground.scrollToView({
|
||||
bounds: block.bounds,
|
||||
scrollToCenter: true,
|
||||
});
|
||||
}, 1);
|
||||
};
|
||||
|
||||
return playground.config.readonlyOrDisabled ? null : (
|
||||
<div
|
||||
className="relative"
|
||||
onMouseEnter={() => node.firstChild?.renderData?.toggleMouseEnter()}
|
||||
onMouseLeave={() => node.firstChild?.renderData?.toggleMouseLeave()}
|
||||
>
|
||||
<Button shape="round" size="small" onClick={handleAddBranch}>
|
||||
<span className="text-xs">{t("workflow_node.action.add_branch")}</span>
|
||||
</Button>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default BranchAdder;
|
||||
@@ -0,0 +1,9 @@
|
||||
import { type CollapseProps as FlowgramCollapseProps } from "@flowgram.ai/fixed-layout-editor";
|
||||
|
||||
export interface CollapseProps extends FlowgramCollapseProps {}
|
||||
|
||||
const Collapse = (_: CollapseProps) => {
|
||||
return null;
|
||||
};
|
||||
|
||||
export default Collapse;
|
||||
@@ -0,0 +1,14 @@
|
||||
import { type DragNodeProps as FlowgramDragNodeProps } from "@flowgram.ai/fixed-layout-editor";
|
||||
import { IconGradienter } from "@tabler/icons-react";
|
||||
|
||||
const DragHighlightAdder = (_: FlowgramDragNodeProps) => {
|
||||
return (
|
||||
<div className="size-4 animate-ping rounded-full bg-primary text-white shadow">
|
||||
<div className="flex size-full items-center justify-center">
|
||||
<IconGradienter size="1em" />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default DragHighlightAdder;
|
||||
@@ -0,0 +1,26 @@
|
||||
import { type FlowNodeEntity, type DragNodeProps as FlowgramDragNodeProps, getNodeForm } from "@flowgram.ai/fixed-layout-editor";
|
||||
import { Badge, Card } from "antd";
|
||||
|
||||
export interface DragNodeProps extends FlowgramDragNodeProps {
|
||||
dragStart: FlowNodeEntity;
|
||||
dragNodes: FlowNodeEntity[];
|
||||
}
|
||||
|
||||
const DragNode = ({ dragStart, dragNodes }: DragNodeProps) => {
|
||||
const count = (dragNodes || [])
|
||||
.map((n) => (n.allCollapsedChildren.length ? n.allCollapsedChildren.filter((_n) => !_n.hidden).length : 1))
|
||||
.reduce((acc, cur) => acc + cur, 0);
|
||||
return (
|
||||
<Badge count={count > 1 ? count : 0} size="small">
|
||||
<div className="relative w-[160px]">
|
||||
<Card className="bg-transparent shadow" styles={{ body: { padding: 0 } }}>
|
||||
<div className="overflow-hidden px-4 py-2 text-primary">
|
||||
<div className="truncate">{dragStart ? getNodeForm(dragStart)?.getValueIn("name") || `#${dragStart?.id}` : "\u00A0"}</div>
|
||||
</div>
|
||||
</Card>
|
||||
</div>
|
||||
</Badge>
|
||||
);
|
||||
};
|
||||
|
||||
export default DragNode;
|
||||
@@ -0,0 +1,31 @@
|
||||
import { FlowDragLayer, type AdderProps as FlowgramAdderProps, usePlayground } from "@flowgram.ai/fixed-layout-editor";
|
||||
import { IconChevronsDown } from "@tabler/icons-react";
|
||||
|
||||
export interface DraggingAdderProps extends FlowgramAdderProps {}
|
||||
|
||||
const DraggingAdder = ({ from }: DraggingAdderProps) => {
|
||||
const playground = usePlayground();
|
||||
|
||||
const layer = playground.getLayer(FlowDragLayer);
|
||||
if (!layer) return <></>;
|
||||
if (
|
||||
layer.options.canDrop &&
|
||||
!layer.options.canDrop({
|
||||
dragNodes: layer.dragEntities ?? [],
|
||||
dropNode: from,
|
||||
isBranch: false,
|
||||
})
|
||||
) {
|
||||
return <></>;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="size-4 animate-bounce rounded-full bg-primary text-white shadow">
|
||||
<div className="flex size-full items-center justify-center">
|
||||
<IconChevronsDown size="1em" />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default DraggingAdder;
|
||||
@@ -0,0 +1,11 @@
|
||||
import { type FlowNodeEntity } from "@flowgram.ai/fixed-layout-editor";
|
||||
|
||||
export interface NullProps {
|
||||
node: FlowNodeEntity;
|
||||
}
|
||||
|
||||
const Null = (_: NullProps) => {
|
||||
return null;
|
||||
};
|
||||
|
||||
export default Null;
|
||||
@@ -0,0 +1,69 @@
|
||||
import { useState } from "react";
|
||||
import {
|
||||
type CustomLabelProps,
|
||||
FlowNodeRenderData,
|
||||
FlowNodeTransformData,
|
||||
FlowRendererRegistry,
|
||||
FlowTextKey,
|
||||
useBaseColor,
|
||||
} from "@flowgram.ai/fixed-layout-editor";
|
||||
|
||||
export interface TryCatchCollapseProps extends CustomLabelProps {}
|
||||
|
||||
const TryCatchCollapse = ({ node, ...props }: TryCatchCollapseProps) => {
|
||||
const { baseColor, baseActivatedColor } = useBaseColor();
|
||||
|
||||
const nodeActivateData = node.getData(FlowNodeRenderData)!;
|
||||
const nodeTransformData = node.getData(FlowNodeTransformData)!;
|
||||
|
||||
const [hoverActivated, setHoverActivated] = useState(false);
|
||||
|
||||
const handleMouseEnter = () => {
|
||||
setHoverActivated(true);
|
||||
nodeActivateData.activated = true;
|
||||
};
|
||||
|
||||
const handleMouseLeave = () => {
|
||||
setHoverActivated(false);
|
||||
nodeActivateData.activated = false;
|
||||
};
|
||||
|
||||
if (!nodeTransformData || !nodeTransformData.parent) {
|
||||
return <></>;
|
||||
}
|
||||
|
||||
const width = nodeTransformData.inputPoint.x - nodeTransformData.parent.inputPoint.x;
|
||||
const height = 40;
|
||||
return (
|
||||
<div
|
||||
onMouseEnter={handleMouseEnter}
|
||||
onMouseLeave={handleMouseLeave}
|
||||
style={{
|
||||
width,
|
||||
height,
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
gap: 6,
|
||||
}}
|
||||
>
|
||||
<div
|
||||
data-label-id={props.labelId}
|
||||
style={{
|
||||
fontSize: 12,
|
||||
color: hoverActivated ? baseActivatedColor : baseColor,
|
||||
textAlign: "center",
|
||||
lineHeight: "20px",
|
||||
whiteSpace: "nowrap",
|
||||
backgroundColor: "var(--g-editor-background)",
|
||||
}}
|
||||
>
|
||||
{node.getService(FlowRendererRegistry).getText(FlowTextKey.CATCH_TEXT)}
|
||||
</div>
|
||||
|
||||
{/* {renderCollapse()} */}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default TryCatchCollapse;
|
||||
@@ -0,0 +1,30 @@
|
||||
import { FlowRendererKey } from "@flowgram.ai/fixed-layout-editor";
|
||||
|
||||
import Adder from "./Adder";
|
||||
import BranchAdder from "./BranchAdder";
|
||||
import Collapse from "./Collapse";
|
||||
import DraggingAdder from "./DraggingAdder";
|
||||
import DragHighlightAdder from "./DragHighlightAdder";
|
||||
import DragNode from "./DragNode";
|
||||
import Null from "./Null";
|
||||
import TryCatchCollapse from "./TryCatchCollapse";
|
||||
|
||||
export const getFlowComponents = () => {
|
||||
return {
|
||||
[FlowRendererKey.ADDER]: Adder,
|
||||
[FlowRendererKey.BRANCH_ADDER]: BranchAdder,
|
||||
[FlowRendererKey.SLOT_ADDER]: Null,
|
||||
|
||||
[FlowRendererKey.COLLAPSE]: Collapse,
|
||||
[FlowRendererKey.TRY_CATCH_COLLAPSE]: TryCatchCollapse,
|
||||
[FlowRendererKey.SLOT_COLLPASE]: Null,
|
||||
|
||||
[FlowRendererKey.DRAG_NODE]: DragNode,
|
||||
[FlowRendererKey.DRAG_HIGHLIGHT_ADDER]: DragHighlightAdder,
|
||||
[FlowRendererKey.DRAG_BRANCH_HIGHLIGHT_ADDER]: DragHighlightAdder,
|
||||
[FlowRendererKey.DRAGGABLE_ADDER]: DraggingAdder,
|
||||
|
||||
[FlowRendererKey.CONTEXT_MENU_POPOVER]: Null,
|
||||
[FlowRendererKey.SELECTOR_BOX_POPOVER]: Null,
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,22 @@
|
||||
/* 隐藏 flowgram 的标签背景 */
|
||||
/* flowgram 暂未提供相关配置项,这里需与 antd.colorBgContainer 保持一致 */
|
||||
:root {
|
||||
--g-editor-background: #fff;
|
||||
}
|
||||
.dark {
|
||||
--g-editor-background: #262c2d;
|
||||
}
|
||||
|
||||
/* 隐藏 flowgram 的折叠按钮 */
|
||||
.flow-canvas-collapse-adder > .flow-canvas-collapse {
|
||||
display: hidden !important;
|
||||
position: absolute !important;
|
||||
top: -99999px !important;
|
||||
left: -99999px !important;
|
||||
}
|
||||
|
||||
/* 控制 flowgram 线条样式 */
|
||||
/* flowgram 暂未提供相关配置项 */
|
||||
.flow-lines-container {
|
||||
stroke-width: 1.5px;
|
||||
}
|
||||
@@ -0,0 +1,206 @@
|
||||
import { getI18n } from "react-i18next";
|
||||
import { IconCloudUpload, IconContract, IconDeviceDesktopSearch, IconPackage, IconSend } from "@tabler/icons-react";
|
||||
import { Avatar, Typography } from "antd";
|
||||
import { nanoid } from "nanoid";
|
||||
|
||||
import { deploymentProvidersMap, notificationProvidersMap } from "@/domain/provider";
|
||||
|
||||
import { BaseNode } from "./_shared";
|
||||
import { type NodeRegistry, NodeType } from "./typings";
|
||||
|
||||
export const BizApplyNodeRegistry: NodeRegistry = {
|
||||
type: NodeType.BizApply,
|
||||
|
||||
meta: {
|
||||
helpText: getI18n().t("workflow_node.apply.help"),
|
||||
|
||||
icon: IconContract,
|
||||
iconColor: "#fff",
|
||||
iconBgColor: "#5b65f5",
|
||||
},
|
||||
|
||||
formMeta: {
|
||||
render: ({ form }) => {
|
||||
const { t } = getI18n();
|
||||
|
||||
const fieldDomains = form.getValueIn<string>("config.domains");
|
||||
|
||||
return <BaseNode>{fieldDomains ? fieldDomains.split(";").join("; ") : t("workflow.detail.design.nodes.placeholder")}</BaseNode>;
|
||||
},
|
||||
},
|
||||
|
||||
onAdd: () => {
|
||||
const { t } = getI18n();
|
||||
|
||||
return {
|
||||
id: nanoid(),
|
||||
type: NodeType.BizApply,
|
||||
data: {
|
||||
name: t("workflow_node.apply.default_name"),
|
||||
},
|
||||
};
|
||||
},
|
||||
};
|
||||
|
||||
export const BizUploadNodeRegistry: NodeRegistry = {
|
||||
type: NodeType.BizUpload,
|
||||
|
||||
meta: {
|
||||
helpText: getI18n().t("workflow_node.upload.help"),
|
||||
|
||||
icon: IconCloudUpload,
|
||||
iconColor: "#fff",
|
||||
iconBgColor: "#5b65f5",
|
||||
},
|
||||
|
||||
formMeta: {
|
||||
render: ({ form }) => {
|
||||
const { t } = getI18n();
|
||||
|
||||
const fieldDomains = form.getValueIn<string>("config.domains");
|
||||
|
||||
return <BaseNode>{fieldDomains ? fieldDomains.split(";").join("; ") : t("workflow.detail.design.nodes.placeholder")}</BaseNode>;
|
||||
},
|
||||
},
|
||||
|
||||
onAdd: () => {
|
||||
const { t } = getI18n();
|
||||
|
||||
return {
|
||||
id: nanoid(),
|
||||
type: NodeType.BizUpload,
|
||||
data: {
|
||||
name: t("workflow_node.upload.default_name"),
|
||||
},
|
||||
};
|
||||
},
|
||||
};
|
||||
|
||||
export const BizMonitorNodeRegistry: NodeRegistry = {
|
||||
type: NodeType.BizMonitor,
|
||||
|
||||
meta: {
|
||||
helpText: getI18n().t("workflow_node.monitor.help"),
|
||||
|
||||
icon: IconDeviceDesktopSearch,
|
||||
iconColor: "#fff",
|
||||
iconBgColor: "#5b65f5",
|
||||
},
|
||||
|
||||
formMeta: {
|
||||
render: ({ form }) => {
|
||||
const { t } = getI18n();
|
||||
|
||||
const fieldDomain = form.getValueIn<string>("config.domain");
|
||||
const fieldHost = form.getValueIn<string>("config.host");
|
||||
|
||||
return <BaseNode>{fieldDomain || fieldHost ? fieldDomain || fieldHost : t("workflow.detail.design.nodes.placeholder")}</BaseNode>;
|
||||
},
|
||||
},
|
||||
|
||||
onAdd: () => {
|
||||
const { t } = getI18n();
|
||||
|
||||
return {
|
||||
id: nanoid(),
|
||||
type: NodeType.BizMonitor,
|
||||
data: {
|
||||
name: t("workflow_node.monitor.default_name"),
|
||||
},
|
||||
};
|
||||
},
|
||||
};
|
||||
|
||||
export const BizDeployNodeRegistry: NodeRegistry = {
|
||||
type: NodeType.BizDeploy,
|
||||
|
||||
meta: {
|
||||
helpText: getI18n().t("workflow_node.deploy.help"),
|
||||
|
||||
icon: IconPackage,
|
||||
iconColor: "#fff",
|
||||
iconBgColor: "#5b65f5",
|
||||
},
|
||||
|
||||
formMeta: {
|
||||
render: ({ form }) => {
|
||||
const { t } = getI18n();
|
||||
|
||||
const fieldProvider = form.getValueIn<string>("config.provider");
|
||||
|
||||
return (
|
||||
<BaseNode>
|
||||
<div className="flex items-center justify-between gap-1">
|
||||
{fieldProvider ? (
|
||||
<>
|
||||
<div className="flex-1 truncate">{t(deploymentProvidersMap.get(fieldProvider)?.name ?? "")}</div>
|
||||
<Avatar shape="square" src={deploymentProvidersMap.get(fieldProvider)?.icon} size={20} />
|
||||
</>
|
||||
) : (
|
||||
t("workflow.detail.design.nodes.placeholder")
|
||||
)}
|
||||
</div>
|
||||
</BaseNode>
|
||||
);
|
||||
},
|
||||
},
|
||||
|
||||
onAdd: () => {
|
||||
const { t } = getI18n();
|
||||
|
||||
return {
|
||||
id: nanoid(),
|
||||
type: NodeType.BizMonitor,
|
||||
data: {
|
||||
name: t("workflow_node.deploy.default_name"),
|
||||
},
|
||||
};
|
||||
},
|
||||
};
|
||||
|
||||
export const BizNotifyNodeRegistry: NodeRegistry = {
|
||||
type: NodeType.BizNotify,
|
||||
|
||||
meta: {
|
||||
helpText: getI18n().t("workflow_node.notify.help"),
|
||||
|
||||
icon: IconSend,
|
||||
iconColor: "#fff",
|
||||
iconBgColor: "#0693d4",
|
||||
},
|
||||
|
||||
formMeta: {
|
||||
render: ({ form }) => {
|
||||
const { t } = getI18n();
|
||||
|
||||
const fieldProvider = form.getValueIn<string>("config.provider");
|
||||
|
||||
return (
|
||||
<BaseNode>
|
||||
<div className="flex items-center justify-between gap-1">
|
||||
{fieldProvider ? (
|
||||
<>
|
||||
<div className="flex-1 truncate">{t(notificationProvidersMap.get(fieldProvider)?.name ?? "")}</div>
|
||||
<Avatar shape="square" src={notificationProvidersMap.get(fieldProvider)?.icon} size="small" />
|
||||
</>
|
||||
) : (
|
||||
t("workflow.detail.design.nodes.placeholder")
|
||||
)}
|
||||
</div>
|
||||
</BaseNode>
|
||||
);
|
||||
},
|
||||
},
|
||||
|
||||
onAdd: () => {
|
||||
const { t } = getI18n();
|
||||
|
||||
return {
|
||||
id: nanoid(),
|
||||
type: NodeType.BizNotify,
|
||||
data: {
|
||||
name: t("workflow_node.notify.default_name"),
|
||||
},
|
||||
};
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,108 @@
|
||||
import { getI18n } from "react-i18next";
|
||||
import { FlowNodeBaseType, FlowNodeSplitType, ValidateTrigger } from "@flowgram.ai/fixed-layout-editor";
|
||||
import { IconFilter, IconFilterFilled, IconSitemap } from "@tabler/icons-react";
|
||||
import { nanoid } from "nanoid";
|
||||
|
||||
import { BaseNode, BlockNode } from "./_shared";
|
||||
import { type NodeRegistry, NodeType } from "./typings";
|
||||
|
||||
export const ConditionNodeRegistry: NodeRegistry = {
|
||||
type: NodeType.Condition,
|
||||
|
||||
extend: FlowNodeSplitType.DYNAMIC_SPLIT,
|
||||
|
||||
meta: {
|
||||
helpText: getI18n().t("workflow_node.condition.help"),
|
||||
|
||||
icon: IconSitemap,
|
||||
iconColor: "#fff",
|
||||
iconBgColor: "#373c43",
|
||||
|
||||
expandable: false,
|
||||
},
|
||||
|
||||
formMeta: {
|
||||
validateTrigger: ValidateTrigger.onChange,
|
||||
render: () => {
|
||||
return <BaseNode></BaseNode>;
|
||||
},
|
||||
},
|
||||
|
||||
onAdd() {
|
||||
const { t } = getI18n();
|
||||
|
||||
return {
|
||||
id: nanoid(),
|
||||
type: NodeType.Condition,
|
||||
data: {
|
||||
name: t("workflow_node.condition.default_name"),
|
||||
},
|
||||
blocks: [
|
||||
{
|
||||
id: nanoid(),
|
||||
type: NodeType.BranchBlock,
|
||||
blocks: [],
|
||||
data: {
|
||||
name: t("workflow_node.branch_block.default_name") + " 1",
|
||||
},
|
||||
},
|
||||
{
|
||||
id: nanoid(),
|
||||
type: NodeType.BranchBlock,
|
||||
data: {
|
||||
name: t("workflow_node.branch_block.default_name") + " 2",
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
},
|
||||
};
|
||||
|
||||
export const BranchBlockNodeRegistry: NodeRegistry = {
|
||||
type: NodeType.BranchBlock,
|
||||
|
||||
extend: FlowNodeBaseType.BLOCK,
|
||||
|
||||
meta: {
|
||||
addDisable: true,
|
||||
copyDisable: true,
|
||||
},
|
||||
|
||||
formMeta: {
|
||||
validateTrigger: ValidateTrigger.onChange,
|
||||
render: ({ form }) => {
|
||||
const fieldExpr = form.getValueIn("config.expression");
|
||||
|
||||
return (
|
||||
<BlockNode>
|
||||
<div className="flex items-center justify-center gap-2">
|
||||
<div className="flex items-center justify-center">
|
||||
{fieldExpr ? <IconFilterFilled color="var(--color-primary)" size="1.25em" stroke="1.25" /> : <IconFilter size="1.25em" stroke="1.25" />}
|
||||
</div>
|
||||
<div className="truncate">{form.getValueIn<string>("name") || "\u00A0"}</div>
|
||||
</div>
|
||||
</BlockNode>
|
||||
);
|
||||
},
|
||||
},
|
||||
|
||||
canAdd: () => {
|
||||
return false;
|
||||
},
|
||||
|
||||
canDelete: (_, node) => {
|
||||
return node.parent!.blocks.length >= 2;
|
||||
},
|
||||
|
||||
onAdd() {
|
||||
const { t } = getI18n();
|
||||
|
||||
return {
|
||||
id: nanoid(),
|
||||
type: NodeType.BranchBlock,
|
||||
data: {
|
||||
name: t("workflow_node.branch_block.default_name"),
|
||||
},
|
||||
};
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,75 @@
|
||||
import { getI18n } from "react-i18next";
|
||||
import { FlowNodeBaseType } from "@flowgram.ai/fixed-layout-editor";
|
||||
import { IconLogout } from "@tabler/icons-react";
|
||||
import { nanoid } from "nanoid";
|
||||
|
||||
import { BaseNode } from "./_shared";
|
||||
import { type NodeRegistry, NodeType } from "./typings";
|
||||
|
||||
export const EndNodeRegistry: NodeRegistry = {
|
||||
type: NodeType.End,
|
||||
|
||||
meta: {
|
||||
helpText: getI18n().t("workflow_node.end.help"),
|
||||
|
||||
icon: IconLogout,
|
||||
iconColor: "#fff",
|
||||
iconBgColor: "#336df4",
|
||||
|
||||
isNodeEnd: true,
|
||||
|
||||
expandable: false,
|
||||
selectable: false,
|
||||
|
||||
copyDisable: true,
|
||||
},
|
||||
|
||||
formMeta: {
|
||||
render: () => {
|
||||
return <BaseNode></BaseNode>;
|
||||
},
|
||||
},
|
||||
|
||||
canAdd(_, from) {
|
||||
// You can only add to the last node of the branch
|
||||
if (!from.isLast) return false;
|
||||
|
||||
/**
|
||||
* condition
|
||||
* blockIcon
|
||||
* inlineBlocks
|
||||
* block1
|
||||
* blockOrderIcon
|
||||
* <---- [add end]
|
||||
* block2
|
||||
* blockOrderIcon
|
||||
* end
|
||||
*/
|
||||
// originParent can determine whether it is condition , and then determine whether it is the last one
|
||||
// https://github.com/bytedance/flowgram.ai/pull/146
|
||||
if (from.parent && from.parent.parent?.flowNodeType === FlowNodeBaseType.INLINE_BLOCKS && from.parent.originParent && !from.parent.originParent.isLast) {
|
||||
const allBranches = from.parent.parent!.blocks;
|
||||
// Determine whether the last node of all branch is end, All branches are not allowed to be end
|
||||
const branchEndCount = allBranches.filter((block) => block.blocks[block.blocks.length - 1]?.getNodeMeta().isNodeEnd).length;
|
||||
return branchEndCount < allBranches.length - 1;
|
||||
}
|
||||
|
||||
return true;
|
||||
},
|
||||
|
||||
canDelete(ctx, node) {
|
||||
return node.parent !== ctx.document.root;
|
||||
},
|
||||
|
||||
onAdd() {
|
||||
const { t } = getI18n();
|
||||
|
||||
return {
|
||||
id: nanoid(),
|
||||
type: NodeType.End,
|
||||
data: {
|
||||
name: t("workflow_node.end.default_name"),
|
||||
},
|
||||
};
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,67 @@
|
||||
import { getI18n } from "react-i18next";
|
||||
import { IconRocket } from "@tabler/icons-react";
|
||||
import { Typography } from "antd";
|
||||
|
||||
import { WORKFLOW_TRIGGERS } from "@/domain/workflow";
|
||||
|
||||
import { BaseNode } from "./_shared";
|
||||
import { type NodeRegistry, NodeType } from "./typings";
|
||||
|
||||
export const StartNodeRegistry: NodeRegistry = {
|
||||
type: NodeType.Start,
|
||||
|
||||
meta: {
|
||||
helpText: getI18n().t("workflow_node.start.help"),
|
||||
|
||||
icon: IconRocket,
|
||||
iconColor: "#fff",
|
||||
iconBgColor: "#ed6d0c",
|
||||
|
||||
isStart: true,
|
||||
|
||||
expandable: false,
|
||||
selectable: false,
|
||||
|
||||
addDisable: true,
|
||||
copyDisable: true,
|
||||
deleteDisable: true,
|
||||
},
|
||||
|
||||
formMeta: {
|
||||
render: ({ form }) => {
|
||||
const { t } = getI18n();
|
||||
|
||||
const fieldTrigger = form.getValueIn<string>("config.trigger");
|
||||
const fieldTriggerCron = form.getValueIn<string>("config.triggerCron");
|
||||
|
||||
return (
|
||||
<BaseNode>
|
||||
<div className="flex items-center justify-between gap-1">
|
||||
{fieldTrigger ? (
|
||||
<>
|
||||
<div>
|
||||
{fieldTrigger === WORKFLOW_TRIGGERS.SCHEDULED
|
||||
? t("workflow.props.trigger.scheduled")
|
||||
: fieldTrigger === WORKFLOW_TRIGGERS.MANUAL
|
||||
? t("workflow.props.trigger.manual")
|
||||
: "\u00A0"}
|
||||
</div>
|
||||
<div>{fieldTrigger === WORKFLOW_TRIGGERS.SCHEDULED ? fieldTriggerCron || "\u00A0" : ""}</div>
|
||||
</>
|
||||
) : (
|
||||
t("workflow.detail.design.nodes.placeholder")
|
||||
)}
|
||||
</div>
|
||||
</BaseNode>
|
||||
);
|
||||
},
|
||||
},
|
||||
|
||||
canAdd: () => {
|
||||
return false;
|
||||
},
|
||||
|
||||
canDelete: () => {
|
||||
return false;
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,105 @@
|
||||
import { getI18n } from "react-i18next";
|
||||
import { ValidateTrigger } from "@flowgram.ai/fixed-layout-editor";
|
||||
import { IconArrowsSplit, IconCircleX } from "@tabler/icons-react";
|
||||
import { nanoid } from "nanoid";
|
||||
|
||||
import { BaseNode, BlockNode } from "./_shared";
|
||||
import { type NodeRegistry, NodeType } from "./typings";
|
||||
|
||||
export const TryCatchNodeRegistry: NodeRegistry = {
|
||||
type: NodeType.TryCatch,
|
||||
|
||||
meta: {
|
||||
helpText: getI18n().t("workflow_node.try_catch.help"),
|
||||
|
||||
icon: IconArrowsSplit,
|
||||
iconColor: "#fff",
|
||||
iconBgColor: "#373c43",
|
||||
|
||||
expandable: false,
|
||||
},
|
||||
|
||||
formMeta: {
|
||||
validateTrigger: ValidateTrigger.onChange,
|
||||
render: () => {
|
||||
return <BaseNode></BaseNode>;
|
||||
},
|
||||
},
|
||||
|
||||
onAdd() {
|
||||
const { t } = getI18n();
|
||||
|
||||
return {
|
||||
id: nanoid(),
|
||||
type: NodeType.TryCatch,
|
||||
data: {
|
||||
name: t("workflow_node.try_catch.default_name"),
|
||||
},
|
||||
blocks: [
|
||||
{
|
||||
id: nanoid(),
|
||||
type: NodeType.TryBlock,
|
||||
blocks: [],
|
||||
},
|
||||
{
|
||||
id: nanoid(),
|
||||
type: NodeType.CatchBlock,
|
||||
blocks: [
|
||||
{
|
||||
id: nanoid(),
|
||||
type: NodeType.End,
|
||||
data: {
|
||||
name: t("workflow_node.end.default_name"),
|
||||
},
|
||||
},
|
||||
],
|
||||
data: {
|
||||
name: t("workflow_node.catch_block.default_name"),
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
},
|
||||
};
|
||||
|
||||
export const CatchBlockNodeRegistry: NodeRegistry = {
|
||||
type: NodeType.CatchBlock,
|
||||
|
||||
meta: {
|
||||
addDisable: true,
|
||||
copyDisable: true,
|
||||
},
|
||||
|
||||
formMeta: {
|
||||
validateTrigger: ValidateTrigger.onChange,
|
||||
render: ({ form }) => {
|
||||
return (
|
||||
<BlockNode>
|
||||
<div className="flex items-center justify-center gap-2">
|
||||
<div className="flex items-center justify-center">
|
||||
<IconCircleX color="var(--color-error)" size="1.25em" stroke="1.25" />
|
||||
</div>
|
||||
<div className="truncate">{form.getValueIn<string>("name") || "\u00A0"}</div>
|
||||
</div>
|
||||
</BlockNode>
|
||||
);
|
||||
},
|
||||
},
|
||||
|
||||
canAdd: () => false,
|
||||
|
||||
canDelete: (_, node) => node.parent != null && node.parent.blocks.length >= 2,
|
||||
|
||||
onAdd() {
|
||||
const { t } = getI18n();
|
||||
|
||||
return {
|
||||
id: nanoid(),
|
||||
type: NodeType.CatchBlock,
|
||||
blocks: [],
|
||||
data: {
|
||||
name: t("workflow_node.catch_block.default_name"),
|
||||
},
|
||||
};
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,63 @@
|
||||
import { type FlowNodeRegistry } from "@flowgram.ai/fixed-layout-editor";
|
||||
import { nanoid } from "nanoid";
|
||||
|
||||
/**
|
||||
* 自定义节点注册
|
||||
*/
|
||||
export const nodeRegistry: FlowNodeRegistry = {
|
||||
/**
|
||||
* 自定义节点类型
|
||||
*/
|
||||
type: "condition",
|
||||
|
||||
/**
|
||||
* 自定义节点扩展:
|
||||
* - loop: 扩展为循环节点
|
||||
* - start: 扩展为开始节点
|
||||
* - dynamicSplit: 扩展为分支节点
|
||||
* - end: 扩展为结束节点
|
||||
* - tryCatch: 扩展为 tryCatch 节点
|
||||
* - break: 分支断开
|
||||
* - default: 扩展为普通节点 (默认)
|
||||
*/
|
||||
extend: "dynamicSplit",
|
||||
|
||||
/**
|
||||
* 节点配置信息
|
||||
*/
|
||||
meta: {
|
||||
isStart: false, // 是否为开始节点
|
||||
isNodeEnd: false, // 是否为结束节点,结束节点后边无法再添加节点
|
||||
draggable: false, // 是否可拖拽,如开始节点和结束节点无法拖拽
|
||||
selectable: false, // 触发器等开始节点不能被框选
|
||||
deleteDisable: true, // 禁止删除
|
||||
copyDisable: true, // 禁止copy
|
||||
addDisable: true, // 禁止添加
|
||||
},
|
||||
|
||||
onAdd() {
|
||||
return {
|
||||
id: `condition_${nanoid(5)}`,
|
||||
type: "condition",
|
||||
data: {
|
||||
title: "Condition",
|
||||
},
|
||||
blocks: [
|
||||
{
|
||||
id: nanoid(5),
|
||||
type: "block",
|
||||
data: {
|
||||
title: "If_0",
|
||||
},
|
||||
},
|
||||
{
|
||||
id: nanoid(5),
|
||||
type: "block",
|
||||
data: {
|
||||
title: "If_1",
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,161 @@
|
||||
import { useMemo } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useClientContext, useNodeRender } from "@flowgram.ai/fixed-layout-editor";
|
||||
import { IconCopy, IconDotsVertical, IconGripVertical, IconLabel, IconX } from "@tabler/icons-react";
|
||||
import { Button, type ButtonProps, Card, Dropdown, type MenuProps, Popover, Tooltip, Typography, theme } from "antd";
|
||||
import { mergeCls } from "@/utils/css";
|
||||
|
||||
import { type NodeRegistry } from "./typings";
|
||||
|
||||
export const BaseNode = ({ className, style, children }: { className?: string; style?: React.CSSProperties; children?: React.ReactNode }) => {
|
||||
const { token: themeToken } = theme.useToken();
|
||||
|
||||
const ctx = useClientContext();
|
||||
const { playground } = ctx;
|
||||
|
||||
const nodeRender = useNodeRender();
|
||||
const nodeRegistry = nodeRender.node.getNodeRegistry<NodeRegistry>();
|
||||
|
||||
const NodeIcon = nodeRegistry.meta?.icon;
|
||||
const renderNodeIcon = () => {
|
||||
return NodeIcon == null ? null : (
|
||||
<div
|
||||
className="mr-2 flex size-9 items-center justify-center rounded-lg bg-white text-primary shadow-md dark:bg-stone-200"
|
||||
style={{
|
||||
color: nodeRegistry.meta?.iconColor,
|
||||
backgroundColor: nodeRegistry.meta?.iconBgColor,
|
||||
}}
|
||||
>
|
||||
<NodeIcon size="1.75em" color={nodeRegistry.meta?.iconColor} stroke="1.25" />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={mergeCls("relative w-[320px] group/node", className)} style={style}>
|
||||
<Card className="rounded-xl shadow-sm" styles={{ body: { padding: 0 } }} hoverable>
|
||||
<div className="flex items-center gap-1 overflow-hidden p-3">
|
||||
{nodeRegistry.meta?.helpText == null ? (
|
||||
renderNodeIcon()
|
||||
) : (
|
||||
<Tooltip title={<span dangerouslySetInnerHTML={{ __html: nodeRegistry.meta.helpText }}></span>} mouseEnterDelay={1}>
|
||||
<div className="cursor-help">{renderNodeIcon()}</div>
|
||||
</Tooltip>
|
||||
)}
|
||||
<div className="flex-1 overflow-hidden">
|
||||
<div className="truncate">
|
||||
<Typography.Text>{nodeRender.data?.name || "\u00A0"}</Typography.Text>
|
||||
</div>
|
||||
{children != null && (
|
||||
<div className="truncate text-xs" style={{ color: themeToken.colorTextTertiary }}>
|
||||
{children}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div className="-mr-2 ml-1" onClick={(e) => e.stopPropagation()}>
|
||||
<NodeMenuButton className="opacity-0 transition-opacity group-hover/node:opacity-100" size="small" />
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
{!playground.config.readonlyOrDisabled && nodeRegistry.meta?.draggable === true && (
|
||||
<div className="absolute top-1/2 -left-4 hidden -translate-y-1/2 group-hover/node:block">
|
||||
<IconGripVertical size="1em" stroke="1" />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export const BlockNode = ({ className, style, children }: { className?: string; style?: React.CSSProperties; children?: React.ReactNode }) => {
|
||||
const ctx = useClientContext();
|
||||
const { playground } = ctx;
|
||||
|
||||
const nodeRender = useNodeRender();
|
||||
const nodeRegistry = nodeRender.node.getNodeRegistry<NodeRegistry>();
|
||||
|
||||
return (
|
||||
<Popover classNames={{ root: "shadow-md" }} styles={{ body: { padding: 0 } }} arrow={false} content={<NodeMenuButton variant="text" />} placement="right">
|
||||
<div className={mergeCls("relative w-[240px] group/node", className)} style={style}>
|
||||
<Card className="rounded-xl shadow-sm" styles={{ body: { padding: 0 } }} hoverable>
|
||||
<div className="overflow-hidden px-3 py-2">
|
||||
<div className="truncate text-center">{children ?? (nodeRender.data?.name || "\u00A0")}</div>
|
||||
</div>
|
||||
</Card>
|
||||
{!playground.config.readonlyOrDisabled && nodeRegistry.meta?.draggable === true && (
|
||||
<div className="absolute top-1/2 -left-4 hidden -translate-y-1/2 group-hover/node:block">
|
||||
<IconGripVertical size="1em" stroke="1" />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</Popover>
|
||||
);
|
||||
};
|
||||
|
||||
export const NodeMenuButton = ({ className, style, ...props }: ButtonProps) => {
|
||||
const ctx = useClientContext();
|
||||
const { playground } = ctx;
|
||||
|
||||
const menuItems = useNodeMenuItems();
|
||||
|
||||
return playground.config.readonlyOrDisabled ? null : (
|
||||
<Dropdown menu={{ items: menuItems }} trigger={["click"]} arrow={false}>
|
||||
<Button className={className} style={style} ghost icon={<IconDotsVertical color="grey" size="1.25em" />} type="text" {...props} />
|
||||
</Dropdown>
|
||||
);
|
||||
};
|
||||
|
||||
export const useNodeMenuItems = () => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const ctx = useClientContext();
|
||||
const { operation } = ctx;
|
||||
|
||||
const nodeRender = useNodeRender();
|
||||
const nodeRegistry = nodeRender.node.getNodeRegistry<NodeRegistry>();
|
||||
|
||||
const nodeDeleteDisabled = useMemo(() => {
|
||||
if (nodeRegistry.canDelete != null) {
|
||||
return !nodeRegistry.canDelete(ctx, nodeRender.node);
|
||||
}
|
||||
return nodeRegistry.meta!.deleteDisable;
|
||||
}, [nodeRegistry, nodeRender.node]);
|
||||
|
||||
const menuItems = useMemo<Required<MenuProps>["items"]>(() => {
|
||||
return [
|
||||
{
|
||||
key: "rename",
|
||||
label: t("workflow_node.action.rename_node"),
|
||||
icon: <IconLabel size="1em" />,
|
||||
onClick: () => {
|
||||
operation.deleteNode(nodeRender.node);
|
||||
alert("TODO: rename");
|
||||
},
|
||||
},
|
||||
{
|
||||
key: "duplicate",
|
||||
label: t("workflow_node.action.duplicate_node"),
|
||||
icon: <IconCopy size="1em" />,
|
||||
onClick: () => {
|
||||
operation.deleteNode(nodeRender.node);
|
||||
alert("TODO: duplicate");
|
||||
},
|
||||
},
|
||||
{
|
||||
type: "divider",
|
||||
},
|
||||
{
|
||||
key: "remove",
|
||||
label: t("workflow_node.action.remove_node"),
|
||||
icon: <IconX size="1em" />,
|
||||
danger: true,
|
||||
disabled: nodeDeleteDisabled,
|
||||
onClick: () => {
|
||||
operation.deleteNode(nodeRender.node);
|
||||
alert("TODO: remove");
|
||||
},
|
||||
},
|
||||
];
|
||||
}, [nodeRender.node, nodeDeleteDisabled]);
|
||||
|
||||
return menuItems;
|
||||
};
|
||||
@@ -0,0 +1,23 @@
|
||||
import { BizApplyNodeRegistry, BizDeployNodeRegistry, BizMonitorNodeRegistry, BizNotifyNodeRegistry, BizUploadNodeRegistry } from "./BusinessNode";
|
||||
import { BranchBlockNodeRegistry, ConditionNodeRegistry } from "./ConditionNode";
|
||||
import { EndNodeRegistry } from "./EndNode";
|
||||
import { StartNodeRegistry } from "./StartNode";
|
||||
import { CatchBlockNodeRegistry, TryCatchNodeRegistry } from "./TryCatchNode";
|
||||
|
||||
export const getFlowNodeRegistries = () => {
|
||||
return [
|
||||
StartNodeRegistry,
|
||||
BizApplyNodeRegistry,
|
||||
BizUploadNodeRegistry,
|
||||
BizMonitorNodeRegistry,
|
||||
BizDeployNodeRegistry,
|
||||
BizNotifyNodeRegistry,
|
||||
ConditionNodeRegistry,
|
||||
BranchBlockNodeRegistry,
|
||||
TryCatchNodeRegistry,
|
||||
CatchBlockNodeRegistry,
|
||||
EndNodeRegistry,
|
||||
];
|
||||
};
|
||||
|
||||
export type * from "./typings";
|
||||
@@ -0,0 +1,55 @@
|
||||
import { FlowNodeBaseType } from "@flowgram.ai/document";
|
||||
import {
|
||||
type FixedLayoutPluginContext,
|
||||
type FlowNodeEntity,
|
||||
type FlowNodeJSON,
|
||||
type FlowNodeMeta,
|
||||
type FlowNodeRegistry,
|
||||
type FormMeta,
|
||||
type FormRenderProps,
|
||||
} from "@flowgram.ai/fixed-layout-editor";
|
||||
|
||||
export enum NodeType {
|
||||
Start = FlowNodeBaseType.START,
|
||||
End = FlowNodeBaseType.END,
|
||||
Condition = "condition",
|
||||
BranchBlock = "branchBlock",
|
||||
TryCatch = "tryCatch",
|
||||
TryBlock = "tryBlock",
|
||||
CatchBlock = "catchBlock",
|
||||
BizApply = "bizApply",
|
||||
BizUpload = "bizUpload",
|
||||
BizMonitor = "bizMonitor",
|
||||
BizDeploy = "bizDeploy",
|
||||
BizNotify = "bizNotify",
|
||||
}
|
||||
|
||||
export interface NodeJSON extends FlowNodeJSON {
|
||||
data: {
|
||||
name?: string;
|
||||
disabled?: boolean;
|
||||
[key: string]: any;
|
||||
};
|
||||
}
|
||||
|
||||
export interface DocumentJSON {
|
||||
nodes: NodeJSON[];
|
||||
}
|
||||
|
||||
export interface NodeMeta extends FlowNodeMeta {
|
||||
style?: React.CSSProperties;
|
||||
helpText?: React.ReactNode;
|
||||
icon?: React.Component;
|
||||
iconColor?: string;
|
||||
iconBgColor?: string;
|
||||
}
|
||||
|
||||
export interface NodeRegistry<V extends NodeJSON["data"] = NodeJSON["data"]> extends FlowNodeRegistry<NodeMeta> {
|
||||
meta?: FlowNodeMeta;
|
||||
formMeta?: Omit<FormMeta<V>, "render"> & {
|
||||
render: (props: FormRenderProps<V>) => React.ReactElement;
|
||||
};
|
||||
canAdd?: (ctx: FixedLayoutPluginContext, from: FlowNodeEntity) => boolean;
|
||||
canDelete?: (ctx: FixedLayoutPluginContext, from: FlowNodeEntity) => boolean;
|
||||
onAdd?: (ctx: FixedLayoutPluginContext, from: FlowNodeEntity) => FlowNodeJSON;
|
||||
}
|
||||
@@ -253,8 +253,8 @@ const SharedNodeBlock = ({ children, node, disabled, onClick }: SharedNodeBlockP
|
||||
content={<SharedNodeMenu node={node} disabled={disabled} trigger={<Button color="primary" icon={<IconDotsVertical size="1em" />} variant="text" />} />}
|
||||
placement="rightTop"
|
||||
>
|
||||
<div className="relative w-[256px] overflow-hidden">
|
||||
<Card className="shadow-md" styles={{ body: { padding: 0 } }} hoverable>
|
||||
<div className="relative w-[256px]">
|
||||
<Card className="overflow-hidden shadow-md" styles={{ body: { padding: 0 } }} hoverable>
|
||||
<div className="flex h-[48px] flex-col items-center justify-center truncate bg-primary px-4 py-2 text-white">
|
||||
<SharedNodeTitle
|
||||
className="overflow-hidden outline-hidden focus:rounded-xs focus:bg-background focus:text-foreground"
|
||||
@@ -331,7 +331,7 @@ const SharedNodeConfigDrawer = ({
|
||||
if (changed) {
|
||||
modal.confirm({
|
||||
title: t("common.text.operation_confirm"),
|
||||
content: t("workflow_node.unsaved_changes.confirm"),
|
||||
content: t("workflow.detail.design.unsaved_changes.confirm"),
|
||||
onOk: () => resolve(void 0),
|
||||
onCancel: () => reject(),
|
||||
});
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
"workflow.action.rollback.button": "Rollback",
|
||||
"workflow.action.rollback.modal.title": "Rollback changes",
|
||||
"workflow.action.rollback.modal.content": "Are you sure to rollback your changes to the last published version?",
|
||||
"workflow.action.run.button": "Run at once",
|
||||
"workflow.action.run.button": "Run",
|
||||
"workflow.action.run.modal.title": "Run workflow",
|
||||
"workflow.action.run.modal.content": "You have unpublished changes. Do you really want to run this workflow based on the last published version?",
|
||||
"workflow.action.run.prompt": "Running... Please check the history later",
|
||||
@@ -60,8 +60,18 @@
|
||||
|
||||
"workflow.detail.baseinfo.name.placeholder": "Please enter workflow name",
|
||||
"workflow.detail.baseinfo.description.placeholder": "Please enter workflow description",
|
||||
"workflow.detail.design.tab": "Orchestration",
|
||||
"workflow.detail.design_legacy.tab": "Orchestration (Legacy)",
|
||||
"workflow.detail.design.draft.alert": "The orchestration is not released yet.",
|
||||
"workflow.detail.design_legacy.tab": "Orchestration (legacy)",
|
||||
"workflow.detail.design.tab": "Orchestration (TODO)",
|
||||
"workflow.detail.design.toolbar.zoom": "Zoom",
|
||||
"workflow.detail.design.toolbar.zoomin": "Zoom in",
|
||||
"workflow.detail.design.toolbar.zoomout": "Zoom out",
|
||||
"workflow.detail.design.toolbar.auto_fit": "Auto fit view",
|
||||
"workflow.detail.design.toolbar.horizontal_layout": "Horizontal Layout",
|
||||
"workflow.detail.design.toolbar.vertical_layout": "Vertical Layout",
|
||||
"workflow.detail.design.toolbar.hand_mode": "Hand mode",
|
||||
"workflow.detail.design.toolbar.pointer_mode": "Pointer mode",
|
||||
"workflow.detail.design.nodes.placeholder": "Please configure",
|
||||
"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?",
|
||||
"workflow.detail.runs.tab": "History runs"
|
||||
}
|
||||
|
||||
@@ -9,9 +9,8 @@
|
||||
"workflow_node.action.duplicate_branch": "Duplicate branch",
|
||||
"workflow_node.action.remove_branch": "Delete branch",
|
||||
|
||||
"workflow_node.unsaved_changes.confirm": "You have unsaved changes. Do you really want to close the panel and drop those changes?",
|
||||
|
||||
"workflow_node.start.label": "Start",
|
||||
"workflow_node.start.help": "Define the trigger for workflow initiation. A workflow can only have one start node.",
|
||||
"workflow_node.start.default_name": "Start",
|
||||
"workflow_node.start.form.trigger.label": "Trigger",
|
||||
"workflow_node.start.form.trigger.placeholder": "Please select trigger",
|
||||
@@ -24,7 +23,8 @@
|
||||
"workflow_node.start.form.trigger_cron.extra": "Expected execution time for the last 5 times:",
|
||||
"workflow_node.start.form.trigger_cron.guide": "Tips: If you have multiple workflows, it is recommended to set them to run at different times of the day instead of always running at a specific time. And please don't always set it to midnight every day to avoid spikes in traffic. <br><br>Reference links:<br>1. <a href=\"https://letsencrypt.org/docs/rate-limits/\" target=\"_blank\">Let’s Encrypt rate limits</a><br>2. <a href=\"https://letsencrypt.org/docs/faq/#why-should-my-let-s-encrypt-acme-client-run-at-a-random-time\" target=\"_blank\">Why should my Let’s Encrypt (ACME) client run at a random time?</a>",
|
||||
|
||||
"workflow_node.apply.label": "Obtain certificate",
|
||||
"workflow_node.apply.label": "Request certificate",
|
||||
"workflow_node.apply.help": "Apply for SSL certificate issuance from the certificate authority.",
|
||||
"workflow_node.apply.default_name": "Application",
|
||||
"workflow_node.apply.form.domains.label": "Domains",
|
||||
"workflow_node.apply.form.domains.placeholder": "Please enter domains (separated by semicolons)",
|
||||
@@ -104,6 +104,7 @@
|
||||
"workflow_node.apply.form.skip_before_expiry_days.tooltip": "Be careful not to exceed the validity period limit of the issued certificate, otherwise the certificate may never be renewed.",
|
||||
|
||||
"workflow_node.upload.label": "Upload certificate",
|
||||
"workflow_node.upload.help": "Upload the user's existing SSL certificate.",
|
||||
"workflow_node.upload.default_name": "Uploading",
|
||||
"workflow_node.upload.form.domains.label": "Domains",
|
||||
"workflow_node.upload.form.domains.placholder": "Please select certificate file",
|
||||
@@ -112,7 +113,21 @@
|
||||
"workflow_node.upload.form.private_key.label": "Private key (PEM format)",
|
||||
"workflow_node.upload.form.private_key.placeholder": "-----BEGIN (RSA|EC) PRIVATE KEY-----...-----END(RSA|EC) PRIVATE KEY-----",
|
||||
|
||||
"workflow_node.monitor.label": "Monitor certificate",
|
||||
"workflow_node.monitor.help": "Obtain the SSL certificate of the website through HTTPS protocol.",
|
||||
"workflow_node.monitor.default_name": "Monitoring",
|
||||
"workflow_node.monitor.form.guide": "Tips: Certimate will send a HEAD request to the target address to obtain the certificate. Please ensure that the address is accessible through HTTPS protocol.",
|
||||
"workflow_node.monitor.form.host.label": "Host",
|
||||
"workflow_node.monitor.form.host.placeholder": "Please enter host",
|
||||
"workflow_node.monitor.form.port.label": "Port",
|
||||
"workflow_node.monitor.form.port.placeholder": "Please enter port",
|
||||
"workflow_node.monitor.form.domain.label": "Domain (Optional)",
|
||||
"workflow_node.monitor.form.domain.placeholder": "Please enter domain name",
|
||||
"workflow_node.monitor.form.request_path.label": "Request path (Optional)",
|
||||
"workflow_node.monitor.form.request_path.placeholder": "Please enter request path",
|
||||
|
||||
"workflow_node.deploy.label": "Deploy certificate",
|
||||
"workflow_node.deploy.help": "Invoke the APIs of the service provider to deploy the SSL certificate.",
|
||||
"workflow_node.deploy.default_name": "Deployment",
|
||||
"workflow_node.deploy.form.provider.label": "Deploy target",
|
||||
"workflow_node.deploy.form.provider.placeholder": "Please select deploy target",
|
||||
@@ -938,19 +953,8 @@
|
||||
"workflow_node.deploy.form.skip_on_last_succeeded.switch.on": "skip",
|
||||
"workflow_node.deploy.form.skip_on_last_succeeded.switch.off": "not skip",
|
||||
|
||||
"workflow_node.monitor.label": "Monitor certificate",
|
||||
"workflow_node.monitor.default_name": "Monitoring",
|
||||
"workflow_node.monitor.form.guide": "Tips: Certimate will send a HEAD request to the target address to obtain the certificate. Please ensure that the address is accessible through HTTPS protocol.",
|
||||
"workflow_node.monitor.form.host.label": "Host",
|
||||
"workflow_node.monitor.form.host.placeholder": "Please enter host",
|
||||
"workflow_node.monitor.form.port.label": "Port",
|
||||
"workflow_node.monitor.form.port.placeholder": "Please enter port",
|
||||
"workflow_node.monitor.form.domain.label": "Domain (Optional)",
|
||||
"workflow_node.monitor.form.domain.placeholder": "Please enter domain name",
|
||||
"workflow_node.monitor.form.request_path.label": "Request path (Optional)",
|
||||
"workflow_node.monitor.form.request_path.placeholder": "Please enter request path",
|
||||
|
||||
"workflow_node.notify.label": "Send notification",
|
||||
"workflow_node.notify.help": "Invoke the APIs of the service provider to push message notifications.",
|
||||
"workflow_node.notify.default_name": "Notification",
|
||||
"workflow_node.notify.form.subject.label": "Subject",
|
||||
"workflow_node.notify.form.subject.placeholder": "Please enter subject",
|
||||
@@ -989,13 +993,11 @@
|
||||
"workflow_node.notify.form.skip_on_all_prev_skipped.switch.on": "skip",
|
||||
"workflow_node.notify.form.skip_on_all_prev_skipped.switch.off": "not skip",
|
||||
|
||||
"workflow_node.end.label": "End",
|
||||
"workflow_node.end.default_name": "End",
|
||||
|
||||
"workflow_node.branch.label": "Parallel/Conditional branch",
|
||||
"workflow_node.branch.default_name": "Branch",
|
||||
|
||||
"workflow_node.condition.label": "Branch",
|
||||
"workflow_node.condition.help": "When the specified conditions are met, enter the corresponding branch. The failure of a node in a certain branch does not affect the continuation of parallel branch execution.",
|
||||
"workflow_node.condition.default_name": "Branch",
|
||||
"workflow_node.condition.default_name.template_certtest_on_expire_soon": "If the certificate will expire soon ...",
|
||||
"workflow_node.condition.default_name.template_certtest_on_expired": "If the certificate has expired ...",
|
||||
@@ -1021,6 +1023,9 @@
|
||||
"workflow_node.condition.form.expression.value.option.false.label": "False",
|
||||
"workflow_node.condition.form.expression.add_condition.button": "Add condition",
|
||||
|
||||
"workflow_node.branch_block.label": "Branch",
|
||||
"workflow_node.branch_block.default_name": "Branch",
|
||||
|
||||
"workflow_node.execute_result_branch.label": "Execution result branch",
|
||||
"workflow_node.execute_result_branch.default_name": "Branch",
|
||||
|
||||
@@ -1028,5 +1033,16 @@
|
||||
"workflow_node.execute_success.default_name": "On Succeeded",
|
||||
|
||||
"workflow_node.execute_failure.label": "If the previous node failed ...",
|
||||
"workflow_node.execute_failure.default_name": "On Failed"
|
||||
"workflow_node.execute_failure.default_name": "On Failed",
|
||||
|
||||
"workflow_node.try_catch.label": "Execution result branch",
|
||||
"workflow_node.try_catch.help": "Attempt to execute subsequent nodes, and when any node fails to execute, interrupt and enter the execution failure branch.",
|
||||
"workflow_node.try_catch.default_name": "Try...",
|
||||
|
||||
"workflow_node.catch_block.label": "Execution failure branch",
|
||||
"workflow_node.catch_block.default_name": "On failed...",
|
||||
|
||||
"workflow_node.end.label": "End",
|
||||
"workflow_node.end.help": "Suspend the workflow and exit. Usually the last node of a workflow.",
|
||||
"workflow_node.end.default_name": "End"
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
{
|
||||
"certificate.page.title": "SSL 证书",
|
||||
"certificate.page.subtitle": "SSL 证书含有网站的公钥和网站标识以及其他相关信息。它们来自于工作流的执行输出。",
|
||||
"certificate.page.subtitle": "SSL 证书含有网站的公钥和网站标识以及其他相关信息。它们来自于工作流的运行输出。",
|
||||
|
||||
"certificate.nodata.title": "暂无证书",
|
||||
"certificate.nodata.description": "当前未找到任何认证。请先执行一个工作流以生成证书。",
|
||||
"certificate.nodata.description": "当前未找到任何认证。请先运行一个工作流以生成证书。",
|
||||
"certificate.nodata.button": "前往工作流",
|
||||
|
||||
"certificate.search.placeholder": "按证书名称或序列号搜索……",
|
||||
|
||||
@@ -7,8 +7,8 @@
|
||||
"dashboard.statistics.all_workflows": "所有工作流",
|
||||
"dashboard.statistics.enabled_workflows": "已启用工作流",
|
||||
|
||||
"dashboard.latest_workflow_runs": "最近执行的工作流",
|
||||
"dashboard.latest_workflow_runs.nodata.description": "当前未找到任何工作流执行记录。请先执行一个工作流。",
|
||||
"dashboard.latest_workflow_runs": "最近运行的工作流",
|
||||
"dashboard.latest_workflow_runs.nodata.description": "当前未找到任何工作流运行历史。请先运行一个工作流。",
|
||||
"dashboard.latest_workflow_runs.nodata.button": "前往工作流",
|
||||
|
||||
"dashboard.quick_actions": "快捷操作",
|
||||
|
||||
@@ -56,8 +56,8 @@
|
||||
|
||||
"settings.persistence.tab": "数据持久化",
|
||||
"settings.persistence.title": "定期清理数据",
|
||||
"settings.persistence.form.workflow_runs_max_days.label": "工作流执行历史保留天数",
|
||||
"settings.persistence.form.workflow_runs_max_days.placeholder": "请输入执行历史保留天数",
|
||||
"settings.persistence.form.workflow_runs_max_days.label": "工作流运行历史保留天数",
|
||||
"settings.persistence.form.workflow_runs_max_days.placeholder": "请输入运行历史保留天数",
|
||||
"settings.persistence.form.workflow_runs_max_days.unit": "天",
|
||||
"settings.persistence.form.workflow_runs_max_days.extra": "设置为 <b>0</b> 表示永久保留,不会自动清理。建议设置为 <b>180</b> 天以上。",
|
||||
"settings.persistence.form.expired_certificates_max_days.label": "证书过期后保留天数",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"workflow.page.title": "工作流",
|
||||
"workflow.page.subtitle": "工作流是自动化流程的节点集合。当满足触发条件时,工作流开始顺序执行各节点以完成复杂的任务。",
|
||||
"workflow.page.subtitle": "工作流是自动化流程的节点集合。当满足触发条件时,工作流开始顺序运行各节点以完成复杂的任务。",
|
||||
|
||||
"workflow.nodata.title": "暂无工作流",
|
||||
"workflow.nodata.description": "当前未找到工作流。请先创建。",
|
||||
@@ -28,17 +28,17 @@
|
||||
"workflow.action.rollback.button": "回退更改",
|
||||
"workflow.action.rollback.modal.title": "回退更改",
|
||||
"workflow.action.rollback.modal.content": "确定要回退到最近一次发布的版本吗?",
|
||||
"workflow.action.run.button": "立即执行",
|
||||
"workflow.action.run.modal.title": "执行工作流",
|
||||
"workflow.action.run.modal.content": "你有尚未发布的更改。确定要以最近一次发布的版本继续执行吗?",
|
||||
"workflow.action.run.prompt": "执行中……请稍后查看执行历史",
|
||||
"workflow.action.run.button": "运行",
|
||||
"workflow.action.run.modal.title": "运行工作流",
|
||||
"workflow.action.run.modal.content": "你有尚未发布的更改。确定要以最近一次发布的版本继续运行吗?",
|
||||
"workflow.action.run.prompt": "运行中……请稍后查看运行历史",
|
||||
|
||||
"workflow.props.name": "名称",
|
||||
"workflow.props.description": "描述",
|
||||
"workflow.props.trigger": "触发方式",
|
||||
"workflow.props.trigger.scheduled": "定时",
|
||||
"workflow.props.trigger.manual": "手动",
|
||||
"workflow.props.last_run_at": "最近执行时间",
|
||||
"workflow.props.last_run_at": "最近运行时间",
|
||||
"workflow.props.state": "是否启用",
|
||||
"workflow.props.state.filter.all": "全部",
|
||||
"workflow.props.state.filter.enabled": "启用",
|
||||
@@ -60,8 +60,18 @@
|
||||
|
||||
"workflow.detail.baseinfo.name.placeholder": "请输入工作流名称",
|
||||
"workflow.detail.baseinfo.description.placeholder": "请输入工作流描述",
|
||||
"workflow.detail.design.tab": "流程编排",
|
||||
"workflow.detail.design_legacy.tab": "旧版编辑器",
|
||||
"workflow.detail.design.draft.alert": "当前编排有未发布的更改。",
|
||||
"workflow.detail.runs.tab": "执行历史"
|
||||
"workflow.detail.design_legacy.tab": "流程编排(旧版)",
|
||||
"workflow.detail.design.tab": "流程编排 (TODO)",
|
||||
"workflow.detail.design.toolbar.zoom": "缩放",
|
||||
"workflow.detail.design.toolbar.zoomin": "放大",
|
||||
"workflow.detail.design.toolbar.zoomout": "缩小",
|
||||
"workflow.detail.design.toolbar.auto_fit": "自适应视图",
|
||||
"workflow.detail.design.toolbar.horizontal_layout": "横向布局",
|
||||
"workflow.detail.design.toolbar.vertical_layout": "竖向布局",
|
||||
"workflow.detail.design.toolbar.hand_mode": "触摸模式",
|
||||
"workflow.detail.design.toolbar.pointer_mode": "指针模式",
|
||||
"workflow.detail.design.nodes.placeholder": "请完成配置",
|
||||
"workflow.detail.design.unpublished_draft.alert": "当前编排有未发布的更改。",
|
||||
"workflow.detail.design.unsaved_changes.confirm": "你有尚未保存的更改。确定要关闭面板吗?",
|
||||
"workflow.detail.runs.tab": "运行历史"
|
||||
}
|
||||
|
||||
@@ -9,9 +9,8 @@
|
||||
"workflow_node.action.duplicate_branch": "复制分支",
|
||||
"workflow_node.action.remove_branch": "删除分支",
|
||||
|
||||
"workflow_node.unsaved_changes.confirm": "你有尚未保存的更改。确定要关闭面板吗?",
|
||||
|
||||
"workflow_node.start.label": "开始",
|
||||
"workflow_node.start.help": "定义工作流启动的触发条件。一个工作流只能有一个开始节点。",
|
||||
"workflow_node.start.default_name": "开始",
|
||||
"workflow_node.start.form.trigger.label": "触发方式",
|
||||
"workflow_node.start.form.trigger.placeholder": "请选择触发方式",
|
||||
@@ -21,10 +20,11 @@
|
||||
"workflow_node.start.form.trigger_cron.placeholder": "请输入 Cron 表达式",
|
||||
"workflow_node.start.form.trigger_cron.errmsg.invalid": "请输入正确的 Cron 表达式",
|
||||
"workflow_node.start.form.trigger_cron.tooltip": "五段式表达式,支持使用任意值(即 <strong>*</strong>)、值列表分隔符(即 <strong>,</strong>)、值的范围(即 <strong>-</strong>)、步骤值(即 <strong>/</strong>)等四种表达式。时区以服务器设置为准。",
|
||||
"workflow_node.start.form.trigger_cron.extra": "预计最近 5 次执行时间:",
|
||||
"workflow_node.start.form.trigger_cron.extra": "预计最近 5 次运行时间:",
|
||||
"workflow_node.start.form.trigger_cron.guide": "小贴士:如果你有多个工作流,建议将它们设置为在一天中的多个时间段运行,而非总是在相同的特定时间。也不要总是设置为每日零时,以免遭遇证书颁发机构的流量高峰。<br><br>参考链接:<br>1. <a href=\"https://letsencrypt.org/zh-cn/docs/rate-limits/\" target=\"_blank\">Let’s Encrypt 速率限制</a><br>2. <a href=\"https://letsencrypt.org/zh-cn/docs/faq/#%E4%B8%BA%E4%BB%80%E4%B9%88%E6%88%91%E7%9A%84-let-s-encrypt-acme-%E5%AE%A2%E6%88%B7%E7%AB%AF%E5%90%AF%E5%8A%A8%E6%97%B6%E9%97%B4%E5%BA%94%E5%BD%93%E9%9A%8F%E6%9C%BA\" target=\"_blank\">为什么我的 Let’s Encrypt (ACME) 客户端启动时间应当随机?</a>",
|
||||
|
||||
"workflow_node.apply.label": "申请签发证书",
|
||||
"workflow_node.apply.help": "从证书颁发机构申请签发 SSL 证书。",
|
||||
"workflow_node.apply.default_name": "申请",
|
||||
"workflow_node.apply.form.domains.label": "域名",
|
||||
"workflow_node.apply.form.domains.placeholder": "请输入域名(多个值请用半角分号隔开)",
|
||||
@@ -98,11 +98,12 @@
|
||||
"workflow_node.apply.form.skip_before_expiry_days.label": "重复申请",
|
||||
"workflow_node.apply.form.skip_before_expiry_days.placeholder": "请输入续期间隔",
|
||||
"workflow_node.apply.form.skip_before_expiry_days.prefix": "当上次签发的证书剩余有效期大于",
|
||||
"workflow_node.apply.form.skip_before_expiry_days.suffix": "时,再次执行工作流时跳过此申请节点。",
|
||||
"workflow_node.apply.form.skip_before_expiry_days.suffix": "时,再次运行工作流时跳过此申请节点。",
|
||||
"workflow_node.apply.form.skip_before_expiry_days.unit": "天",
|
||||
"workflow_node.apply.form.skip_before_expiry_days.tooltip": "注意不要超过颁发的证书最大有效期,否则证书可能永远不会续期。",
|
||||
|
||||
"workflow_node.upload.label": "上传自有证书",
|
||||
"workflow_node.upload.help": "上传用户已有的本地 SSL 证书。",
|
||||
"workflow_node.upload.default_name": "上传",
|
||||
"workflow_node.upload.form.domains.label": "域名",
|
||||
"workflow_node.upload.form.domains.placeholder": "上传证书文件后显示",
|
||||
@@ -111,7 +112,21 @@
|
||||
"workflow_node.upload.form.private_key.label": "私钥文件(PEM 格式)",
|
||||
"workflow_node.upload.form.private_key.placeholder": "-----BEGIN (RSA|EC) PRIVATE KEY-----...-----END(RSA|EC) PRIVATE KEY-----",
|
||||
|
||||
"workflow_node.monitor.label": "监控网站证书",
|
||||
"workflow_node.monitor.help": "通过 HTTPS 协议获取指定网站的 SSL 证书。",
|
||||
"workflow_node.monitor.default_name": "监控",
|
||||
"workflow_node.monitor.form.guide": "小贴士:Certimate 将向目标地址发送一个 HEAD 请求来获取相应的域名证书,请确保该地址可通过 HTTPS 协议访问。",
|
||||
"workflow_node.monitor.form.host.label": "主机地址",
|
||||
"workflow_node.monitor.form.host.placeholder": "请输入主机地址(可以是域名或 IP)",
|
||||
"workflow_node.monitor.form.port.label": "主机端口",
|
||||
"workflow_node.monitor.form.port.placeholder": "请输入主机端口",
|
||||
"workflow_node.monitor.form.domain.label": "域名(可选)",
|
||||
"workflow_node.monitor.form.domain.placeholder": "请输入域名(仅当主机地址为 IP 时可选)",
|
||||
"workflow_node.monitor.form.request_path.label": "请求路径(可选)",
|
||||
"workflow_node.monitor.form.request_path.placeholder": "请输入请求路径",
|
||||
|
||||
"workflow_node.deploy.label": "部署证书到 ...",
|
||||
"workflow_node.deploy.help": "调用服务提供商相关 API,将 SSL 证书部署到指定的目标。",
|
||||
"workflow_node.deploy.default_name": "部署",
|
||||
"workflow_node.deploy.form.provider.label": "部署目标",
|
||||
"workflow_node.deploy.form.provider.placeholder": "请选择部署目标",
|
||||
@@ -931,24 +946,13 @@
|
||||
"workflow_node.deploy.form.webhook_data.errmsg.json_invalid": "请输入有效的 JSON 格式字符串",
|
||||
"workflow_node.deploy.form.strategy_config.label": "执行策略",
|
||||
"workflow_node.deploy.form.skip_on_last_succeeded.label": "重复部署",
|
||||
"workflow_node.deploy.form.skip_on_last_succeeded.prefix": "当上次部署相同证书成功时,再次执行工作流时",
|
||||
"workflow_node.deploy.form.skip_on_last_succeeded.prefix": "当上次部署相同证书成功时,再次运行工作流时",
|
||||
"workflow_node.deploy.form.skip_on_last_succeeded.suffix": "此部署节点。",
|
||||
"workflow_node.deploy.form.skip_on_last_succeeded.switch.on": "跳过",
|
||||
"workflow_node.deploy.form.skip_on_last_succeeded.switch.off": "不跳过",
|
||||
|
||||
"workflow_node.monitor.label": "监控网站证书",
|
||||
"workflow_node.monitor.default_name": "监控",
|
||||
"workflow_node.monitor.form.guide": "小贴士:Certimate 将向目标地址发送一个 HEAD 请求来获取相应的域名证书,请确保该地址可通过 HTTPS 协议访问。",
|
||||
"workflow_node.monitor.form.host.label": "主机地址",
|
||||
"workflow_node.monitor.form.host.placeholder": "请输入主机地址(可以是域名或 IP)",
|
||||
"workflow_node.monitor.form.port.label": "主机端口",
|
||||
"workflow_node.monitor.form.port.placeholder": "请输入主机端口",
|
||||
"workflow_node.monitor.form.domain.label": "域名(可选)",
|
||||
"workflow_node.monitor.form.domain.placeholder": "请输入域名(仅当主机地址为 IP 时可选)",
|
||||
"workflow_node.monitor.form.request_path.label": "请求路径(可选)",
|
||||
"workflow_node.monitor.form.request_path.placeholder": "请输入请求路径",
|
||||
|
||||
"workflow_node.notify.label": "推送通知",
|
||||
"workflow_node.notify.help": "调用服务提供商相关 API,将消息通知推送到指定的目标。",
|
||||
"workflow_node.notify.default_name": "通知",
|
||||
"workflow_node.notify.form.subject.label": "通知主题",
|
||||
"workflow_node.notify.form.subject.placeholder": "请输入通知主题",
|
||||
@@ -987,13 +991,11 @@
|
||||
"workflow_node.notify.form.skip_on_all_prev_skipped.switch.on": "跳过",
|
||||
"workflow_node.notify.form.skip_on_all_prev_skipped.switch.off": "不跳过",
|
||||
|
||||
"workflow_node.end.label": "结束",
|
||||
"workflow_node.end.default_name": "结束",
|
||||
|
||||
"workflow_node.branch.label": "并行/条件分支",
|
||||
"workflow_node.branch.default_name": "分支",
|
||||
|
||||
"workflow_node.condition.label": "分支",
|
||||
"workflow_node.condition.help": "当满足指定的条件时,进入相应分支。某一分支中的节点执行失败不影响平行分支继续执行。",
|
||||
"workflow_node.condition.default_name": "分支",
|
||||
"workflow_node.condition.default_name.template_certtest_on_expire_soon": "若网站证书即将到期 ...",
|
||||
"workflow_node.condition.default_name.template_certtest_on_expired": "若网站证书已到期 ...",
|
||||
@@ -1019,6 +1021,9 @@
|
||||
"workflow_node.condition.form.expression.value.option.false.label": "假",
|
||||
"workflow_node.condition.form.expression.add_condition.button": "添加条件",
|
||||
|
||||
"workflow_node.branch_block.label": "分支",
|
||||
"workflow_node.branch_block.default_name": "分支",
|
||||
|
||||
"workflow_node.execute_result_branch.label": "执行结果分支",
|
||||
"workflow_node.execute_result_branch.default_name": "分支",
|
||||
|
||||
@@ -1026,5 +1031,16 @@
|
||||
"workflow_node.execute_success.default_name": "执行成功",
|
||||
|
||||
"workflow_node.execute_failure.label": "若上一节点执行失败…",
|
||||
"workflow_node.execute_failure.default_name": "执行失败"
|
||||
"workflow_node.execute_failure.default_name": "执行失败",
|
||||
|
||||
"workflow_node.try_catch.label": "执行结果分支",
|
||||
"workflow_node.try_catch.help": "尝试执行后续节点,当任一节点执行失败后,中断并进入执行失败分支。",
|
||||
"workflow_node.try_catch.default_name": "尝试执行…",
|
||||
|
||||
"workflow_node.catch_block.label": "执行失败分支",
|
||||
"workflow_node.catch_block.default_name": "若执行失败…",
|
||||
|
||||
"workflow_node.end.label": "结束",
|
||||
"workflow_node.end.help": "中止工作流运行并退出。通常是一个工作流的最后一个节点。",
|
||||
"workflow_node.end.default_name": "结束"
|
||||
}
|
||||
|
||||
@@ -1,27 +1,27 @@
|
||||
{
|
||||
"workflow_run.action.view.button": "查看详情",
|
||||
"workflow_run.action.cancel.button": "取消执行",
|
||||
"workflow_run.action.cancel.modal.title": "取消执行",
|
||||
"workflow_run.action.cancel.modal.content": "确定要取消此执行吗?取消后仅会中止流程,但不会回滚已执行的节点。",
|
||||
"workflow_run.action.delete.button": "删除执行",
|
||||
"workflow_run.action.delete.modal.title": "删除执行「{{name}}」",
|
||||
"workflow_run.action.delete.modal.content": "确定要删除该工作流执行记录吗?删除后仅会清除日志历史,但不会影响签发的证书。<br>注意此操作不可撤销,请谨慎操作。",
|
||||
"workflow_run.action.cancel.button": "取消运行",
|
||||
"workflow_run.action.cancel.modal.title": "取消运行",
|
||||
"workflow_run.action.cancel.modal.content": "确定要取消此运行吗?取消后仅会中止流程,但不会回滚已执行的节点。",
|
||||
"workflow_run.action.delete.button": "删除运行历史",
|
||||
"workflow_run.action.delete.modal.title": "删除运行「{{name}}」",
|
||||
"workflow_run.action.delete.modal.content": "确定要删除该工作流运行历史吗?删除后仅会清除日志历史,但不会影响签发的证书。<br>注意此操作不可撤销,请谨慎操作。",
|
||||
|
||||
"workflow_run.deletion.alert": "执行记录中包含工作流各节点的执行结果,删除后可能导致因找不到前次执行结果而触发重新申请或部署证书。如无必要请勿提前删除,建议保留至少 180 天。",
|
||||
"workflow_run.cancellation.alert": "如遇进程意外中止、服务器超时等原因,你可以手动取消长时间挂起的执行,避免阻塞后续执行。",
|
||||
"workflow_run.deletion.alert": "运行历史中包含工作流各节点的运行结果,删除后可能导致因找不到前次运行结果而触发重新申请或部署证书。如无必要请勿提前删除,建议保留至少 180 天。",
|
||||
"workflow_run.cancellation.alert": "如遇进程意外中止、服务器超时等原因,你可以手动取消长时间挂起的运行,避免阻塞后续运行。",
|
||||
|
||||
"workflow_run.nodata.description": "当前未找到任何执行记录。请先执行此工作流。",
|
||||
"workflow_run.nodata.description": "当前未找到任何运行历史。请先运行此工作流。",
|
||||
|
||||
"workflow_run.props.id": "ID",
|
||||
"workflow_run.props.status": "状态",
|
||||
"workflow_run.props.status.pending": "等待执行",
|
||||
"workflow_run.props.status.running": "执行中",
|
||||
"workflow_run.props.status.pending": "等待运行",
|
||||
"workflow_run.props.status.running": "运行中",
|
||||
"workflow_run.props.status.succeeded": "已成功",
|
||||
"workflow_run.props.status.failed": "已失败",
|
||||
"workflow_run.props.status.canceled": "已取消",
|
||||
"workflow_run.props.trigger": "执行方式",
|
||||
"workflow_run.props.trigger.scheduled": "定时执行",
|
||||
"workflow_run.props.trigger.manual": "手动执行",
|
||||
"workflow_run.props.trigger": "触发方式",
|
||||
"workflow_run.props.trigger.scheduled": "定时",
|
||||
"workflow_run.props.trigger.manual": "手动",
|
||||
"workflow_run.props.started_at": "开始时间",
|
||||
"workflow_run.props.ended_at": "完成时间",
|
||||
|
||||
|
||||
@@ -8,8 +8,8 @@ import {
|
||||
IconHelpCircle,
|
||||
IconHierarchy3,
|
||||
IconHome,
|
||||
IconLogout,
|
||||
IconMenu2,
|
||||
IconPower,
|
||||
IconSettings,
|
||||
} from "@tabler/icons-react";
|
||||
import { Alert, Button, Drawer, Layout, Menu, type MenuProps, theme } from "antd";
|
||||
@@ -88,7 +88,7 @@ const ConsoleLayout = () => {
|
||||
danger: true,
|
||||
icon: (
|
||||
<span className="anticon scale-125" role="img">
|
||||
<IconLogout size="1em" />
|
||||
<IconPower size="1em" />
|
||||
</span>
|
||||
),
|
||||
label: t("common.menu.logout"),
|
||||
@@ -136,7 +136,7 @@ const ConsoleLayout = () => {
|
||||
<AppVersion.Badge>
|
||||
<Button icon={<IconBrandGithub size="1.25em" stroke="1.25" />} onClick={handleGitHubClick} />
|
||||
</AppVersion.Badge>
|
||||
<Button danger icon={<IconLogout size="1.25em" stroke="1.25" />} onClick={handleLogoutClick} />
|
||||
<Button danger icon={<IconPower size="1.25em" stroke="1.25" />} onClick={handleLogoutClick} />
|
||||
</div>
|
||||
</div>
|
||||
</Layout.Header>
|
||||
|
||||
@@ -143,9 +143,9 @@ const AccessList = () => {
|
||||
renderCell(checked, _, index, node) {
|
||||
if (!checked) {
|
||||
return (
|
||||
<div className="group">
|
||||
<div className="group-hover:hidden">{(page - 1) * pageSize + index + 1}</div>
|
||||
<div className="hidden group-hover:block">{node}</div>
|
||||
<div className="group/selection">
|
||||
<div className="group-hover/selection:hidden">{(page - 1) * pageSize + index + 1}</div>
|
||||
<div className="hidden group-hover/selection:block">{node}</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -190,9 +190,9 @@ const CertificateList = () => {
|
||||
renderCell(checked, _, index, node) {
|
||||
if (!checked) {
|
||||
return (
|
||||
<div className="group">
|
||||
<div className="group-hover:hidden">{(page - 1) * pageSize + index + 1}</div>
|
||||
<div className="hidden group-hover:block">{node}</div>
|
||||
<div className="group/selection">
|
||||
<div className="group-hover/selection:hidden">{(page - 1) * pageSize + index + 1}</div>
|
||||
<div className="hidden group-hover/selection:block">{node}</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -3,10 +3,11 @@ import { useTranslation } from "react-i18next";
|
||||
import { Outlet, useLocation, useNavigate, useParams } from "react-router-dom";
|
||||
import { IconEdit, IconHistory, IconPlayerPlay, IconRobot } from "@tabler/icons-react";
|
||||
import { useSize } from "ahooks";
|
||||
import { App, Button, Input, type InputRef, Segmented, Skeleton, Space } from "antd";
|
||||
import { App, Button, Input, type InputRef, Segmented, Skeleton } from "antd";
|
||||
|
||||
import { startRun as startWorkflowRun } from "@/api/workflows";
|
||||
import Show from "@/components/Show";
|
||||
import { isAllNodesValidated } from "@/domain/workflow";
|
||||
import { WORKFLOW_RUN_STATUSES } from "@/domain/workflowRun";
|
||||
import { useZustandShallowSelector } from "@/hooks";
|
||||
import { useWorkflowStore } from "@/stores/workflow";
|
||||
@@ -19,10 +20,10 @@ const WorkflowDetail = () => {
|
||||
|
||||
const { t } = useTranslation();
|
||||
|
||||
const { message, modal } = App.useApp();
|
||||
const { message, modal, notification } = App.useApp();
|
||||
|
||||
const { id: workflowId } = useParams();
|
||||
const { workflow, initialized, ...workflowState } = useWorkflowStore(useZustandShallowSelector(["workflow", "initialized", "init", "destroy"]));
|
||||
const { workflow, initialized, ...workflowState } = useWorkflowStore(useZustandShallowSelector(["workflow", "initialized", "init", "destroy", "setEnabled"]));
|
||||
useEffect(() => {
|
||||
workflowState.init(workflowId!);
|
||||
|
||||
@@ -35,6 +36,7 @@ const WorkflowDetail = () => {
|
||||
const divHeaderSize = useSize(divHeaderRef);
|
||||
|
||||
const tabs = [
|
||||
["design-legacy", "workflow.detail.design_legacy.tab", <IconRobot size="1em" />], // TODO: 正式发布时移除
|
||||
["design", "workflow.detail.design.tab", <IconRobot size="1em" />],
|
||||
["runs", "workflow.detail.runs.tab", <IconHistory size="1em" />],
|
||||
] satisfies [string, string, React.ReactElement][];
|
||||
@@ -90,6 +92,20 @@ const WorkflowDetail = () => {
|
||||
});
|
||||
};
|
||||
|
||||
const handleActiveClick = async () => {
|
||||
try {
|
||||
if (!workflow.enabled && (!workflow.content || !isAllNodesValidated(workflow.content))) {
|
||||
message.warning(t("workflow.action.enable.errmsg.uncompleted"));
|
||||
return;
|
||||
}
|
||||
|
||||
await workflowState.setEnabled(!workflow.enabled);
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
notification.error({ message: t("common.text.request_error"), description: getErrMsg(err) });
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="flex size-full flex-col">
|
||||
<div className="px-6 py-4" ref={divHeaderRef}>
|
||||
@@ -117,11 +133,14 @@ const WorkflowDetail = () => {
|
||||
</div>
|
||||
</div>
|
||||
<div className="py-2">
|
||||
<Space.Compact>
|
||||
<Button disabled={runButtonDisabled} icon={<IconPlayerPlay size="1.25em" />} loading={runButtonLoading} type="primary" onClick={handleRunClick}>
|
||||
{t("workflow.action.run.button")}
|
||||
</Button>
|
||||
</Space.Compact>
|
||||
<Show when={initialized}>
|
||||
<div className="flex items-center gap-2">
|
||||
<Button onClick={handleActiveClick}>{workflow.enabled ? t("workflow.action.disable.button") : t("workflow.action.enable.button")}</Button>
|
||||
<Button disabled={runButtonDisabled} icon={<IconPlayerPlay size="1.25em" />} loading={runButtonLoading} type="primary" onClick={handleRunClick}>
|
||||
{t("workflow.action.run.button")}
|
||||
</Button>
|
||||
</div>
|
||||
</Show>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -152,7 +171,7 @@ const WorkflowDetailBaseName = () => {
|
||||
|
||||
const { notification } = App.useApp();
|
||||
|
||||
const { workflow, setBaseInfo: setWorkflowBaseInfo } = useWorkflowStore(useZustandShallowSelector(["workflow", "setBaseInfo"]));
|
||||
const { workflow, initialized, setBaseInfo: setWorkflowBaseInfo } = useWorkflowStore(useZustandShallowSelector(["workflow", "initialized", "setBaseInfo"]));
|
||||
|
||||
const inputRef = useRef<InputRef>(null);
|
||||
|
||||
@@ -194,7 +213,7 @@ const WorkflowDetailBaseName = () => {
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="group relative flex items-center gap-1">
|
||||
<div className="group/input relative flex items-center gap-1">
|
||||
<h1
|
||||
className={mergeCls("break-all", {
|
||||
invisible: editing,
|
||||
@@ -202,14 +221,16 @@ const WorkflowDetailBaseName = () => {
|
||||
>
|
||||
{workflow.name || "\u00A0"}
|
||||
</h1>
|
||||
<Button
|
||||
className={mergeCls("mb-2 opacity-0 transition-opacity group-hover:opacity-100", {
|
||||
invisible: editing,
|
||||
})}
|
||||
icon={<IconEdit size="1.25em" stroke="1.25" />}
|
||||
type="text"
|
||||
onClick={handleEditClick}
|
||||
/>
|
||||
{initialized && (
|
||||
<Button
|
||||
className={mergeCls("mb-2 opacity-0 transition-opacity group-hover/input:opacity-100", {
|
||||
invisible: editing,
|
||||
})}
|
||||
icon={<IconEdit size="1.25em" stroke="1.25" />}
|
||||
type="text"
|
||||
onClick={handleEditClick}
|
||||
/>
|
||||
)}
|
||||
<Input
|
||||
className={mergeCls("absolute top-0 left-0", editing ? "block" : "hidden")}
|
||||
ref={inputRef}
|
||||
@@ -231,7 +252,7 @@ const WorkflowDetailBaseDescription = () => {
|
||||
|
||||
const { notification } = App.useApp();
|
||||
|
||||
const { workflow, setBaseInfo: setWorkflowBaseInfo } = useWorkflowStore(useZustandShallowSelector(["workflow", "setBaseInfo"]));
|
||||
const { workflow, initialized, setBaseInfo: setWorkflowBaseInfo } = useWorkflowStore(useZustandShallowSelector(["workflow", "initialized", "setBaseInfo"]));
|
||||
|
||||
const inputRef = useRef<InputRef>(null);
|
||||
|
||||
@@ -273,7 +294,7 @@ const WorkflowDetailBaseDescription = () => {
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="group relative flex items-center gap-1">
|
||||
<div className="group/input relative flex items-center gap-1">
|
||||
<p
|
||||
className={mergeCls("text-base text-gray-500", {
|
||||
invisible: editing,
|
||||
@@ -281,14 +302,16 @@ const WorkflowDetailBaseDescription = () => {
|
||||
>
|
||||
{workflow.description || "\u00A0"}
|
||||
</p>
|
||||
<Button
|
||||
className={mergeCls("mb-4 opacity-0 transition-opacity group-hover:opacity-100", {
|
||||
invisible: editing,
|
||||
})}
|
||||
icon={<IconEdit size="1.25em" stroke="1.25" />}
|
||||
type="text"
|
||||
onClick={handleEditClick}
|
||||
/>
|
||||
{initialized && (
|
||||
<Button
|
||||
className={mergeCls("mb-4 opacity-0 transition-opacity group-hover/input:opacity-100", {
|
||||
invisible: editing,
|
||||
})}
|
||||
icon={<IconEdit size="1.25em" stroke="1.25" />}
|
||||
type="text"
|
||||
onClick={handleEditClick}
|
||||
/>
|
||||
)}
|
||||
<Input
|
||||
className={mergeCls("absolute top-0 left-0", editing ? "block" : "hidden")}
|
||||
ref={inputRef}
|
||||
|
||||
@@ -1,12 +1,16 @@
|
||||
import { useEffect, useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { getI18n, useTranslation } from "react-i18next";
|
||||
import { type FlowDocumentJSON } from "@flowgram.ai/document";
|
||||
import { IconArrowBackUp, IconDots } from "@tabler/icons-react";
|
||||
import { Alert, App, Button, Card, Dropdown, Space } from "antd";
|
||||
import { useDeepCompareEffect } from "ahooks";
|
||||
import { Alert, App, Button, Card, Dropdown, Space, theme } from "antd";
|
||||
import { nanoid } from "nanoid";
|
||||
import { isEqual } from "radash";
|
||||
|
||||
import Show from "@/components/Show";
|
||||
import WorkflowElementsContainer from "@/components/workflow/WorkflowElementsContainer";
|
||||
import { isAllNodesValidated } from "@/domain/workflow";
|
||||
import WorkflowDesigner from "@/components/workflow/designer/Editor";
|
||||
import WorkflowDesignerToolbar from "@/components/workflow/designer/Toolbar";
|
||||
import { type WorkflowNode, WorkflowNodeType, isAllNodesValidated } from "@/domain/workflow";
|
||||
import { WORKFLOW_RUN_STATUSES } from "@/domain/workflowRun";
|
||||
import { useZustandShallowSelector } from "@/hooks";
|
||||
import { useWorkflowStore } from "@/stores/workflow";
|
||||
@@ -15,6 +19,7 @@ import { getErrMsg } from "@/utils/error";
|
||||
const WorkflowDetailDesign = () => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const { token: themeToken } = theme.useToken();
|
||||
const { message, modal, notification } = App.useApp();
|
||||
|
||||
const { workflow, ...workflowState } = useWorkflowStore(useZustandShallowSelector(["workflow", "init", "publish", "rollback"]));
|
||||
@@ -35,6 +40,11 @@ const WorkflowDetailDesign = () => {
|
||||
setAllowPublish(!isPendingOrRunning && hasChanges);
|
||||
}, [workflow.content, workflow.draft, workflow.hasDraft, isPendingOrRunning]);
|
||||
|
||||
const [editorData, setEditorData] = useState<FlowDocumentJSON>();
|
||||
useDeepCompareEffect(() => {
|
||||
setEditorData({ nodes: compactWorkflowDraft(workflow.draft) });
|
||||
}, [workflow.draft]);
|
||||
|
||||
const handleRollbackClick = () => {
|
||||
modal.confirm({
|
||||
title: t("workflow.action.rollback.modal.title"),
|
||||
@@ -75,7 +85,7 @@ const WorkflowDetailDesign = () => {
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="min-h-[360px] flex-1 overflow-hidden">
|
||||
<div className="size-full">
|
||||
<Card
|
||||
className="size-full overflow-hidden"
|
||||
styles={{
|
||||
@@ -86,43 +96,231 @@ const WorkflowDetailDesign = () => {
|
||||
},
|
||||
}}
|
||||
>
|
||||
<div className="size-full pt-9">
|
||||
<div className="absolute inset-x-6 z-2 mx-auto flex max-w-320 items-center justify-between gap-4">
|
||||
<div className="flex-1 overflow-hidden">
|
||||
<Show when={workflow.hasDraft!}>
|
||||
<Alert message={<div className="truncate">{t("workflow.detail.design.draft.alert")}</div>} showIcon type="warning" />
|
||||
</Show>
|
||||
</div>
|
||||
<div className="flex justify-end">
|
||||
<Space.Compact>
|
||||
<Button color="primary" disabled={!allowPublish} variant="outlined" onClick={handlePublishClick}>
|
||||
{t("workflow.action.publish.button")}
|
||||
</Button>
|
||||
<Dropdown
|
||||
menu={{
|
||||
items: [
|
||||
{
|
||||
key: "rollback",
|
||||
disabled: !allowRollback,
|
||||
label: t("workflow.action.rollback.button"),
|
||||
icon: <IconArrowBackUp size="1.25em" />,
|
||||
onClick: handleRollbackClick,
|
||||
},
|
||||
],
|
||||
}}
|
||||
trigger={["click"]}
|
||||
>
|
||||
<Button color="primary" icon={<IconDots size="1.25em" />} variant="outlined" />
|
||||
</Dropdown>
|
||||
</Space.Compact>
|
||||
<WorkflowDesigner initialData={editorData}>
|
||||
<div className="absolute top-8 z-10 w-full px-4">
|
||||
<div className="container">
|
||||
<div className="flex items-center justify-end gap-4">
|
||||
<div className="flex flex-1 items-center justify-end gap-4 overflow-hidden">
|
||||
<div className="flex-1 overflow-hidden">
|
||||
<Show when={workflow.hasDraft!}>
|
||||
<Alert message={<div className="truncate">{t("workflow.detail.design.unpublished_draft.alert")}</div>} showIcon type="warning" />
|
||||
</Show>
|
||||
</div>
|
||||
<Space.Compact>
|
||||
<Button disabled={!allowPublish} ghost type="primary" onClick={handlePublishClick}>
|
||||
{t("workflow.action.publish.button")}
|
||||
</Button>
|
||||
<Dropdown
|
||||
menu={{
|
||||
items: [
|
||||
{
|
||||
key: "rollback",
|
||||
disabled: !allowRollback,
|
||||
label: t("workflow.action.rollback.button"),
|
||||
icon: <IconArrowBackUp size="1.25em" />,
|
||||
onClick: handleRollbackClick,
|
||||
},
|
||||
],
|
||||
}}
|
||||
trigger={["click"]}
|
||||
>
|
||||
<Button icon={<IconDots size="1.25em" />} />
|
||||
</Dropdown>
|
||||
</Space.Compact>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<WorkflowElementsContainer className="pt-12" />
|
||||
</div>
|
||||
<div className="absolute bottom-8 z-10 w-full px-4">
|
||||
<div className="container">
|
||||
<div className="flex justify-end">
|
||||
<WorkflowDesignerToolbar
|
||||
style={{
|
||||
backgroundColor: themeToken.colorBgContainer,
|
||||
borderRadius: themeToken.borderRadius,
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</WorkflowDesigner>
|
||||
</Card>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const compactWorkflowDraft = (root: WorkflowNode | undefined) => {
|
||||
const { t } = getI18n();
|
||||
|
||||
// TODO: 仅为兼容适配 v0.3.x 数据,正式上线后待删除
|
||||
const res: FlowDocumentJSON["nodes"] = [];
|
||||
|
||||
if (!root) {
|
||||
res.push({
|
||||
id: nanoid(),
|
||||
type: "start",
|
||||
data: {
|
||||
name: "Start",
|
||||
},
|
||||
});
|
||||
} else {
|
||||
const convert = (node: WorkflowNode | undefined) => {
|
||||
const temp: typeof res = [];
|
||||
|
||||
let current: typeof node = node;
|
||||
while (current) {
|
||||
switch (current.type) {
|
||||
case WorkflowNodeType.Start:
|
||||
temp.push({
|
||||
id: current.id,
|
||||
type: "start",
|
||||
data: {
|
||||
name: current.name,
|
||||
config: current.config,
|
||||
},
|
||||
});
|
||||
break;
|
||||
|
||||
case WorkflowNodeType.Apply:
|
||||
temp.push({
|
||||
id: current.id,
|
||||
type: "bizApply",
|
||||
data: {
|
||||
name: current.name,
|
||||
config: current.config,
|
||||
},
|
||||
});
|
||||
break;
|
||||
|
||||
case WorkflowNodeType.Upload:
|
||||
temp.push({
|
||||
id: current.id,
|
||||
type: "bizUpload",
|
||||
data: {
|
||||
name: current.name,
|
||||
config: current.config,
|
||||
},
|
||||
});
|
||||
break;
|
||||
|
||||
case WorkflowNodeType.Monitor:
|
||||
temp.push({
|
||||
id: current.id,
|
||||
type: "bizMonitor",
|
||||
data: {
|
||||
name: current.name,
|
||||
config: current.config,
|
||||
},
|
||||
});
|
||||
break;
|
||||
|
||||
case WorkflowNodeType.Deploy:
|
||||
temp.push({
|
||||
id: current.id,
|
||||
type: "bizDeploy",
|
||||
data: {
|
||||
name: current.name,
|
||||
config: current.config,
|
||||
},
|
||||
});
|
||||
break;
|
||||
|
||||
case WorkflowNodeType.Notify:
|
||||
temp.push({
|
||||
id: current.id,
|
||||
type: "bizNotify",
|
||||
data: {
|
||||
name: current.name,
|
||||
config: current.config,
|
||||
},
|
||||
});
|
||||
break;
|
||||
|
||||
case WorkflowNodeType.ExecuteResultBranch: {
|
||||
const tryNode = temp.pop()!;
|
||||
temp.push({
|
||||
id: current.id,
|
||||
type: "tryCatch",
|
||||
blocks: [
|
||||
{
|
||||
id: current.branches?.find((b) => b.type === WorkflowNodeType.ExecuteSuccess)?.id || nanoid(),
|
||||
type: "tryBlock",
|
||||
blocks: [tryNode],
|
||||
data: {
|
||||
name: current.branches?.find((b) => b.type === WorkflowNodeType.ExecuteSuccess)?.name,
|
||||
},
|
||||
},
|
||||
{
|
||||
id: current.branches?.find((b) => b.type === WorkflowNodeType.ExecuteFailure)?.id || nanoid(),
|
||||
type: "catchBlock",
|
||||
blocks: [
|
||||
...convert(current.branches?.find((b) => b.type === WorkflowNodeType.ExecuteFailure)?.next),
|
||||
{
|
||||
id: nanoid(),
|
||||
type: "end",
|
||||
data: {
|
||||
name: t("workflow_node.end.default_name"),
|
||||
},
|
||||
},
|
||||
],
|
||||
data: {
|
||||
name: current.branches?.find((b) => b.type === WorkflowNodeType.ExecuteFailure)?.name,
|
||||
},
|
||||
},
|
||||
],
|
||||
data: {
|
||||
name: current.name,
|
||||
config: current.config,
|
||||
},
|
||||
});
|
||||
|
||||
current = current.branches?.find((b) => b.type === WorkflowNodeType.ExecuteSuccess);
|
||||
break;
|
||||
}
|
||||
|
||||
case WorkflowNodeType.Branch: {
|
||||
temp.push({
|
||||
id: current.id,
|
||||
type: "condition",
|
||||
blocks:
|
||||
current.branches?.map((branch) => {
|
||||
return {
|
||||
id: branch.id,
|
||||
type: "branchBlock",
|
||||
blocks: convert(branch.next),
|
||||
data: {
|
||||
name: branch.name,
|
||||
config: branch.config,
|
||||
},
|
||||
};
|
||||
}) ?? [],
|
||||
data: {
|
||||
name: current.name,
|
||||
config: current.config,
|
||||
},
|
||||
});
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
current = current?.next;
|
||||
}
|
||||
|
||||
return temp;
|
||||
};
|
||||
|
||||
res.push(...convert(root));
|
||||
}
|
||||
|
||||
res.push({
|
||||
id: nanoid(),
|
||||
type: "end",
|
||||
data: {
|
||||
name: t("workflow_node.end.default_name"),
|
||||
},
|
||||
});
|
||||
|
||||
return res;
|
||||
};
|
||||
|
||||
export default WorkflowDetailDesign;
|
||||
|
||||
@@ -0,0 +1,132 @@
|
||||
import { useEffect, useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { IconArrowBackUp, IconDots } from "@tabler/icons-react";
|
||||
import { Alert, App, Button, Card, Dropdown, Space } from "antd";
|
||||
import { isEqual } from "radash";
|
||||
|
||||
import Show from "@/components/Show";
|
||||
import WorkflowElementsContainer from "@/components/workflow/WorkflowElementsContainer";
|
||||
import { isAllNodesValidated } from "@/domain/workflow";
|
||||
import { WORKFLOW_RUN_STATUSES } from "@/domain/workflowRun";
|
||||
import { useZustandShallowSelector } from "@/hooks";
|
||||
import { useWorkflowStore } from "@/stores/workflow";
|
||||
import { getErrMsg } from "@/utils/error";
|
||||
|
||||
const WorkflowDetailDesign = () => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const { message, modal, notification } = App.useApp();
|
||||
|
||||
const { workflow, ...workflowState } = useWorkflowStore(useZustandShallowSelector(["workflow", "init", "publish", "rollback"]));
|
||||
|
||||
const [isPendingOrRunning, setIsPendingOrRunning] = useState(false);
|
||||
const [allowRollback, setAllowRollback] = useState(false);
|
||||
const [allowPublish, setAllowPublish] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
const pending = workflow.lastRunStatus === WORKFLOW_RUN_STATUSES.PENDING || workflow.lastRunStatus === WORKFLOW_RUN_STATUSES.RUNNING;
|
||||
setIsPendingOrRunning(pending);
|
||||
}, [workflow]);
|
||||
|
||||
useEffect(() => {
|
||||
const hasContent = !!workflow.content;
|
||||
const hasChanges = workflow.hasDraft! || !isEqual(workflow.draft, workflow.content);
|
||||
setAllowRollback(!isPendingOrRunning && hasContent && hasChanges);
|
||||
setAllowPublish(!isPendingOrRunning && hasChanges);
|
||||
}, [workflow.content, workflow.draft, workflow.hasDraft, isPendingOrRunning]);
|
||||
|
||||
const handleRollbackClick = () => {
|
||||
modal.confirm({
|
||||
title: t("workflow.action.rollback.modal.title"),
|
||||
content: t("workflow.action.rollback.modal.content"),
|
||||
onOk: async () => {
|
||||
try {
|
||||
await workflowState.rollback();
|
||||
|
||||
message.success(t("common.text.operation_succeeded"));
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
notification.error({ message: t("common.text.request_error"), description: getErrMsg(err) });
|
||||
}
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const handlePublishClick = () => {
|
||||
if (!isAllNodesValidated(workflow.draft!)) {
|
||||
message.warning(t("workflow.action.publish.errmsg.uncompleted"));
|
||||
return;
|
||||
}
|
||||
|
||||
modal.confirm({
|
||||
title: t("workflow.action.publish.modal.title"),
|
||||
content: t("workflow.action.publish.modal.content"),
|
||||
onOk: async () => {
|
||||
try {
|
||||
await workflowState.publish();
|
||||
|
||||
message.success(t("common.text.operation_succeeded"));
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
notification.error({ message: t("common.text.request_error"), description: getErrMsg(err) });
|
||||
}
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="min-h-[360px] flex-1 overflow-hidden">
|
||||
<Card
|
||||
className="size-full overflow-hidden"
|
||||
styles={{
|
||||
body: {
|
||||
position: "relative",
|
||||
height: "100%",
|
||||
padding: 0,
|
||||
},
|
||||
}}
|
||||
>
|
||||
<div className="size-full pt-9">
|
||||
<div className="absolute top-8 z-10 w-full px-4">
|
||||
<div className="container">
|
||||
<div className="flex items-center justify-end gap-4">
|
||||
<div className="flex flex-1 items-center justify-end gap-4 overflow-hidden">
|
||||
<div className="flex-1 overflow-hidden">
|
||||
<Show when={workflow.hasDraft!}>
|
||||
<Alert message={<div className="truncate">{t("workflow.detail.design.unpublished_draft.alert")}</div>} showIcon type="warning" />
|
||||
</Show>
|
||||
</div>
|
||||
<Space.Compact>
|
||||
<Button disabled={!allowPublish} type="primary" onClick={handlePublishClick}>
|
||||
{t("workflow.action.publish.button")}
|
||||
</Button>
|
||||
<Dropdown
|
||||
menu={{
|
||||
items: [
|
||||
{
|
||||
key: "rollback",
|
||||
disabled: !allowRollback,
|
||||
label: t("workflow.action.rollback.button"),
|
||||
icon: <IconArrowBackUp size="1.25em" />,
|
||||
onClick: handleRollbackClick,
|
||||
},
|
||||
],
|
||||
}}
|
||||
trigger={["click"]}
|
||||
>
|
||||
<Button icon={<IconDots size="1.25em" />} />
|
||||
</Dropdown>
|
||||
</Space.Compact>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<WorkflowElementsContainer className="pt-12" />
|
||||
</div>
|
||||
</Card>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default WorkflowDetailDesign;
|
||||
@@ -1,8 +1,8 @@
|
||||
import { useEffect, useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { IconBrowserShare, IconHistory, IconPlayerPause, IconTrash } from "@tabler/icons-react";
|
||||
import { IconBrowserShare, IconDotsVertical, IconHistory, IconPlayerPause, IconTrash } from "@tabler/icons-react";
|
||||
import { useRequest } from "ahooks";
|
||||
import { Alert, App, Button, Skeleton, Table, type TableProps, Tooltip } from "antd";
|
||||
import { Alert, App, Button, Dropdown, Skeleton, Table, type TableProps, Tooltip } from "antd";
|
||||
import dayjs from "dayjs";
|
||||
import { ClientResponseError } from "pocketbase";
|
||||
|
||||
@@ -98,53 +98,63 @@ const WorkflowDetailRuns = () => {
|
||||
key: "$action",
|
||||
align: "end",
|
||||
fixed: "right",
|
||||
width: 120,
|
||||
width: 64,
|
||||
render: (_, record) => {
|
||||
const allowCancel = record.status === WORKFLOW_RUN_STATUSES.PENDING || record.status === WORKFLOW_RUN_STATUSES.RUNNING;
|
||||
const aloowDelete =
|
||||
record.status === WORKFLOW_RUN_STATUSES.SUCCEEDED ||
|
||||
record.status === WORKFLOW_RUN_STATUSES.FAILED ||
|
||||
record.status === WORKFLOW_RUN_STATUSES.CANCELED;
|
||||
const cancelDisabled = !([WORKFLOW_RUN_STATUSES.PENDING, WORKFLOW_RUN_STATUSES.RUNNING] as string[]).includes(record.status);
|
||||
const deleteDisabled = !cancelDisabled;
|
||||
|
||||
return (
|
||||
<div className="flex items-center justify-end">
|
||||
<Tooltip title={t("workflow_run.action.view.button")}>
|
||||
<Button
|
||||
color="primary"
|
||||
icon={<IconBrowserShare size="1.25em" />}
|
||||
variant="text"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
handleRecordDetailClick(record);
|
||||
}}
|
||||
/>
|
||||
</Tooltip>
|
||||
<Tooltip title={t("workflow_run.action.cancel.button")}>
|
||||
<Button
|
||||
color="default"
|
||||
disabled={!allowCancel}
|
||||
icon={<IconPlayerPause size="1.25em" />}
|
||||
variant="text"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
handleRecordCancelClick(record);
|
||||
}}
|
||||
/>
|
||||
</Tooltip>
|
||||
<Tooltip title={t("workflow_run.action.delete.button")}>
|
||||
<Button
|
||||
color="danger"
|
||||
danger
|
||||
disabled={!aloowDelete}
|
||||
icon={<IconTrash size="1.25em" />}
|
||||
variant="text"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
handleRecordDeleteClick(record);
|
||||
}}
|
||||
/>
|
||||
</Tooltip>
|
||||
</div>
|
||||
<Dropdown
|
||||
menu={{
|
||||
items: [
|
||||
{
|
||||
key: "view",
|
||||
label: t("workflow_run.action.view.button"),
|
||||
icon: (
|
||||
<span className="anticon scale-125">
|
||||
<IconBrowserShare size="1em" />
|
||||
</span>
|
||||
),
|
||||
onClick: () => {
|
||||
handleRecordDetailClick(record);
|
||||
},
|
||||
},
|
||||
{
|
||||
key: "cancel",
|
||||
label: t("workflow_run.action.cancel.button"),
|
||||
icon: (
|
||||
<span className="anticon scale-125">
|
||||
<IconPlayerPause size="1em" />
|
||||
</span>
|
||||
),
|
||||
disabled: cancelDisabled,
|
||||
onClick: () => {
|
||||
handleRecordCancelClick(record);
|
||||
},
|
||||
},
|
||||
{
|
||||
type: "divider",
|
||||
},
|
||||
{
|
||||
key: "delete",
|
||||
label: t("workflow_run.action.delete.button"),
|
||||
icon: (
|
||||
<span className="anticon scale-125">
|
||||
<IconTrash size="1em" />
|
||||
</span>
|
||||
),
|
||||
danger: true,
|
||||
disabled: deleteDisabled,
|
||||
onClick: () => {
|
||||
handleRecordDeleteClick(record);
|
||||
},
|
||||
},
|
||||
],
|
||||
}}
|
||||
trigger={["click"]}
|
||||
>
|
||||
<Button icon={<IconDotsVertical size="1.25em" />} type="text" />
|
||||
</Dropdown>
|
||||
);
|
||||
},
|
||||
onCell: () => {
|
||||
|
||||
@@ -194,9 +194,9 @@ const WorkflowList = () => {
|
||||
renderCell(checked, _, index, node) {
|
||||
if (!checked) {
|
||||
return (
|
||||
<div className="group">
|
||||
<div className="group-hover:hidden">{(page - 1) * pageSize + index + 1}</div>
|
||||
<div className="hidden group-hover:block">{node}</div>
|
||||
<div className="group/selection">
|
||||
<div className="group-hover/selection:hidden">{(page - 1) * pageSize + index + 1}</div>
|
||||
<div className="hidden group-hover/selection:block">{node}</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@ import SettingsPersistence from "@/pages/settings/SettingsPersistence";
|
||||
import SettingsSSLProvider from "@/pages/settings/SettingsSSLProvider";
|
||||
import WorkflowDetail from "@/pages/workflows/WorkflowDetail";
|
||||
import WorkflowDetailDesign from "@/pages/workflows/WorkflowDetailDesign";
|
||||
import WorkflowDetailDesignLegacy from "@/pages/workflows/WorkflowDetailDesignLegacy";
|
||||
import WorkflowDetailRuns from "@/pages/workflows/WorkflowDetailRuns";
|
||||
import WorkflowList from "@/pages/workflows/WorkflowList";
|
||||
import WorkflowNew from "@/pages/workflows/WorkflowNew";
|
||||
@@ -56,6 +57,10 @@ export const router = createHashRouter([
|
||||
path: "/workflows/:id/design",
|
||||
element: <WorkflowDetailDesign />,
|
||||
},
|
||||
{
|
||||
path: "/workflows/:id/design-legacy",
|
||||
element: <WorkflowDetailDesignLegacy />,
|
||||
},
|
||||
{
|
||||
path: "/workflows/:id/runs",
|
||||
element: <WorkflowDetailRuns />,
|
||||
|
||||
Reference in New Issue
Block a user