Merge dev into shortcuts_settings
This commit is contained in:
commit
58cd138b4f
123 changed files with 2344 additions and 1022 deletions
|
|
@ -1,7 +1,24 @@
|
|||
{
|
||||
"extends": ["eslint:recommended", "plugin:node/recommended"],
|
||||
"extends": [
|
||||
"eslint:recommended",
|
||||
"plugin:react/recommended",
|
||||
"plugin:prettier/recommended"
|
||||
],
|
||||
"plugins": ["react", "import-helpers", "prettier"],
|
||||
"parser": "@typescript-eslint/parser",
|
||||
"parserOptions": {
|
||||
"ecmaVersion": 2018
|
||||
"project": ["./tsconfig.node.json", "./tsconfig.json"],
|
||||
"extraFileExtensions:": [".mdx"],
|
||||
"extensions:": [".mdx"]
|
||||
},
|
||||
"env": {
|
||||
"browser": true,
|
||||
"es2021": true
|
||||
},
|
||||
"settings": {
|
||||
"react": {
|
||||
"version": "detect"
|
||||
}
|
||||
},
|
||||
"rules": {
|
||||
"no-console": "warn",
|
||||
|
|
@ -16,6 +33,7 @@
|
|||
"no-redeclare": "error",
|
||||
"no-empty": "error",
|
||||
"no-await-in-loop": "error",
|
||||
"react/react-in-jsx-scope": 0,
|
||||
"node/exports-style": ["error", "module.exports"],
|
||||
"node/file-extension-in-import": ["error", "always"],
|
||||
"node/prefer-global/buffer": ["error", "always"],
|
||||
|
|
|
|||
283
src/frontend/package-lock.json
generated
283
src/frontend/package-lock.json
generated
|
|
@ -100,6 +100,7 @@
|
|||
"prettier-plugin-organize-imports": "^3.2.3",
|
||||
"prettier-plugin-tailwindcss": "^0.3.0",
|
||||
"pretty-quick": "^3.1.3",
|
||||
"simple-git-hooks": "^2.11.1",
|
||||
"tailwindcss": "^3.3.3",
|
||||
"tailwindcss-dotted-background": "^1.1.0",
|
||||
"typescript": "^5.2.2",
|
||||
|
|
@ -1008,6 +1009,27 @@
|
|||
"url": "https://github.com/chalk/ansi-regex?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/@isaacs/cliui/node_modules/emoji-regex": {
|
||||
"version": "9.2.2",
|
||||
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz",
|
||||
"integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg=="
|
||||
},
|
||||
"node_modules/@isaacs/cliui/node_modules/string-width": {
|
||||
"version": "5.1.2",
|
||||
"resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz",
|
||||
"integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==",
|
||||
"dependencies": {
|
||||
"eastasianwidth": "^0.2.0",
|
||||
"emoji-regex": "^9.2.2",
|
||||
"strip-ansi": "^7.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/@isaacs/cliui/node_modules/strip-ansi": {
|
||||
"version": "7.1.0",
|
||||
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz",
|
||||
|
|
@ -3531,9 +3553,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@tailwindcss/typography": {
|
||||
"version": "0.5.12",
|
||||
"resolved": "https://registry.npmjs.org/@tailwindcss/typography/-/typography-0.5.12.tgz",
|
||||
"integrity": "sha512-CNwpBpconcP7ppxmuq3qvaCxiRWnbhANpY/ruH4L5qs2GCiVDJXde/pjj2HWPV1+Q4G9+V/etrwUYopdcjAlyg==",
|
||||
"version": "0.5.13",
|
||||
"resolved": "https://registry.npmjs.org/@tailwindcss/typography/-/typography-0.5.13.tgz",
|
||||
"integrity": "sha512-ADGcJ8dX21dVVHIwTRgzrcunY6YY9uSlAHHGVKvkA+vLc5qLwEszvKts40lx7z0qc4clpjclwLeK5rVCV2P/uw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"lodash.castarray": "^4.4.0",
|
||||
|
|
@ -3926,6 +3948,20 @@
|
|||
"url": "https://github.com/sponsors/isaacs"
|
||||
}
|
||||
},
|
||||
"node_modules/@ts-morph/common/node_modules/mkdirp": {
|
||||
"version": "2.1.6",
|
||||
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-2.1.6.tgz",
|
||||
"integrity": "sha512-+hEnITedc8LAtIP9u3HJDFIdcLV2vXP33sqLLIzkv1Db1zO/1OxbvYf0Y1OC/S/Qo5dxHXepofhmxL02PsKe+A==",
|
||||
"bin": {
|
||||
"mkdirp": "dist/cjs/src/bin.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/isaacs"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/aria-query": {
|
||||
"version": "5.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.4.tgz",
|
||||
|
|
@ -5824,9 +5860,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/dompurify": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.1.0.tgz",
|
||||
"integrity": "sha512-yoU4rhgPKCo+p5UrWWWNKiIq+ToGqmVVhk0PmMYBK4kRsR3/qhemNFL8f6CFmBd4gMwm3F4T7HBoydP5uY07fA=="
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.1.1.tgz",
|
||||
"integrity": "sha512-tVP8C/GJwnABOn/7cx/ymx/hXpmBfWIPihC1aOEvS8GbMqy3pgeYtJk1HXN3CO7tu+8bpY18f6isjR5Cymj0TQ=="
|
||||
},
|
||||
"node_modules/dot-case": {
|
||||
"version": "3.0.4",
|
||||
|
|
@ -5854,14 +5890,14 @@
|
|||
"integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA=="
|
||||
},
|
||||
"node_modules/electron-to-chromium": {
|
||||
"version": "1.4.749",
|
||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.749.tgz",
|
||||
"integrity": "sha512-LRMMrM9ITOvue0PoBrvNIraVmuDbJV5QC9ierz/z5VilMdPOVMjOtpICNld3PuXuTZ3CHH/UPxX9gHhAPwi+0Q=="
|
||||
"version": "1.4.750",
|
||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.750.tgz",
|
||||
"integrity": "sha512-9ItEpeu15hW5m8jKdriL+BQrgwDTXEL9pn4SkillWFu73ZNNNQ2BKKLS+ZHv2vC9UkNhosAeyfxOf/5OSeTCPA=="
|
||||
},
|
||||
"node_modules/emoji-regex": {
|
||||
"version": "9.2.2",
|
||||
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz",
|
||||
"integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg=="
|
||||
"version": "8.0.0",
|
||||
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
|
||||
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="
|
||||
},
|
||||
"node_modules/end-of-stream": {
|
||||
"version": "1.4.4",
|
||||
|
|
@ -6938,26 +6974,6 @@
|
|||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/gauge/node_modules/emoji-regex": {
|
||||
"version": "8.0.0",
|
||||
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
|
||||
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
|
||||
"optional": true
|
||||
},
|
||||
"node_modules/gauge/node_modules/string-width": {
|
||||
"version": "4.2.3",
|
||||
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
|
||||
"integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"emoji-regex": "^8.0.0",
|
||||
"is-fullwidth-code-point": "^3.0.0",
|
||||
"strip-ansi": "^6.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/gensync": {
|
||||
"version": "1.0.0-beta.2",
|
||||
"resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz",
|
||||
|
|
@ -9472,11 +9488,11 @@
|
|||
}
|
||||
},
|
||||
"node_modules/minipass": {
|
||||
"version": "7.0.4",
|
||||
"resolved": "https://registry.npmjs.org/minipass/-/minipass-7.0.4.tgz",
|
||||
"integrity": "sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ==",
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz",
|
||||
"integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==",
|
||||
"engines": {
|
||||
"node": ">=16 || 14 >=14.17"
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/minizlib": {
|
||||
|
|
@ -9516,17 +9532,15 @@
|
|||
"integrity": "sha512-7NO5s6n10TIV96d4g2uDpG7ZDpIhMh0QNfGdJw/W47JswFcosz457wqz/b5sAKvl12sxINGFCn80NZHKwxQEXA=="
|
||||
},
|
||||
"node_modules/mkdirp": {
|
||||
"version": "2.1.6",
|
||||
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-2.1.6.tgz",
|
||||
"integrity": "sha512-+hEnITedc8LAtIP9u3HJDFIdcLV2vXP33sqLLIzkv1Db1zO/1OxbvYf0Y1OC/S/Qo5dxHXepofhmxL02PsKe+A==",
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz",
|
||||
"integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==",
|
||||
"optional": true,
|
||||
"bin": {
|
||||
"mkdirp": "dist/cjs/src/bin.js"
|
||||
"mkdirp": "bin/cmd.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/isaacs"
|
||||
}
|
||||
},
|
||||
"node_modules/moment": {
|
||||
|
|
@ -10794,9 +10808,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/react": {
|
||||
"version": "18.3.0",
|
||||
"resolved": "https://registry.npmjs.org/react/-/react-18.3.0.tgz",
|
||||
"integrity": "sha512-RPutkJftSAldDibyrjuku7q11d3oy6wKOyPe5K1HA/HwwrXcEqBdHsLypkC2FFYjP7bPUa6gbzSBhw4sY2JcDg==",
|
||||
"version": "18.3.1",
|
||||
"resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz",
|
||||
"integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==",
|
||||
"dependencies": {
|
||||
"loose-envify": "^1.1.0"
|
||||
},
|
||||
|
|
@ -10834,15 +10848,15 @@
|
|||
}
|
||||
},
|
||||
"node_modules/react-dom": {
|
||||
"version": "18.3.0",
|
||||
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.0.tgz",
|
||||
"integrity": "sha512-zaKdLBftQJnvb7FtDIpZtsAIb2MZU087RM8bRDZU8LVCCFYjPTsDZJNFUWPcVz3HFSN1n/caxi0ca4B/aaVQGQ==",
|
||||
"version": "18.3.1",
|
||||
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz",
|
||||
"integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==",
|
||||
"dependencies": {
|
||||
"loose-envify": "^1.1.0",
|
||||
"scheduler": "^0.23.1"
|
||||
"scheduler": "^0.23.2"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^18.3.0"
|
||||
"react": "^18.3.1"
|
||||
}
|
||||
},
|
||||
"node_modules/react-draggable": {
|
||||
|
|
@ -10935,9 +10949,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/react-markdown/node_modules/react-is": {
|
||||
"version": "18.3.0",
|
||||
"resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.0.tgz",
|
||||
"integrity": "sha512-wRiUsea88TjKDc4FBEn+sLvIDesp6brMbGWnJGjew2waAc9evdhja/2LvePc898HJbHw0L+MTWy7NhpnELAvLQ=="
|
||||
"version": "18.3.1",
|
||||
"resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz",
|
||||
"integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg=="
|
||||
},
|
||||
"node_modules/react-pdf": {
|
||||
"version": "7.7.1",
|
||||
|
|
@ -10977,18 +10991,18 @@
|
|||
}
|
||||
},
|
||||
"node_modules/react-reconciler": {
|
||||
"version": "0.29.1",
|
||||
"resolved": "https://registry.npmjs.org/react-reconciler/-/react-reconciler-0.29.1.tgz",
|
||||
"integrity": "sha512-xyrmljHU7F5cn1jngF+qPlbSZwstjSOW2BYwkv2LTIv37/BOCJ1XW7Z4iyMxofRJexBueKkhmlX8G2Y01hQx9w==",
|
||||
"version": "0.29.2",
|
||||
"resolved": "https://registry.npmjs.org/react-reconciler/-/react-reconciler-0.29.2.tgz",
|
||||
"integrity": "sha512-zZQqIiYgDCTP/f1N/mAR10nJGrPD2ZR+jDSEsKWJHYC7Cm2wodlwbR3upZRdC3cjIjSlTLNVyO7Iu0Yy7t2AYg==",
|
||||
"dependencies": {
|
||||
"loose-envify": "^1.1.0",
|
||||
"scheduler": "^0.23.1"
|
||||
"scheduler": "^0.23.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^18.3.0"
|
||||
"react": "^18.3.1"
|
||||
}
|
||||
},
|
||||
"node_modules/react-remove-scroll": {
|
||||
|
|
@ -11499,9 +11513,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/scheduler": {
|
||||
"version": "0.23.1",
|
||||
"resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.1.tgz",
|
||||
"integrity": "sha512-5GKS5JGfiah1O38Vfa9srZE4s3wdHbwjlCrvIookrg2FO9aIwKLOJXuJQFlEfNcVSOXuaL2hzDeY20uVXcUtrw==",
|
||||
"version": "0.23.2",
|
||||
"resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz",
|
||||
"integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==",
|
||||
"dependencies": {
|
||||
"loose-envify": "^1.1.0"
|
||||
}
|
||||
|
|
@ -11914,6 +11928,16 @@
|
|||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/simple-git-hooks": {
|
||||
"version": "2.11.1",
|
||||
"resolved": "https://registry.npmjs.org/simple-git-hooks/-/simple-git-hooks-2.11.1.tgz",
|
||||
"integrity": "sha512-tgqwPUMDcNDhuf1Xf6KTUsyeqGdgKMhzaH4PAZZuzguOgTl5uuyeYe/8mWgAr6IBxB5V06uqEf6Dy37gIWDtDg==",
|
||||
"dev": true,
|
||||
"hasInstallScript": true,
|
||||
"bin": {
|
||||
"simple-git-hooks": "cli.js"
|
||||
}
|
||||
},
|
||||
"node_modules/sisteransi": {
|
||||
"version": "1.0.5",
|
||||
"resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz",
|
||||
|
|
@ -12043,19 +12067,16 @@
|
|||
}
|
||||
},
|
||||
"node_modules/string-width": {
|
||||
"version": "5.1.2",
|
||||
"resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz",
|
||||
"integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==",
|
||||
"version": "4.2.3",
|
||||
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
|
||||
"integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
|
||||
"dependencies": {
|
||||
"eastasianwidth": "^0.2.0",
|
||||
"emoji-regex": "^9.2.2",
|
||||
"strip-ansi": "^7.0.1"
|
||||
"emoji-regex": "^8.0.0",
|
||||
"is-fullwidth-code-point": "^3.0.0",
|
||||
"strip-ansi": "^6.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/string-width-cjs": {
|
||||
|
|
@ -12072,36 +12093,6 @@
|
|||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/string-width-cjs/node_modules/emoji-regex": {
|
||||
"version": "8.0.0",
|
||||
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
|
||||
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="
|
||||
},
|
||||
"node_modules/string-width/node_modules/ansi-regex": {
|
||||
"version": "6.0.1",
|
||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz",
|
||||
"integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/chalk/ansi-regex?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/string-width/node_modules/strip-ansi": {
|
||||
"version": "7.1.0",
|
||||
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz",
|
||||
"integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==",
|
||||
"dependencies": {
|
||||
"ansi-regex": "^6.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/chalk/strip-ansi?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/strip-ansi": {
|
||||
"version": "6.0.1",
|
||||
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
|
||||
|
|
@ -12262,6 +12253,14 @@
|
|||
"url": "https://github.com/sponsors/isaacs"
|
||||
}
|
||||
},
|
||||
"node_modules/sucrase/node_modules/minipass": {
|
||||
"version": "7.0.4",
|
||||
"resolved": "https://registry.npmjs.org/minipass/-/minipass-7.0.4.tgz",
|
||||
"integrity": "sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ==",
|
||||
"engines": {
|
||||
"node": ">=16 || 14 >=14.17"
|
||||
}
|
||||
},
|
||||
"node_modules/supports-color": {
|
||||
"version": "5.5.0",
|
||||
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
|
||||
|
|
@ -12385,27 +12384,6 @@
|
|||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/tar/node_modules/minipass": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz",
|
||||
"integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==",
|
||||
"optional": true,
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/tar/node_modules/mkdirp": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz",
|
||||
"integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==",
|
||||
"optional": true,
|
||||
"bin": {
|
||||
"mkdirp": "bin/cmd.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/tar/node_modules/yallist": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
|
||||
|
|
@ -13577,26 +13555,6 @@
|
|||
"string-width": "^1.0.2 || 2 || 3 || 4"
|
||||
}
|
||||
},
|
||||
"node_modules/wide-align/node_modules/emoji-regex": {
|
||||
"version": "8.0.0",
|
||||
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
|
||||
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
|
||||
"optional": true
|
||||
},
|
||||
"node_modules/wide-align/node_modules/string-width": {
|
||||
"version": "4.2.3",
|
||||
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
|
||||
"integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"emoji-regex": "^8.0.0",
|
||||
"is-fullwidth-code-point": "^3.0.0",
|
||||
"strip-ansi": "^6.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/wrap-ansi": {
|
||||
"version": "8.1.0",
|
||||
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz",
|
||||
|
|
@ -13660,24 +13618,6 @@
|
|||
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
|
||||
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
|
||||
},
|
||||
"node_modules/wrap-ansi-cjs/node_modules/emoji-regex": {
|
||||
"version": "8.0.0",
|
||||
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
|
||||
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="
|
||||
},
|
||||
"node_modules/wrap-ansi-cjs/node_modules/string-width": {
|
||||
"version": "4.2.3",
|
||||
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
|
||||
"integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
|
||||
"dependencies": {
|
||||
"emoji-regex": "^8.0.0",
|
||||
"is-fullwidth-code-point": "^3.0.0",
|
||||
"strip-ansi": "^6.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/wrap-ansi/node_modules/ansi-regex": {
|
||||
"version": "6.0.1",
|
||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz",
|
||||
|
|
@ -13700,6 +13640,27 @@
|
|||
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/wrap-ansi/node_modules/emoji-regex": {
|
||||
"version": "9.2.2",
|
||||
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz",
|
||||
"integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg=="
|
||||
},
|
||||
"node_modules/wrap-ansi/node_modules/string-width": {
|
||||
"version": "5.1.2",
|
||||
"resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz",
|
||||
"integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==",
|
||||
"dependencies": {
|
||||
"eastasianwidth": "^0.2.0",
|
||||
"emoji-regex": "^9.2.2",
|
||||
"strip-ansi": "^7.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/wrap-ansi/node_modules/strip-ansi": {
|
||||
"version": "7.1.0",
|
||||
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz",
|
||||
|
|
|
|||
|
|
@ -79,6 +79,9 @@
|
|||
"format": "npx prettier --write \"./**/*.{js,jsx,ts,tsx,json,md}\" --ignore-path .prettierignore",
|
||||
"type-check": "tsc --noEmit --pretty --project tsconfig.json && vite"
|
||||
},
|
||||
"simple-git-hooks": {
|
||||
"pre-commit": "npx pretty-quick --staged"
|
||||
},
|
||||
"eslintConfig": {
|
||||
"extends": [
|
||||
"react-app",
|
||||
|
|
@ -122,10 +125,11 @@
|
|||
"prettier-plugin-organize-imports": "^3.2.3",
|
||||
"prettier-plugin-tailwindcss": "^0.3.0",
|
||||
"pretty-quick": "^3.1.3",
|
||||
"simple-git-hooks": "^2.11.1",
|
||||
"tailwindcss": "^3.3.3",
|
||||
"tailwindcss-dotted-background": "^1.1.0",
|
||||
"typescript": "^5.2.2",
|
||||
"ua-parser-js": "^1.0.37",
|
||||
"vite": "^4.5.2"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -96,6 +96,18 @@ body {
|
|||
}
|
||||
|
||||
.custom-hover:hover {
|
||||
background-color: rgba(99, 102, 241, 0.1); /* Medium indigo color with 20% opacity */
|
||||
background-color: rgba(
|
||||
99,
|
||||
102,
|
||||
241,
|
||||
0.1
|
||||
); /* Medium indigo color with 20% opacity */
|
||||
}
|
||||
|
||||
.json-view-playground .json-view {
|
||||
background-color: #fff !important;
|
||||
}
|
||||
|
||||
.json-view-flow .json-view {
|
||||
background-color: #bbb !important;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import "./App.css";
|
|||
import ErrorAlert from "./alerts/error";
|
||||
import NoticeAlert from "./alerts/notice";
|
||||
import SuccessAlert from "./alerts/success";
|
||||
import CrashErrorComponent from "./components/CrashErrorComponent";
|
||||
import CrashErrorComponent from "./components/crashErrorComponent";
|
||||
import FetchErrorComponent from "./components/fetchErrorComponent";
|
||||
import LoadingComponent from "./components/loadingComponent";
|
||||
import {
|
||||
|
|
|
|||
|
|
@ -97,6 +97,7 @@ export default function AddNewVariableButton({ children }): JSX.Element {
|
|||
password={false}
|
||||
options={["Generic", "Credential"]}
|
||||
placeholder="Choose a type for the variable..."
|
||||
id={"type-global-variables"}
|
||||
></InputComponent>
|
||||
<Label>Value</Label>
|
||||
<Textarea
|
||||
|
|
@ -114,6 +115,7 @@ export default function AddNewVariableButton({ children }): JSX.Element {
|
|||
password={false}
|
||||
options={availableFields}
|
||||
placeholder="Choose a field for the variable..."
|
||||
id={"apply-to-fields"}
|
||||
></InputComponent>
|
||||
</div>
|
||||
</BaseModal.Content>
|
||||
|
|
|
|||
|
|
@ -1,14 +1,16 @@
|
|||
import { useEffect, useState } from "react";
|
||||
import { getComponent, postLikeComponent } from "../../controllers/API";
|
||||
import DeleteConfirmationModal from "../../modals/DeleteConfirmationModal";
|
||||
import IOModal from "../../modals/IOModal";
|
||||
import DeleteConfirmationModal from "../../modals/deleteConfirmationModal";
|
||||
import useAlertStore from "../../stores/alertStore";
|
||||
import useFlowStore from "../../stores/flowStore";
|
||||
import useFlowsManagerStore from "../../stores/flowsManagerStore";
|
||||
import { useStoreStore } from "../../stores/storeStore";
|
||||
import { storeComponent } from "../../types/store";
|
||||
import cloneFLowWithParent from "../../utils/storeUtils";
|
||||
import { cn } from "../../utils/utils";
|
||||
import ShadTooltip from "../ShadTooltipComponent";
|
||||
import { cn, convertTestName } from "../../utils/utils";
|
||||
import IconComponent from "../genericIconComponent";
|
||||
import ShadTooltip from "../shadTooltipComponent";
|
||||
import { Badge } from "../ui/badge";
|
||||
import { Button } from "../ui/button";
|
||||
import {
|
||||
|
|
@ -18,37 +20,74 @@ import {
|
|||
CardHeader,
|
||||
CardTitle,
|
||||
} from "../ui/card";
|
||||
import Loading from "../ui/loading";
|
||||
|
||||
export default function CollectionCardComponent({
|
||||
data,
|
||||
authorized = true,
|
||||
disabled = false,
|
||||
button,
|
||||
onClick,
|
||||
onDelete,
|
||||
playground,
|
||||
}: {
|
||||
data: storeComponent;
|
||||
authorized?: boolean;
|
||||
disabled?: boolean;
|
||||
onClick?: () => void;
|
||||
button?: JSX.Element;
|
||||
playground?: boolean;
|
||||
onDelete?: () => void;
|
||||
}) {
|
||||
const addFlow = useFlowsManagerStore((state) => state.addFlow);
|
||||
const setSuccessData = useAlertStore((state) => state.setSuccessData);
|
||||
const setErrorData = useAlertStore((state) => state.setErrorData);
|
||||
const setValidApiKey = useStoreStore((state) => state.updateValidApiKey);
|
||||
const cleanFlowPool = useFlowStore((state) => state.CleanFlowPool);
|
||||
const isStore = false;
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [loadingLike, setLoadingLike] = useState(false);
|
||||
const [liked_by_user, setLiked_by_user] = useState(
|
||||
data?.liked_by_user ?? false
|
||||
data?.liked_by_user ?? false,
|
||||
);
|
||||
const [likes_count, setLikes_count] = useState(data?.liked_by_count ?? 0);
|
||||
const [downloads_count, setDownloads_count] = useState(
|
||||
data?.downloads_count ?? 0
|
||||
data?.downloads_count ?? 0,
|
||||
);
|
||||
const currentFlow = useFlowsManagerStore((state) => state.currentFlow);
|
||||
const setCurrentFlow = useFlowsManagerStore((state) => state.setCurrentFlow);
|
||||
const getFlowById = useFlowsManagerStore((state) => state.getFlowById);
|
||||
const currentFlowId = useFlowsManagerStore((state) => state.currentFlowId);
|
||||
const setNodes = useFlowStore((state) => state.setNodes);
|
||||
const setEdges = useFlowStore((state) => state.setEdges);
|
||||
const [openPlayground, setOpenPlayground] = useState(false);
|
||||
const [openDelete, setOpenDelete] = useState(false);
|
||||
const setCurrentFlowId = useFlowsManagerStore(
|
||||
(state) => state.setCurrentFlowId,
|
||||
);
|
||||
const [loadingPlayground, setLoadingPlayground] = useState(false);
|
||||
|
||||
const name = data.is_component ? "Component" : "Flow";
|
||||
|
||||
async function getFlowData() {
|
||||
const res = await getComponent(data.id);
|
||||
const newFlow = cloneFLowWithParent(res, res.id, data.is_component, true);
|
||||
return newFlow;
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (currentFlowId && playground) {
|
||||
if (openPlayground) {
|
||||
setNodes(currentFlow?.data?.nodes ?? [], true);
|
||||
setEdges(currentFlow?.data?.edges ?? [], true);
|
||||
} else {
|
||||
setNodes([], true);
|
||||
setEdges([], true);
|
||||
}
|
||||
cleanFlowPool();
|
||||
}
|
||||
}, [openPlayground]);
|
||||
|
||||
useEffect(() => {
|
||||
if (data) {
|
||||
setLiked_by_user(data?.liked_by_user ?? false);
|
||||
|
|
@ -128,226 +167,337 @@ export default function CollectionCardComponent({
|
|||
}
|
||||
|
||||
return (
|
||||
<Card
|
||||
className={cn(
|
||||
"group relative flex min-h-[11rem] flex-col justify-between overflow-hidden transition-all hover:shadow-md",
|
||||
disabled ? "pointer-events-none opacity-50" : ""
|
||||
)}
|
||||
>
|
||||
<div>
|
||||
<CardHeader>
|
||||
<div>
|
||||
<CardTitle className="flex w-full items-center justify-between gap-3 text-xl">
|
||||
<IconComponent
|
||||
className={cn(
|
||||
"flex-shrink-0",
|
||||
data.is_component
|
||||
? "mx-0.5 h-6 w-6 text-component-icon"
|
||||
: "h-7 w-7 flex-shrink-0 text-flow-icon"
|
||||
)}
|
||||
name={data.is_component ? "ToyBrick" : "Group"}
|
||||
/>
|
||||
<ShadTooltip content={data.name}>
|
||||
<div className="w-full truncate">{data.name}</div>
|
||||
</ShadTooltip>
|
||||
{data?.metadata !== undefined && (
|
||||
<div className="flex gap-3">
|
||||
{data.private && (
|
||||
<ShadTooltip content="Private">
|
||||
<span className="flex items-center gap-1.5 text-xs text-muted-foreground">
|
||||
<IconComponent name="Lock" className="h-4 w-4" />
|
||||
</span>
|
||||
</ShadTooltip>
|
||||
<>
|
||||
<Card
|
||||
data-testid={`card-${convertTestName(data.name)}`}
|
||||
className={cn(
|
||||
"group relative flex min-h-[11rem] flex-col justify-between overflow-hidden transition-all hover:shadow-md",
|
||||
disabled ? "pointer-events-none opacity-50" : "",
|
||||
onClick ? "cursor-pointer" : "",
|
||||
)}
|
||||
onClick={onClick}
|
||||
>
|
||||
<div>
|
||||
<CardHeader>
|
||||
<div>
|
||||
<CardTitle className="flex w-full items-center justify-between gap-3 text-xl">
|
||||
<IconComponent
|
||||
className={cn(
|
||||
"flex-shrink-0",
|
||||
data.is_component
|
||||
? "mx-0.5 h-6 w-6 text-component-icon"
|
||||
: "h-7 w-7 flex-shrink-0 text-flow-icon",
|
||||
)}
|
||||
{!data.is_component && (
|
||||
<ShadTooltip content="Components">
|
||||
name={data.is_component ? "ToyBrick" : "Group"}
|
||||
/>
|
||||
<ShadTooltip content={data.name}>
|
||||
<div className="w-full truncate">{data.name}</div>
|
||||
</ShadTooltip>
|
||||
{data?.metadata !== undefined && (
|
||||
<div className="flex gap-3">
|
||||
{data.private && (
|
||||
<ShadTooltip content="Private">
|
||||
<span className="flex items-center gap-1.5 text-xs text-muted-foreground">
|
||||
<IconComponent name="Lock" className="h-4 w-4" />
|
||||
</span>
|
||||
</ShadTooltip>
|
||||
)}
|
||||
{!data.is_component && (
|
||||
<ShadTooltip content="Components">
|
||||
<span className="flex items-center gap-1.5 text-xs text-muted-foreground">
|
||||
<IconComponent name="ToyBrick" className="h-4 w-4" />
|
||||
<span data-testid={`total-${data.name}`}>
|
||||
{data?.metadata?.total ?? 0}
|
||||
</span>
|
||||
</span>
|
||||
</ShadTooltip>
|
||||
)}
|
||||
<ShadTooltip content="Likes">
|
||||
<span className="flex items-center gap-1.5 text-xs text-muted-foreground">
|
||||
<IconComponent name="ToyBrick" className="h-4 w-4" />
|
||||
<span data-testid={`total-${data.name}`}>
|
||||
{data?.metadata?.total ?? 0}
|
||||
<IconComponent
|
||||
name="Heart"
|
||||
className={cn("h-4 w-4 ")}
|
||||
/>
|
||||
<span data-testid={`likes-${data.name}`}>
|
||||
{likes_count ?? 0}
|
||||
</span>
|
||||
</span>
|
||||
</ShadTooltip>
|
||||
)}
|
||||
<ShadTooltip content="Likes">
|
||||
<span className="flex items-center gap-1.5 text-xs text-muted-foreground">
|
||||
<IconComponent name="Heart" className={cn("h-4 w-4 ")} />
|
||||
<span data-testid={`likes-${data.name}`}>
|
||||
{likes_count ?? 0}
|
||||
<ShadTooltip content="Downloads">
|
||||
<span className="flex items-center gap-1.5 text-xs text-muted-foreground">
|
||||
<IconComponent
|
||||
name="DownloadCloud"
|
||||
className="h-4 w-4"
|
||||
/>
|
||||
<span data-testid={`downloads-${data.name}`}>
|
||||
{downloads_count ?? 0}
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
</ShadTooltip>
|
||||
<ShadTooltip content="Downloads">
|
||||
<span className="flex items-center gap-1.5 text-xs text-muted-foreground">
|
||||
<IconComponent name="DownloadCloud" className="h-4 w-4" />
|
||||
<span data-testid={`downloads-${data.name}`}>
|
||||
{downloads_count ?? 0}
|
||||
</span>
|
||||
</span>
|
||||
</ShadTooltip>
|
||||
</div>
|
||||
)}
|
||||
</ShadTooltip>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{onDelete && data?.metadata === undefined && (
|
||||
<DeleteConfirmationModal
|
||||
onConfirm={() => {
|
||||
onDelete();
|
||||
{onDelete && data?.metadata === undefined && (
|
||||
<button
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
setOpenDelete(true);
|
||||
}}
|
||||
>
|
||||
<IconComponent
|
||||
name="Trash2"
|
||||
className="h-5 w-5 text-primary opacity-0 transition-all hover:text-destructive group-hover:opacity-100"
|
||||
/>
|
||||
</button>
|
||||
)}
|
||||
</CardTitle>
|
||||
</div>
|
||||
<div className="flex gap-2">
|
||||
{data.user_created && data.user_created.username && (
|
||||
<span className="text-sm text-primary">
|
||||
by <b>{data.user_created.username}</b>
|
||||
{data.last_tested_version && (
|
||||
<>
|
||||
{" "}
|
||||
|{" "}
|
||||
<span className="text-xs">
|
||||
{" "}
|
||||
⛓︎ v{data.last_tested_version}
|
||||
</span>
|
||||
</>
|
||||
)}
|
||||
</span>
|
||||
)}
|
||||
<div className="flex w-full flex-1 flex-wrap gap-2">
|
||||
{data.tags &&
|
||||
data.tags.length > 0 &&
|
||||
data.tags.map((tag, index) => (
|
||||
<Badge
|
||||
key={index}
|
||||
variant="outline"
|
||||
size="xq"
|
||||
className="text-muted-foreground"
|
||||
>
|
||||
{tag.name}
|
||||
</Badge>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<CardDescription className="pb-2 pt-2">
|
||||
<div className="truncate-doubleline">{data.description}</div>
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
</div>
|
||||
|
||||
<CardFooter>
|
||||
<div className="flex w-full items-center justify-between gap-2">
|
||||
<div className="flex w-full flex-wrap items-end justify-between gap-2">
|
||||
{playground && data?.metadata !== undefined ? (
|
||||
<Button
|
||||
disabled={loadingPlayground}
|
||||
key={data.id}
|
||||
tabIndex={-1}
|
||||
variant="outline"
|
||||
size="sm"
|
||||
className="gap-2 whitespace-nowrap"
|
||||
data-testid={"playground-flow-button-" + data.id}
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
setLoadingPlayground(true);
|
||||
if (getFlowById(data.id)) {
|
||||
setCurrentFlowId(data.id);
|
||||
setOpenPlayground(true);
|
||||
setLoadingPlayground(false);
|
||||
} else {
|
||||
getFlowData().then((res) => {
|
||||
setCurrentFlow(res);
|
||||
setOpenPlayground(true);
|
||||
setLoadingPlayground(false);
|
||||
});
|
||||
}
|
||||
}}
|
||||
>
|
||||
<IconComponent
|
||||
name="Trash2"
|
||||
className="h-5 w-5 text-primary opacity-0 transition-all hover:text-destructive group-hover:opacity-100"
|
||||
/>
|
||||
</DeleteConfirmationModal>
|
||||
)}
|
||||
</CardTitle>
|
||||
</div>
|
||||
{data.user_created && data.user_created.username && (
|
||||
<span className="text-sm text-primary">
|
||||
by <b>{data.user_created.username}</b>
|
||||
{data.last_tested_version && (
|
||||
<>
|
||||
{" "}
|
||||
|{" "}
|
||||
<span className="text-xs">
|
||||
{" "}
|
||||
⛓︎ v{data.last_tested_version}
|
||||
</span>
|
||||
</>
|
||||
)}
|
||||
</span>
|
||||
)}
|
||||
|
||||
<CardDescription className="pb-2 pt-2">
|
||||
<div className="truncate-doubleline">{data.description}</div>
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
</div>
|
||||
|
||||
<CardFooter>
|
||||
<div className="flex w-full items-center justify-between gap-2">
|
||||
<div className="flex w-full flex-wrap items-end justify-between gap-2">
|
||||
<div className="flex w-full flex-1 flex-wrap gap-2">
|
||||
{data.tags &&
|
||||
data.tags.length > 0 &&
|
||||
data.tags.map((tag, index) => (
|
||||
<Badge
|
||||
key={index}
|
||||
variant="outline"
|
||||
size="xq"
|
||||
className="text-muted-foreground"
|
||||
>
|
||||
{tag.name}
|
||||
</Badge>
|
||||
))}
|
||||
</div>
|
||||
{data.liked_by_count != undefined && (
|
||||
<div className="flex gap-0.5">
|
||||
{onDelete && data?.metadata !== undefined ? (
|
||||
<ShadTooltip
|
||||
content={
|
||||
authorized ? "Delete" : "Please review your API key."
|
||||
}
|
||||
>
|
||||
<DeleteConfirmationModal
|
||||
onConfirm={() => {
|
||||
onDelete();
|
||||
}}
|
||||
{!loadingPlayground ? (
|
||||
<IconComponent
|
||||
name="BotMessageSquareIcon"
|
||||
className="h-4 w-4 select-none"
|
||||
/>
|
||||
) : (
|
||||
<Loading className="h-4 w-4 text-medium-indigo" />
|
||||
)}
|
||||
Playground
|
||||
</Button>
|
||||
) : undefined}
|
||||
{data.liked_by_count != undefined && (
|
||||
<div className="flex gap-0.5">
|
||||
{onDelete && data?.metadata !== undefined ? (
|
||||
<ShadTooltip
|
||||
content={
|
||||
authorized ? "Delete" : "Please review your API key."
|
||||
}
|
||||
>
|
||||
<DeleteConfirmationModal
|
||||
onConfirm={() => {
|
||||
onDelete();
|
||||
}}
|
||||
>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
className={
|
||||
"whitespace-nowrap" +
|
||||
(!authorized ? " cursor-not-allowed" : "")
|
||||
}
|
||||
>
|
||||
<IconComponent
|
||||
data-testid={`delete-${convertTestName(data.name)}`}
|
||||
name="Trash2"
|
||||
className={cn(
|
||||
"h-5 w-5",
|
||||
!authorized ? " text-ring" : "",
|
||||
)}
|
||||
/>
|
||||
</Button>
|
||||
</DeleteConfirmationModal>
|
||||
</ShadTooltip>
|
||||
) : (
|
||||
<ShadTooltip
|
||||
content={
|
||||
authorized ? "Like" : "Please review your API key."
|
||||
}
|
||||
>
|
||||
<Button
|
||||
disabled={loadingLike}
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
className={
|
||||
"whitespace-nowrap" +
|
||||
(!authorized ? " cursor-not-allowed" : "")
|
||||
}
|
||||
onClick={() => {
|
||||
if (!authorized) {
|
||||
return;
|
||||
}
|
||||
handleLike();
|
||||
}}
|
||||
data-testid={`like-${data.name}`}
|
||||
>
|
||||
<IconComponent
|
||||
name="Trash2"
|
||||
name="Heart"
|
||||
className={cn(
|
||||
"h-5 w-5",
|
||||
!authorized ? " text-ring" : ""
|
||||
liked_by_user
|
||||
? "fill-destructive stroke-destructive"
|
||||
: "",
|
||||
!authorized ? " text-ring" : "",
|
||||
)}
|
||||
/>
|
||||
</Button>
|
||||
</DeleteConfirmationModal>
|
||||
</ShadTooltip>
|
||||
) : (
|
||||
</ShadTooltip>
|
||||
)}
|
||||
<ShadTooltip
|
||||
content={
|
||||
authorized ? "Like" : "Please review your API key."
|
||||
authorized
|
||||
? isStore
|
||||
? "Download"
|
||||
: "Install Locally"
|
||||
: "Please review your API key."
|
||||
}
|
||||
>
|
||||
<Button
|
||||
disabled={loadingLike}
|
||||
disabled={loading}
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
className={
|
||||
"whitespace-nowrap" +
|
||||
(!authorized ? " cursor-not-allowed" : "")
|
||||
(!authorized ? " cursor-not-allowed" : "") +
|
||||
(!loading ? " p-0.5" : "")
|
||||
}
|
||||
onClick={() => {
|
||||
if (!authorized) {
|
||||
if (loading || !authorized) {
|
||||
return;
|
||||
}
|
||||
handleLike();
|
||||
handleInstall();
|
||||
}}
|
||||
data-testid={`like-${data.name}`}
|
||||
data-testid={`install-${data.name}`}
|
||||
>
|
||||
<IconComponent
|
||||
name="Heart"
|
||||
name={
|
||||
loading ? "Loader2" : isStore ? "Download" : "Plus"
|
||||
}
|
||||
className={cn(
|
||||
"h-5 w-5",
|
||||
liked_by_user
|
||||
? "fill-destructive stroke-destructive"
|
||||
: "",
|
||||
!authorized ? " text-ring" : ""
|
||||
loading ? "h-5 w-5 animate-spin" : "h-5 w-5",
|
||||
!authorized ? " text-ring" : "",
|
||||
)}
|
||||
/>
|
||||
</Button>
|
||||
</ShadTooltip>
|
||||
)}
|
||||
<ShadTooltip
|
||||
content={
|
||||
authorized
|
||||
? isStore
|
||||
? "Download"
|
||||
: "Install Locally"
|
||||
: "Please review your API key."
|
||||
}
|
||||
>
|
||||
<Button
|
||||
disabled={loading}
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
className={
|
||||
"whitespace-nowrap" +
|
||||
(!authorized ? " cursor-not-allowed" : "") +
|
||||
(!loading ? " p-0.5" : "")
|
||||
</div>
|
||||
)}
|
||||
{button && button}
|
||||
{playground && data?.metadata === undefined && (
|
||||
<Button
|
||||
disabled={loadingPlayground}
|
||||
key={data.id}
|
||||
tabIndex={-1}
|
||||
variant="outline"
|
||||
size="sm"
|
||||
className="gap-2 whitespace-nowrap"
|
||||
data-testid={"playground-flow-button-" + data.id}
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
setLoadingPlayground(true);
|
||||
if (getFlowById(data.id)) {
|
||||
setCurrentFlowId(data.id);
|
||||
setOpenPlayground(true);
|
||||
setLoadingPlayground(false);
|
||||
} else {
|
||||
getFlowData().then((res) => {
|
||||
setCurrentFlow(res);
|
||||
setOpenPlayground(true);
|
||||
setLoadingPlayground(false);
|
||||
});
|
||||
}
|
||||
onClick={() => {
|
||||
if (loading || !authorized) {
|
||||
return;
|
||||
}
|
||||
handleInstall();
|
||||
}}
|
||||
data-testid={`install-${data.name}`}
|
||||
>
|
||||
}}
|
||||
>
|
||||
{!loadingPlayground ? (
|
||||
<IconComponent
|
||||
name={loading ? "Loader2" : isStore ? "Download" : "Plus"}
|
||||
className={cn(
|
||||
loading ? "h-5 w-5 animate-spin" : "h-5 w-5",
|
||||
!authorized ? " text-ring" : ""
|
||||
)}
|
||||
name="BotMessageSquareIcon"
|
||||
className="h-4 w-4 select-none"
|
||||
/>
|
||||
</Button>
|
||||
</ShadTooltip>
|
||||
</div>
|
||||
)}
|
||||
{button && button}
|
||||
) : (
|
||||
<Loading className="h-4 w-4 text-medium-indigo" />
|
||||
)}
|
||||
Playground
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</CardFooter>
|
||||
</Card>
|
||||
</CardFooter>
|
||||
</Card>
|
||||
{openPlayground && (
|
||||
<IOModal
|
||||
cleanOnClose={true}
|
||||
open={openPlayground}
|
||||
setOpen={setOpenPlayground}
|
||||
>
|
||||
<></>
|
||||
</IOModal>
|
||||
)}
|
||||
{openDelete && (
|
||||
<DeleteConfirmationModal
|
||||
open={openDelete}
|
||||
setOpen={setOpenDelete}
|
||||
onConfirm={() => {
|
||||
if (onDelete) onDelete();
|
||||
}}
|
||||
>
|
||||
<></>
|
||||
</DeleteConfirmationModal>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
import { Transition } from "@headlessui/react";
|
||||
import { useMemo, useRef, useState } from "react";
|
||||
import { useHotkeys } from "react-hotkeys-hook";
|
||||
import ApiModal from "../../modals/ApiModal";
|
||||
import { useEffect, useMemo, useRef, useState } from "react";
|
||||
import IOModal from "../../modals/IOModal";
|
||||
import ApiModal from "../../modals/apiModal";
|
||||
import ShareModal from "../../modals/shareModal";
|
||||
import useFlowStore from "../../stores/flowStore";
|
||||
import useFlowsManagerStore from "../../stores/flowsManagerStore";
|
||||
|
|
@ -91,15 +91,15 @@ export default function FlowToolbar(): JSX.Element {
|
|||
}
|
||||
>
|
||||
<div className="flex">
|
||||
<div className="flex h-full w-full gap-1 rounded-sm text-medium-indigo transition-all">
|
||||
<div className="flex h-full w-full gap-1 rounded-sm transition-all">
|
||||
{hasIO ? (
|
||||
<IOModal open={open} setOpen={setOpen} disable={!hasIO}>
|
||||
<div className="relative inline-flex w-full items-center justify-center gap-1 px-5 py-3 text-sm font-semibold text-medium-indigo transition-all transition-all duration-500 ease-in-out ease-in-out hover:bg-hover">
|
||||
<div className="relative inline-flex w-full items-center justify-center gap-1 px-5 py-3 text-sm font-semibold transition-all duration-500 ease-in-out hover:bg-hover">
|
||||
<ForwardedIconComponent
|
||||
name="Zap"
|
||||
className={"message-button-icon h-5 w-5 transition-all"}
|
||||
name="BotMessageSquareIcon"
|
||||
className={" h-5 w-5 transition-all"}
|
||||
/>
|
||||
Run
|
||||
Playground
|
||||
</div>
|
||||
</IOModal>
|
||||
) : (
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@ import { cloneDeep } from "lodash";
|
|||
import { useEffect, useState } from "react";
|
||||
import { Prism as SyntaxHighlighter } from "react-syntax-highlighter";
|
||||
import { oneDark } from "react-syntax-highlighter/dist/cjs/styles/prism";
|
||||
import AccordionComponent from "../../components/AccordionComponent";
|
||||
import CodeAreaComponent from "../../components/codeAreaComponent";
|
||||
import Dropdown from "../../components/dropdownComponent";
|
||||
import FloatComponent from "../../components/floatComponent";
|
||||
|
|
@ -36,11 +35,12 @@ import {
|
|||
hasDuplicateKeys,
|
||||
} from "../../utils/reactflowUtils";
|
||||
import { classNames } from "../../utils/utils";
|
||||
import ShadTooltip from "../ShadTooltipComponent";
|
||||
import AccordionComponent from "../accordionComponent";
|
||||
import DictComponent from "../dictComponent";
|
||||
import IconComponent from "../genericIconComponent";
|
||||
import KeypairListComponent from "../keypairListComponent";
|
||||
import InputComponent from "../inputComponent";
|
||||
import KeypairListComponent from "../keypairListComponent";
|
||||
import ShadTooltip from "../shadTooltipComponent";
|
||||
|
||||
export default function CodeTabsComponent({
|
||||
flow,
|
||||
|
|
@ -267,7 +267,7 @@ export default function CodeTabsComponent({
|
|||
<div className="mx-auto">
|
||||
{node.data.node.template[
|
||||
templateField
|
||||
].list ? (
|
||||
]?.list ? (
|
||||
<InputListComponent
|
||||
componentName={
|
||||
templateField
|
||||
|
|
@ -733,7 +733,7 @@ export default function CodeTabsComponent({
|
|||
isList={
|
||||
node.data.node!.template[
|
||||
templateField
|
||||
].list ?? false
|
||||
]?.list ?? false
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -3,8 +3,8 @@ import useFlowsManagerStore from "../../stores/flowsManagerStore";
|
|||
import { FlowType } from "../../types/flow";
|
||||
import { updateIds } from "../../utils/reactflowUtils";
|
||||
import { cn } from "../../utils/utils";
|
||||
import ShadTooltip from "../ShadTooltipComponent";
|
||||
import IconComponent from "../genericIconComponent";
|
||||
import ShadTooltip from "../shadTooltipComponent";
|
||||
import { Button } from "../ui/button";
|
||||
import {
|
||||
Card,
|
||||
|
|
|
|||
|
|
@ -17,8 +17,8 @@ import useAlertStore from "../../../../stores/alertStore";
|
|||
import useFlowStore from "../../../../stores/flowStore";
|
||||
import useFlowsManagerStore from "../../../../stores/flowsManagerStore";
|
||||
import { cn } from "../../../../utils/utils";
|
||||
import ShadTooltip from "../../../ShadTooltipComponent";
|
||||
import IconComponent from "../../../genericIconComponent";
|
||||
import ShadTooltip from "../../../shadTooltipComponent";
|
||||
import { Button } from "../../../ui/button";
|
||||
|
||||
export const MenuBar = ({
|
||||
|
|
|
|||
|
|
@ -174,6 +174,7 @@ export default function Header(): JSX.Element {
|
|||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<button
|
||||
data-testid="user-profile-settings"
|
||||
className={
|
||||
"h-7 w-7 rounded-full focus-visible:outline-0 " +
|
||||
(userData?.profile_image ??
|
||||
|
|
|
|||
|
|
@ -247,6 +247,7 @@ export default function InputComponent({
|
|||
</PopoverContentWithoutPortal>
|
||||
</Popover>
|
||||
<div
|
||||
data-testid={"popover-anchor-" + id}
|
||||
className={cn(
|
||||
"pointer-events-auto absolute inset-y-0 h-full w-full cursor-pointer",
|
||||
((selectedOption !== "" || !onChange) && setSelectedOption) ||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { useEffect } from "react";
|
||||
import { deleteGlobalVariable } from "../../controllers/API";
|
||||
import DeleteConfirmationModal from "../../modals/DeleteConfirmationModal";
|
||||
import DeleteConfirmationModal from "../../modals/deleteConfirmationModal";
|
||||
import useAlertStore from "../../stores/alertStore";
|
||||
import { useGlobalVariablesStore } from "../../stores/globalVariables";
|
||||
import { InputGlobalComponentType } from "../../types/components";
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ export default function InputListComponent({
|
|||
disabled,
|
||||
editNode = false,
|
||||
componentName,
|
||||
playgroundDisabled,
|
||||
}: InputListComponentType): JSX.Element {
|
||||
useEffect(() => {
|
||||
if (disabled && value.length > 0 && value[0] !== "") {
|
||||
|
|
@ -24,7 +25,7 @@ export default function InputListComponent({
|
|||
value = [value];
|
||||
}
|
||||
|
||||
if (!value.length) value = [""];
|
||||
if (!value?.length) value = [""];
|
||||
|
||||
return (
|
||||
<div
|
||||
|
|
@ -37,7 +38,7 @@ export default function InputListComponent({
|
|||
return (
|
||||
<div key={idx} className="flex w-full gap-3">
|
||||
<Input
|
||||
disabled={disabled}
|
||||
disabled={disabled || playgroundDisabled}
|
||||
type="text"
|
||||
value={singleValue}
|
||||
className={editNode ? "input-edit-node" : ""}
|
||||
|
|
@ -64,6 +65,7 @@ export default function InputListComponent({
|
|||
editNode ? "-edit" : ""
|
||||
}_${componentName}-` + idx
|
||||
}
|
||||
disabled={disabled || playgroundDisabled}
|
||||
>
|
||||
<IconComponent
|
||||
name="Plus"
|
||||
|
|
@ -82,10 +84,15 @@ export default function InputListComponent({
|
|||
newInputList.splice(idx, 1);
|
||||
onChange(newInputList);
|
||||
}}
|
||||
disabled={disabled || playgroundDisabled}
|
||||
>
|
||||
<IconComponent
|
||||
name="X"
|
||||
className="h-4 w-4 hover:text-status-red"
|
||||
className={`h-4 w-4 ${
|
||||
disabled || playgroundDisabled
|
||||
? ""
|
||||
: "hover:text-accent-foreground"
|
||||
}`}
|
||||
/>
|
||||
</button>
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -20,7 +20,13 @@ export default function KeypairListComponent({
|
|||
}
|
||||
}, [disabled]);
|
||||
|
||||
const ref = useRef(value.length === 0 ? [{ "": "" }] : value);
|
||||
const checkValueType = (value) => {
|
||||
return Array.isArray(value) ? value : [value];
|
||||
};
|
||||
|
||||
const ref = useRef<any>([]);
|
||||
ref.current =
|
||||
!value || value?.length === 0 ? [{ "": "" }] : checkValueType(value);
|
||||
|
||||
useEffect(() => {
|
||||
if (JSON.stringify(value) !== JSON.stringify(ref.current)) {
|
||||
|
|
|
|||
|
|
@ -711,13 +711,22 @@ export const LANGFLOW_SUPPORTED_TYPES = new Set([
|
|||
|
||||
export const priorityFields = new Set(["code", "template"]);
|
||||
|
||||
export const INPUT_TYPES = new Set(["ChatInput", "TextInput", "KeyPairInput"]);
|
||||
export const INPUT_TYPES = new Set([
|
||||
"ChatInput",
|
||||
"TextInput",
|
||||
"KeyPairInput",
|
||||
"JsonInput",
|
||||
"StringListInput",
|
||||
]);
|
||||
export const OUTPUT_TYPES = new Set([
|
||||
"ChatOutput",
|
||||
"TextOutput",
|
||||
"PDFOutput",
|
||||
"ImageOutput",
|
||||
"CSVOutput",
|
||||
"JsonOutput",
|
||||
"KeyPairOutput",
|
||||
"StringListOutput",
|
||||
]);
|
||||
|
||||
export const CHAT_FIRST_INITIAL_TEXT =
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { AxiosResponse } from "axios";
|
||||
import { ReactFlowJsonObject } from "reactflow";
|
||||
import { AxiosRequestConfig, AxiosResponse } from "axios";
|
||||
import { Edge, ReactFlowJsonObject,Node } from "reactflow";
|
||||
import { BASE_URL_API } from "../../constants/constants";
|
||||
import { api } from "../../controllers/API/api";
|
||||
import {
|
||||
|
|
@ -928,17 +928,26 @@ export async function updateGlobalVariable(
|
|||
export async function getVerticesOrder(
|
||||
flowId: string,
|
||||
startNodeId?: string | null,
|
||||
stopNodeId?: string | null
|
||||
stopNodeId?: string | null,
|
||||
nodes?:Node[],
|
||||
Edges?:Edge[]
|
||||
): Promise<AxiosResponse<VerticesOrderTypeAPI>> {
|
||||
// nodeId is optional and is a query parameter
|
||||
// if nodeId is not provided, the API will return all vertices
|
||||
const config = {};
|
||||
const config:AxiosRequestConfig<any> = {};
|
||||
if (stopNodeId) {
|
||||
config["params"] = { stop_component_id: stopNodeId };
|
||||
} else if (startNodeId) {
|
||||
config["params"] = { start_component_id: startNodeId };
|
||||
}
|
||||
return await api.get(`${BASE_URL_API}build/${flowId}/vertices`, config);
|
||||
const data = {
|
||||
data:{}
|
||||
}
|
||||
if(nodes && Edges){
|
||||
data["data"]["nodes"] = nodes
|
||||
data["data"]["edges"] = Edges
|
||||
}
|
||||
return await api.post(`${BASE_URL_API}build/${flowId}/vertices`,data, config);
|
||||
}
|
||||
|
||||
export async function postBuildVertex(
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
import { cloneDeep } from "lodash";
|
||||
import React, { ReactNode, useEffect, useRef, useState } from "react";
|
||||
import { Handle, Position, useUpdateNodeInternals } from "reactflow";
|
||||
import ShadTooltip from "../../../../components/ShadTooltipComponent";
|
||||
import CodeAreaComponent from "../../../../components/codeAreaComponent";
|
||||
import DictComponent from "../../../../components/dictComponent";
|
||||
import Dropdown from "../../../../components/dropdownComponent";
|
||||
|
|
@ -13,6 +12,7 @@ import InputListComponent from "../../../../components/inputListComponent";
|
|||
import IntComponent from "../../../../components/intComponent";
|
||||
import KeypairListComponent from "../../../../components/keypairListComponent";
|
||||
import PromptAreaComponent from "../../../../components/promptComponent";
|
||||
import ShadTooltip from "../../../../components/shadTooltipComponent";
|
||||
import TextAreaComponent from "../../../../components/textAreaComponent";
|
||||
import ToggleShadComponent from "../../../../components/toggleShadComponent";
|
||||
import { Button } from "../../../../components/ui/button";
|
||||
|
|
@ -618,7 +618,7 @@ export default function ParameterComponent({
|
|||
<FloatComponent
|
||||
disabled={disabled}
|
||||
value={data.node?.template[name].value ?? ""}
|
||||
rangeSpec={data.node?.template[name].rangeSpec}
|
||||
rangeSpec={data.node?.template[name]?.rangeSpec}
|
||||
onChange={handleOnNewValue}
|
||||
/>
|
||||
</div>
|
||||
|
|
@ -1,9 +1,9 @@
|
|||
import { cloneDeep } from "lodash";
|
||||
import { useCallback, useEffect, useMemo, useState } from "react";
|
||||
import { NodeToolbar, useUpdateNodeInternals } from "reactflow";
|
||||
import ShadTooltip from "../../components/ShadTooltipComponent";
|
||||
import IconComponent from "../../components/genericIconComponent";
|
||||
import InputComponent from "../../components/inputComponent";
|
||||
import ShadTooltip from "../../components/shadTooltipComponent";
|
||||
import { Button } from "../../components/ui/button";
|
||||
import Checkmark from "../../components/ui/checkmark";
|
||||
import Loading from "../../components/ui/loading";
|
||||
|
|
@ -661,7 +661,11 @@ export default function GenericNode({
|
|||
variant="secondary"
|
||||
className={"group h-9 px-1.5"}
|
||||
>
|
||||
<div>
|
||||
<div
|
||||
data-testid={
|
||||
`button_run_` + data?.node?.display_name.toLowerCase()
|
||||
}
|
||||
>
|
||||
<div className="generic-node-status-position flex items-center justify-center">
|
||||
{renderIconStatus(buildStatus, validationStatus)}
|
||||
</div>
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
import { useEffect, useRef } from "react";
|
||||
import JsonView from "react18-json-view";
|
||||
import { useDarkStore } from "../../../../../../stores/darkStore";
|
||||
import { DictComponentType } from "../../../../../../types/components";
|
||||
|
||||
export default function IoJsonInput({
|
||||
value = [],
|
||||
onChange,
|
||||
left,
|
||||
output,
|
||||
}: DictComponentType): JSX.Element {
|
||||
useEffect(() => {
|
||||
if (value) onChange(value);
|
||||
}, [value]);
|
||||
const isDark = useDarkStore((state) => state.dark);
|
||||
|
||||
const ref = useRef<any>(null);
|
||||
ref.current = value;
|
||||
|
||||
const getClassNames = () => {
|
||||
if (!isDark && !left) return "json-view-playground-white";
|
||||
if (!isDark && left) return "json-view-playground-white-left";
|
||||
if (isDark && left) return "json-view-playground-dark-left";
|
||||
if (isDark && !left) return "json-view-playground-dark";
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="w-full">
|
||||
<JsonView
|
||||
className={getClassNames()}
|
||||
theme="vscode"
|
||||
dark={isDark}
|
||||
editable={!output}
|
||||
enableClipboard
|
||||
onEdit={(edit) => {
|
||||
ref.current = edit["src"];
|
||||
}}
|
||||
onChange={(edit) => {
|
||||
ref.current = edit["src"];
|
||||
}}
|
||||
src={ref.current}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
@ -0,0 +1,107 @@
|
|||
import _ from "lodash";
|
||||
import { useRef } from "react";
|
||||
import IconComponent from "../../../../../../components/genericIconComponent";
|
||||
import { Input } from "../../../../../../components/ui/input";
|
||||
import { classNames } from "../../../../../../utils/utils";
|
||||
|
||||
export type IOKeyPairInputProps = {
|
||||
value: any;
|
||||
onChange: (value: any) => void;
|
||||
duplicateKey: boolean;
|
||||
isList: boolean;
|
||||
isInputField?: boolean;
|
||||
};
|
||||
|
||||
const IOKeyPairInput = ({
|
||||
value,
|
||||
onChange,
|
||||
duplicateKey,
|
||||
isList = true,
|
||||
isInputField,
|
||||
}: IOKeyPairInputProps) => {
|
||||
const checkValueType = (value) => {
|
||||
return Array.isArray(value) ? value : [value];
|
||||
};
|
||||
|
||||
const ref = useRef<any>([]);
|
||||
ref.current =
|
||||
!value || value?.length === 0 ? [{ "": "" }] : checkValueType(value);
|
||||
|
||||
const handleChangeKey = (event, idx) => {
|
||||
const oldKey = Object.keys(ref.current[idx])[0];
|
||||
const updatedObj = { [event.target.value]: ref.current[idx][oldKey] };
|
||||
ref.current[idx] = updatedObj;
|
||||
onChange(ref.current);
|
||||
};
|
||||
|
||||
const handleChangeValue = (newValue, idx) => {
|
||||
const key = Object.keys(ref.current[idx])[0];
|
||||
ref.current[idx][key] = newValue;
|
||||
onChange(ref.current);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className={classNames("flex h-full flex-col gap-3")}>
|
||||
{ref.current?.map((obj, index) => {
|
||||
return Object.keys(obj).map((key, idx) => {
|
||||
return (
|
||||
<div key={idx} className="flex w-full gap-2">
|
||||
<Input
|
||||
type="text"
|
||||
value={key.trim()}
|
||||
className={classNames(duplicateKey ? "input-invalid" : "")}
|
||||
placeholder="Type key..."
|
||||
onChange={(event) => handleChangeKey(event, index)}
|
||||
disabled={!isInputField}
|
||||
/>
|
||||
|
||||
<Input
|
||||
type="text"
|
||||
value={obj[key]}
|
||||
placeholder="Type a value..."
|
||||
onChange={(event) =>
|
||||
handleChangeValue(event.target.value, index)
|
||||
}
|
||||
disabled={!isInputField}
|
||||
/>
|
||||
|
||||
{isList && isInputField && index === ref.current.length - 1 ? (
|
||||
<button
|
||||
onClick={() => {
|
||||
let newInputList = _.cloneDeep(ref.current);
|
||||
newInputList.push({ "": "" });
|
||||
onChange(newInputList);
|
||||
}}
|
||||
>
|
||||
<IconComponent
|
||||
name="Plus"
|
||||
className={"h-4 w-4 hover:text-accent-foreground"}
|
||||
/>
|
||||
</button>
|
||||
) : isList && isInputField ? (
|
||||
<button
|
||||
onClick={() => {
|
||||
let newInputList = _.cloneDeep(ref.current);
|
||||
newInputList.splice(index, 1);
|
||||
onChange(newInputList);
|
||||
}}
|
||||
>
|
||||
<IconComponent
|
||||
name="X"
|
||||
className="h-4 w-4 hover:text-status-red"
|
||||
/>
|
||||
</button>
|
||||
) : (
|
||||
""
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
});
|
||||
})}
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default IOKeyPairInput;
|
||||
|
|
@ -1,6 +1,8 @@
|
|||
import { cloneDeep } from "lodash";
|
||||
import { useState } from "react";
|
||||
import ImageViewer from "../../../../components/ImageViewer";
|
||||
import CsvOutputComponent from "../../../../components/csvOutputComponent";
|
||||
import InputListComponent from "../../../../components/inputListComponent";
|
||||
import PdfViewer from "../../../../components/pdfViewer";
|
||||
import {
|
||||
Select,
|
||||
|
|
@ -15,7 +17,13 @@ import { PDFViewConstant } from "../../../../constants/constants";
|
|||
import { InputOutput } from "../../../../constants/enums";
|
||||
import useFlowStore from "../../../../stores/flowStore";
|
||||
import { IOFieldViewProps } from "../../../../types/components";
|
||||
import {
|
||||
convertValuesToNumbers,
|
||||
hasDuplicateKeys,
|
||||
} from "../../../../utils/reactflowUtils";
|
||||
import IOFileInput from "./components/FileInput";
|
||||
import IoJsonInput from "./components/JSONInput";
|
||||
import IOKeyPairInput from "./components/keyPairInput";
|
||||
|
||||
export default function IOFieldView({
|
||||
type,
|
||||
|
|
@ -39,6 +47,7 @@ export default function IOFieldView({
|
|||
}
|
||||
}
|
||||
};
|
||||
const [errorDuplicateKey, setErrorDuplicateKey] = useState(false);
|
||||
|
||||
function handleOutputType() {
|
||||
if (!node) return <>"No node found!"</>;
|
||||
|
|
@ -78,6 +87,57 @@ export default function IOFieldView({
|
|||
/>
|
||||
);
|
||||
|
||||
case "KeyPairInput":
|
||||
return (
|
||||
<IOKeyPairInput
|
||||
value={node.data.node!.template["input_value"]?.value}
|
||||
onChange={(e) => {
|
||||
if (node) {
|
||||
let newNode = cloneDeep(node);
|
||||
newNode.data.node!.template["input_value"].value = e;
|
||||
setNode(node.id, newNode);
|
||||
}
|
||||
const valueToNumbers = convertValuesToNumbers(e);
|
||||
setErrorDuplicateKey(hasDuplicateKeys(valueToNumbers));
|
||||
}}
|
||||
duplicateKey={errorDuplicateKey}
|
||||
isList={node.data.node!.template["input_value"]?.list ?? false}
|
||||
isInputField
|
||||
/>
|
||||
);
|
||||
|
||||
case "JsonInput":
|
||||
return (
|
||||
<IoJsonInput
|
||||
value={node.data.node!.template["input_value"]?.value}
|
||||
onChange={(e) => {
|
||||
if (node) {
|
||||
let newNode = cloneDeep(node);
|
||||
newNode.data.node!.template["input_value"].value = e;
|
||||
setNode(node.id, newNode);
|
||||
}
|
||||
}}
|
||||
left={left}
|
||||
/>
|
||||
);
|
||||
|
||||
case "StringListInput":
|
||||
return (
|
||||
<>
|
||||
<InputListComponent
|
||||
value={node.data.node!.template["input_value"]?.value}
|
||||
onChange={(e) => {
|
||||
if (node) {
|
||||
let newNode = cloneDeep(node);
|
||||
newNode.data.node!.template["input_value"].value = e;
|
||||
setNode(node.id, newNode);
|
||||
}
|
||||
}}
|
||||
disabled={false}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
|
||||
default:
|
||||
return (
|
||||
<Textarea
|
||||
|
|
@ -169,6 +229,58 @@ export default function IOFieldView({
|
|||
/>
|
||||
);
|
||||
|
||||
case "JsonOutput":
|
||||
return (
|
||||
<IoJsonInput
|
||||
value={node.data.node!.template["input_value"]?.value}
|
||||
onChange={(e) => {
|
||||
if (node) {
|
||||
let newNode = cloneDeep(node);
|
||||
newNode.data.node!.template["input_value"].value = e;
|
||||
setNode(node.id, newNode);
|
||||
}
|
||||
}}
|
||||
left={left}
|
||||
output
|
||||
/>
|
||||
);
|
||||
|
||||
case "KeyPairOutput":
|
||||
return (
|
||||
<IOKeyPairInput
|
||||
value={node.data.node!.template["input_value"]?.value}
|
||||
onChange={(e) => {
|
||||
if (node) {
|
||||
let newNode = cloneDeep(node);
|
||||
newNode.data.node!.template["input_value"].value = e;
|
||||
setNode(node.id, newNode);
|
||||
}
|
||||
const valueToNumbers = convertValuesToNumbers(e);
|
||||
setErrorDuplicateKey(hasDuplicateKeys(valueToNumbers));
|
||||
}}
|
||||
duplicateKey={errorDuplicateKey}
|
||||
isList={node.data.node!.template["input_value"]?.list ?? false}
|
||||
/>
|
||||
);
|
||||
|
||||
case "StringListOutput":
|
||||
return (
|
||||
<>
|
||||
<InputListComponent
|
||||
value={node.data.node!.template["input_value"]?.value}
|
||||
onChange={(e) => {
|
||||
if (node) {
|
||||
let newNode = cloneDeep(node);
|
||||
newNode.data.node!.template["input_value"].value = e;
|
||||
setNode(node.id, newNode);
|
||||
}
|
||||
}}
|
||||
playgroundDisabled
|
||||
disabled={false}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
|
||||
default:
|
||||
return (
|
||||
<Textarea
|
||||
|
|
|
|||
|
|
@ -6,9 +6,9 @@ import remarkGfm from "remark-gfm";
|
|||
import remarkMath from "remark-math";
|
||||
import MaleTechnology from "../../../../../assets/male-technologist.png";
|
||||
import Robot from "../../../../../assets/robot.png";
|
||||
import SanitizedHTMLWrapper from "../../../../../components/SanitizedHTMLWrapper";
|
||||
import CodeTabsComponent from "../../../../../components/codeTabsComponent";
|
||||
import IconComponent from "../../../../../components/genericIconComponent";
|
||||
import SanitizedHTMLWrapper from "../../../../../components/sanitizedHTMLWrapper";
|
||||
import useAlertStore from "../../../../../stores/alertStore";
|
||||
import useFlowStore from "../../../../../stores/flowStore";
|
||||
import { chatMessagePropsType } from "../../../../../types/components";
|
||||
|
|
@ -271,7 +271,7 @@ dark:prose-invert"
|
|||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<div>
|
||||
<div className="form-modal-chat-text-position min-w-96 flex-grow">
|
||||
{template ? (
|
||||
<>
|
||||
<button
|
||||
|
|
@ -324,6 +324,7 @@ dark:prose-invert"
|
|||
</>
|
||||
) : (
|
||||
<span
|
||||
className="prose text-primary word-break-break-word dark:prose-invert"
|
||||
data-testid={
|
||||
"chat-message-" + chat.sender_name + "-" + chatMessage
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { useEffect, useState } from "react";
|
||||
import AccordionComponent from "../../components/AccordionComponent";
|
||||
import ShadTooltip from "../../components/ShadTooltipComponent";
|
||||
import AccordionComponent from "../../components/accordionComponent";
|
||||
import IconComponent from "../../components/genericIconComponent";
|
||||
import ShadTooltip from "../../components/shadTooltipComponent";
|
||||
import { Badge } from "../../components/ui/badge";
|
||||
import { Button } from "../../components/ui/button";
|
||||
import {
|
||||
|
|
@ -34,25 +34,25 @@ export default function IOModal({
|
|||
}: IOModalPropsType): JSX.Element {
|
||||
const allNodes = useFlowStore((state) => state.nodes);
|
||||
const inputs = useFlowStore((state) => state.inputs).filter(
|
||||
(input) => input.type !== "ChatInput"
|
||||
(input) => input.type !== "ChatInput",
|
||||
);
|
||||
const chatInput = useFlowStore((state) => state.inputs).find(
|
||||
(input) => input.type === "ChatInput"
|
||||
(input) => input.type === "ChatInput",
|
||||
);
|
||||
const outputs = useFlowStore((state) => state.outputs).filter(
|
||||
(output) => output.type !== "ChatOutput"
|
||||
(output) => output.type !== "ChatOutput",
|
||||
);
|
||||
const chatOutput = useFlowStore((state) => state.outputs).find(
|
||||
(output) => output.type === "ChatOutput"
|
||||
(output) => output.type === "ChatOutput",
|
||||
);
|
||||
const nodes = useFlowStore((state) => state.nodes).filter(
|
||||
(node) =>
|
||||
inputs.some((input) => input.id === node.id) ||
|
||||
outputs.some((output) => output.id === node.id)
|
||||
outputs.some((output) => output.id === node.id),
|
||||
);
|
||||
const haveChat = chatInput || chatOutput;
|
||||
const [selectedTab, setSelectedTab] = useState(
|
||||
inputs.length > 0 ? 1 : outputs.length > 0 ? 2 : 0
|
||||
inputs.length > 0 ? 1 : outputs.length > 0 ? 2 : 0,
|
||||
);
|
||||
|
||||
function startView() {
|
||||
|
|
@ -83,12 +83,6 @@ export default function IOModal({
|
|||
return updateVerticesOrder(currentFlow!.id, null);
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (open) {
|
||||
updateVertices();
|
||||
}
|
||||
}, [open, currentFlow]);
|
||||
|
||||
async function sendMessage(count = 1): Promise<void> {
|
||||
if (isBuilding) return;
|
||||
setIsBuilding(true);
|
||||
|
|
@ -123,7 +117,7 @@ export default function IOModal({
|
|||
|
||||
return (
|
||||
<BaseModal
|
||||
size={selectedTab === 0 ? "large-thin" : "large"}
|
||||
size={selectedTab === 0 ? "large-thin" : "md-thin"}
|
||||
open={open}
|
||||
setOpen={setOpen}
|
||||
disable={disable}
|
||||
|
|
@ -132,9 +126,9 @@ export default function IOModal({
|
|||
{/* TODO ADAPT TO ALL TYPES OF INPUTS AND OUTPUTS */}
|
||||
<BaseModal.Header description={CHAT_FORM_DIALOG_SUBTITLE}>
|
||||
<div className="flex items-center">
|
||||
<span className="pr-2">Interaction Panel</span>
|
||||
<span className="pr-2">Playground</span>
|
||||
<IconComponent
|
||||
name="prompts"
|
||||
name="BotMessageSquareIcon"
|
||||
className="h-6 w-6 pl-1 text-foreground"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
|
|
@ -146,7 +140,7 @@ export default function IOModal({
|
|||
{selectedTab !== 0 && (
|
||||
<div
|
||||
className={cn(
|
||||
"mr-6 flex h-full w-2/6 flex-shrink-0 flex-col justify-start transition-all duration-300"
|
||||
"mr-6 flex h-full w-2/6 flex-shrink-0 flex-col justify-start transition-all duration-300",
|
||||
)}
|
||||
>
|
||||
<Tabs
|
||||
|
|
@ -179,11 +173,11 @@ export default function IOModal({
|
|||
</div>
|
||||
{nodes
|
||||
.filter((node) =>
|
||||
inputs.some((input) => input.id === node.id)
|
||||
inputs.some((input) => input.id === node.id),
|
||||
)
|
||||
.map((node, index) => {
|
||||
const input = inputs.find(
|
||||
(input) => input.id === node.id
|
||||
(input) => input.id === node.id,
|
||||
)!;
|
||||
return (
|
||||
<div
|
||||
|
|
@ -247,11 +241,11 @@ export default function IOModal({
|
|||
</div>
|
||||
{nodes
|
||||
.filter((node) =>
|
||||
outputs.some((output) => output.id === node.id)
|
||||
outputs.some((output) => output.id === node.id),
|
||||
)
|
||||
.map((node, index) => {
|
||||
const output = outputs.find(
|
||||
(output) => output.id === node.id
|
||||
(output) => output.id === node.id,
|
||||
)!;
|
||||
return (
|
||||
<div
|
||||
|
|
@ -314,7 +308,7 @@ export default function IOModal({
|
|||
<div
|
||||
className={cn(
|
||||
"flex h-full w-full flex-col items-start gap-4 pt-4",
|
||||
!selectedViewField ? "hidden" : ""
|
||||
!selectedViewField ? "hidden" : "",
|
||||
)}
|
||||
>
|
||||
<div className="font-xl flex items-center justify-center gap-3 font-semibold">
|
||||
|
|
@ -333,7 +327,7 @@ export default function IOModal({
|
|||
</div>
|
||||
<div className="h-full w-full">
|
||||
{inputs.some(
|
||||
(input) => input.id === selectedViewField.id
|
||||
(input) => input.id === selectedViewField.id,
|
||||
) ? (
|
||||
<IOFieldView
|
||||
type={InputOutput.INPUT}
|
||||
|
|
@ -355,7 +349,7 @@ export default function IOModal({
|
|||
<div
|
||||
className={cn(
|
||||
"flex h-full w-full",
|
||||
selectedViewField ? "hidden" : ""
|
||||
selectedViewField ? "hidden" : "",
|
||||
)}
|
||||
>
|
||||
{haveChat ? (
|
||||
|
|
@ -390,7 +384,7 @@ export default function IOModal({
|
|||
"h-4 w-4",
|
||||
isBuilding
|
||||
? "animate-spin"
|
||||
: "fill-current text-medium-indigo"
|
||||
: "fill-current text-medium-indigo",
|
||||
)}
|
||||
/>
|
||||
Run Flow
|
||||
|
|
|
|||
|
|
@ -63,7 +63,7 @@ interface BaseModalProps {
|
|||
React.ReactElement<ContentProps>,
|
||||
React.ReactElement<HeaderProps>,
|
||||
React.ReactElement<TriggerProps>?,
|
||||
React.ReactElement<FooterProps>?
|
||||
React.ReactElement<FooterProps>?,
|
||||
];
|
||||
open?: boolean;
|
||||
setOpen?: (open: boolean) => void;
|
||||
|
|
@ -78,6 +78,7 @@ interface BaseModalProps {
|
|||
| "large-h-full"
|
||||
| "small-h-full"
|
||||
| "medium-h-full"
|
||||
| "md-thin"
|
||||
| "smaller-h-full";
|
||||
|
||||
disable?: boolean;
|
||||
|
|
@ -93,16 +94,16 @@ function BaseModal({
|
|||
type = "dialog",
|
||||
}: BaseModalProps) {
|
||||
const headerChild = React.Children.toArray(children).find(
|
||||
(child) => (child as React.ReactElement).type === Header
|
||||
(child) => (child as React.ReactElement).type === Header,
|
||||
);
|
||||
const triggerChild = React.Children.toArray(children).find(
|
||||
(child) => (child as React.ReactElement).type === Trigger
|
||||
(child) => (child as React.ReactElement).type === Trigger,
|
||||
);
|
||||
const ContentChild = React.Children.toArray(children).find(
|
||||
(child) => (child as React.ReactElement).type === Content
|
||||
(child) => (child as React.ReactElement).type === Content,
|
||||
);
|
||||
const ContentFooter = React.Children.toArray(children).find(
|
||||
(child) => (child as React.ReactElement).type === Footer
|
||||
(child) => (child as React.ReactElement).type === Footer,
|
||||
);
|
||||
|
||||
let minWidth: string;
|
||||
|
|
@ -147,6 +148,12 @@ function BaseModal({
|
|||
minWidth = "min-w-[65vw]";
|
||||
height = "h-[80vh]";
|
||||
break;
|
||||
|
||||
case "md-thin":
|
||||
minWidth = "min-w-[85vw]";
|
||||
height = "h-[70vh]";
|
||||
break;
|
||||
|
||||
case "large-h-full":
|
||||
minWidth = "min-w-[80vw]";
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import React, { useEffect, useState } from "react";
|
||||
import ShadTooltip from "../../components/ShadTooltipComponent";
|
||||
import ShadTooltip from "../../components/shadTooltipComponent";
|
||||
import { Button } from "../../components/ui/button";
|
||||
import {
|
||||
ConfirmationModalType,
|
||||
|
|
@ -15,14 +15,18 @@ export default function DeleteConfirmationModal({
|
|||
onConfirm,
|
||||
description,
|
||||
asChild,
|
||||
open,
|
||||
setOpen,
|
||||
}: {
|
||||
children: JSX.Element;
|
||||
onConfirm: (e: React.MouseEvent<HTMLButtonElement, MouseEvent>) => void;
|
||||
description?: string;
|
||||
asChild?: boolean;
|
||||
open?: boolean;
|
||||
setOpen?: (open: boolean) => void;
|
||||
}) {
|
||||
return (
|
||||
<Dialog>
|
||||
<Dialog open={open} onOpenChange={setOpen}>
|
||||
<DialogTrigger asChild={asChild} tabIndex={-1}>
|
||||
{children}
|
||||
</DialogTrigger>
|
||||
|
|
@ -11,6 +11,7 @@ import "react18-json-view/src/style.css";
|
|||
import IconComponent from "../../components/genericIconComponent";
|
||||
import { Button } from "../../components/ui/button";
|
||||
import { CODE_DICT_DIALOG_SUBTITLE } from "../../constants/constants";
|
||||
import { useDarkStore } from "../../stores/darkStore";
|
||||
import BaseModal from "../baseModal";
|
||||
|
||||
export default function DictAreaModal({
|
||||
|
|
@ -19,7 +20,7 @@ export default function DictAreaModal({
|
|||
value,
|
||||
}): JSX.Element {
|
||||
const [open, setOpen] = useState(false);
|
||||
|
||||
const isDark = useDarkStore((state) => state.dark);
|
||||
const ref = useRef(value);
|
||||
|
||||
useEffect(() => {
|
||||
|
|
@ -41,7 +42,8 @@ export default function DictAreaModal({
|
|||
<div className="flex h-full w-full flex-col transition-all ">
|
||||
<JsonView
|
||||
theme="vscode"
|
||||
dark={true}
|
||||
dark={isDark}
|
||||
className={!isDark ? "json-view-white" : "json-view-dark"}
|
||||
editable
|
||||
enableClipboard
|
||||
onEdit={(edit) => {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
import { cloneDeep } from "lodash";
|
||||
import { forwardRef, useEffect, useState } from "react";
|
||||
import ShadTooltip from "../../components/ShadTooltipComponent";
|
||||
import CodeAreaComponent from "../../components/codeAreaComponent";
|
||||
import DictComponent from "../../components/dictComponent";
|
||||
import Dropdown from "../../components/dropdownComponent";
|
||||
|
|
@ -12,6 +11,7 @@ import InputListComponent from "../../components/inputListComponent";
|
|||
import IntComponent from "../../components/intComponent";
|
||||
import KeypairListComponent from "../../components/keypairListComponent";
|
||||
import PromptAreaComponent from "../../components/promptComponent";
|
||||
import ShadTooltip from "../../components/shadTooltipComponent";
|
||||
import TextAreaComponent from "../../components/textAreaComponent";
|
||||
import ToggleShadComponent from "../../components/toggleShadComponent";
|
||||
import { Badge } from "../../components/ui/badge";
|
||||
|
|
@ -211,7 +211,7 @@ const EditNodeModal = forwardRef(
|
|||
!myData.node.template[templateParam].options ? (
|
||||
<div className="mx-auto">
|
||||
{myData.node.template[templateParam]
|
||||
.list ? (
|
||||
?.list ? (
|
||||
<InputListComponent
|
||||
componentName={templateParam}
|
||||
editNode={true}
|
||||
|
|
@ -353,7 +353,7 @@ const EditNodeModal = forwardRef(
|
|||
}}
|
||||
isList={
|
||||
data.node?.template[templateParam]
|
||||
.list ?? false
|
||||
?.list ?? false
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
|
|
@ -428,6 +428,10 @@ const EditNodeModal = forwardRef(
|
|||
.type === "int" ? (
|
||||
<div className="mx-auto">
|
||||
<IntComponent
|
||||
rangeSpec={
|
||||
data.node?.template[templateParam]
|
||||
?.rangeSpec
|
||||
}
|
||||
id={
|
||||
"edit-int-input-" +
|
||||
myData.node.template[templateParam].name
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
import { ReactNode, forwardRef, useEffect, useState } from "react";
|
||||
import EditFlowSettings from "../../components/EditFlowSettingsComponent";
|
||||
import EditFlowSettings from "../../components/editFlowSettingsComponent";
|
||||
import IconComponent from "../../components/genericIconComponent";
|
||||
import { Button } from "../../components/ui/button";
|
||||
import { Checkbox } from "../../components/ui/checkbox";
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { useEffect, useState } from "react";
|
||||
import EditFlowSettings from "../../components/EditFlowSettingsComponent";
|
||||
import EditFlowSettings from "../../components/editFlowSettingsComponent";
|
||||
import IconComponent from "../../components/genericIconComponent";
|
||||
import { Button } from "../../components/ui/button";
|
||||
import { SETTINGS_DIALOG_SUBTITLE } from "../../constants/constants";
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { useEffect, useRef, useState } from "react";
|
||||
import SanitizedHTMLWrapper from "../../components/SanitizedHTMLWrapper";
|
||||
import ShadTooltip from "../../components/ShadTooltipComponent";
|
||||
import IconComponent from "../../components/genericIconComponent";
|
||||
import SanitizedHTMLWrapper from "../../components/sanitizedHTMLWrapper";
|
||||
import ShadTooltip from "../../components/shadTooltipComponent";
|
||||
import { Badge } from "../../components/ui/badge";
|
||||
import { Button } from "../../components/ui/button";
|
||||
import { Textarea } from "../../components/ui/textarea";
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { Loader2 } from "lucide-react";
|
||||
import { ReactNode, useEffect, useMemo, useState } from "react";
|
||||
import { useHotkeys } from "react-hotkeys-hook";
|
||||
import EditFlowSettings from "../../components/EditFlowSettingsComponent";
|
||||
import EditFlowSettings from "../../components/editFlowSettingsComponent";
|
||||
import IconComponent from "../../components/genericIconComponent";
|
||||
import { TagsSelector } from "../../components/tagsSelectorComponent";
|
||||
import { Button } from "../../components/ui/button";
|
||||
|
|
@ -24,8 +24,8 @@ import {
|
|||
removeGlobalVariableFromComponents,
|
||||
} from "../../utils/reactflowUtils";
|
||||
import { getTagsIds } from "../../utils/storeUtils";
|
||||
import ConfirmationModal from "../ConfirmationModal";
|
||||
import BaseModal from "../baseModal";
|
||||
import ConfirmationModal from "../confirmationModal";
|
||||
import ExportModal from "../exportModal";
|
||||
|
||||
export default function ShareModal({
|
||||
|
|
@ -234,7 +234,7 @@ export default function ShareModal({
|
|||
<div className="mt-3 flex h-8 w-full">
|
||||
<TagsSelector
|
||||
tags={tags}
|
||||
loadingTags={loadingTags}
|
||||
loadingTags={loadingTags}
|
||||
disabled={false}
|
||||
selectedTags={selectedTags}
|
||||
setSelectedTags={setSelectedTags}
|
||||
|
|
|
|||
|
|
@ -86,6 +86,7 @@ export default function StoreApiKeyModal({
|
|||
<Form.Root
|
||||
onSubmit={(event) => {
|
||||
event.preventDefault();
|
||||
handleSaveKey();
|
||||
}}
|
||||
>
|
||||
<div className="grid gap-5">
|
||||
|
|
@ -131,9 +132,6 @@ export default function StoreApiKeyModal({
|
|||
<Button
|
||||
data-testid="api-key-save-button-store"
|
||||
className="mt-8"
|
||||
onClick={() => {
|
||||
handleSaveKey();
|
||||
}}
|
||||
>
|
||||
Save
|
||||
</Button>
|
||||
|
|
@ -1,10 +1,10 @@
|
|||
import { cloneDeep } from "lodash";
|
||||
import { useContext, useEffect, useRef, useState } from "react";
|
||||
import PaginatorComponent from "../../components/PaginatorComponent";
|
||||
import ShadTooltip from "../../components/ShadTooltipComponent";
|
||||
import IconComponent from "../../components/genericIconComponent";
|
||||
import Header from "../../components/headerComponent";
|
||||
import LoadingComponent from "../../components/loadingComponent";
|
||||
import PaginatorComponent from "../../components/paginatorComponent";
|
||||
import ShadTooltip from "../../components/shadTooltipComponent";
|
||||
import { Button } from "../../components/ui/button";
|
||||
import { CheckBoxDiv } from "../../components/ui/checkbox";
|
||||
import { Input } from "../../components/ui/input";
|
||||
|
|
@ -35,8 +35,8 @@ import {
|
|||
getUsersPage,
|
||||
updateUser,
|
||||
} from "../../controllers/API";
|
||||
import ConfirmationModal from "../../modals/ConfirmationModal";
|
||||
import UserManagementModal from "../../modals/UserManagementModal";
|
||||
import ConfirmationModal from "../../modals/confirmationModal";
|
||||
import UserManagementModal from "../../modals/userManagementModal";
|
||||
import useAlertStore from "../../stores/alertStore";
|
||||
import useFlowsManagerStore from "../../stores/flowsManagerStore";
|
||||
import { Users } from "../../types/api";
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { useContext, useEffect, useRef, useState } from "react";
|
||||
import ShadTooltip from "../../components/ShadTooltipComponent";
|
||||
import IconComponent from "../../components/genericIconComponent";
|
||||
import ShadTooltip from "../../components/shadTooltipComponent";
|
||||
import { Button } from "../../components/ui/button";
|
||||
import {
|
||||
Table,
|
||||
|
|
@ -12,8 +12,8 @@ import {
|
|||
} from "../../components/ui/table";
|
||||
import { AuthContext } from "../../contexts/authContext";
|
||||
import { deleteApiKey, getApiKey } from "../../controllers/API";
|
||||
import ConfirmationModal from "../../modals/ConfirmationModal";
|
||||
import SecretKeyModal from "../../modals/SecretKeyModal";
|
||||
import ConfirmationModal from "../../modals/confirmationModal";
|
||||
import SecretKeyModal from "../../modals/secretKeyModal";
|
||||
|
||||
import moment from "moment";
|
||||
import Header from "../../components/headerComponent";
|
||||
|
|
|
|||
|
|
@ -19,13 +19,13 @@ import ReactFlow, {
|
|||
SelectionDragHandler,
|
||||
updateEdge,
|
||||
} from "reactflow";
|
||||
import GenericNode from "../../../../CustomNodes/GenericNode";
|
||||
import {
|
||||
INVALID_SELECTION_ERROR_ALERT,
|
||||
UPLOAD_ALERT_LIST,
|
||||
UPLOAD_ERROR_ALERT,
|
||||
WRONG_FILE_ERROR_ALERT,
|
||||
} from "../../../../constants/alerts_constants";
|
||||
import GenericNode from "../../../../customNodes/genericNode";
|
||||
import useAlertStore from "../../../../stores/alertStore";
|
||||
import useFlowStore from "../../../../stores/flowStore";
|
||||
import useFlowsManagerStore from "../../../../stores/flowsManagerStore";
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
import { cloneDeep } from "lodash";
|
||||
import { LinkIcon, SparklesIcon } from "lucide-react";
|
||||
import { useEffect, useMemo, useState } from "react";
|
||||
import ShadTooltip from "../../../../components/ShadTooltipComponent";
|
||||
import IconComponent from "../../../../components/genericIconComponent";
|
||||
import ShadTooltip from "../../../../components/shadTooltipComponent";
|
||||
import { Input } from "../../../../components/ui/input";
|
||||
import { Separator } from "../../../../components/ui/separator";
|
||||
import { PRIORITY_SIDEBAR_ORDER } from "../../../../constants/constants";
|
||||
|
|
|
|||
|
|
@ -2,9 +2,9 @@ import _, { cloneDeep } from "lodash";
|
|||
import { useEffect, useState } from "react";
|
||||
import { useHotkeys } from "react-hotkeys-hook";
|
||||
import { useUpdateNodeInternals } from "reactflow";
|
||||
import ShadTooltip from "../../../../components/ShadTooltipComponent";
|
||||
import CodeAreaComponent from "../../../../components/codeAreaComponent";
|
||||
import IconComponent from "../../../../components/genericIconComponent";
|
||||
import ShadTooltip from "../../../../components/shadTooltipComponent";
|
||||
import {
|
||||
Select,
|
||||
SelectContent,
|
||||
|
|
@ -12,8 +12,8 @@ import {
|
|||
SelectTrigger,
|
||||
} from "../../../../components/ui/select-custom";
|
||||
import { postCustomComponent } from "../../../../controllers/API";
|
||||
import ConfirmationModal from "../../../../modals/ConfirmationModal";
|
||||
import EditNodeModal from "../../../../modals/EditNodeModal";
|
||||
import ConfirmationModal from "../../../../modals/confirmationModal";
|
||||
import EditNodeModal from "../../../../modals/editNodeModal";
|
||||
import ShareModal from "../../../../modals/shareModal";
|
||||
import useAlertStore from "../../../../stores/alertStore";
|
||||
import { useDarkStore } from "../../../../stores/darkStore";
|
||||
|
|
@ -62,7 +62,7 @@ export default function NodeToolbarComponent({
|
|||
data.node.template[templateField].type === "Any" ||
|
||||
data.node.template[templateField].type === "int" ||
|
||||
data.node.template[templateField].type === "dict" ||
|
||||
data.node.template[templateField].type === "NestedDict")
|
||||
data.node.template[templateField].type === "NestedDict"),
|
||||
).length;
|
||||
const templates = useTypesStore((state) => state.templates);
|
||||
const hasStore = useStoreStore((state) => state.hasStore);
|
||||
|
|
@ -182,7 +182,7 @@ export default function NodeToolbarComponent({
|
|||
const [showconfirmShare, setShowconfirmShare] = useState(false);
|
||||
const [showOverrideModal, setShowOverrideModal] = useState(false);
|
||||
const [flowComponent, setFlowComponent] = useState<FlowType>(
|
||||
createFlowComponent(cloneDeep(data), version)
|
||||
createFlowComponent(cloneDeep(data), version),
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
|
|
@ -201,7 +201,7 @@ export default function NodeToolbarComponent({
|
|||
const updateNodeInternals = useUpdateNodeInternals();
|
||||
|
||||
const setLastCopiedSelection = useFlowStore(
|
||||
(state) => state.setLastCopiedSelection
|
||||
(state) => state.setLastCopiedSelection,
|
||||
);
|
||||
|
||||
const setSuccessData = useAlertStore((state) => state.setSuccessData);
|
||||
|
|
@ -260,7 +260,7 @@ export default function NodeToolbarComponent({
|
|||
nodes,
|
||||
edges,
|
||||
setNodes,
|
||||
setEdges
|
||||
setEdges,
|
||||
);
|
||||
break;
|
||||
case "override":
|
||||
|
|
@ -284,7 +284,7 @@ export default function NodeToolbarComponent({
|
|||
y: 10,
|
||||
paneX: nodes.find((node) => node.id === data.id)?.position.x,
|
||||
paneY: nodes.find((node) => node.id === data.id)?.position.y,
|
||||
}
|
||||
},
|
||||
);
|
||||
break;
|
||||
case "update":
|
||||
|
|
@ -322,13 +322,13 @@ export default function NodeToolbarComponent({
|
|||
};
|
||||
|
||||
const isSaved = flows.some((flow) =>
|
||||
Object.values(flow).includes(data.node?.display_name!)
|
||||
Object.values(flow).includes(data.node?.display_name!),
|
||||
);
|
||||
|
||||
const setNode = useFlowStore((state) => state.setNode);
|
||||
|
||||
const handleOnNewValue = (
|
||||
newValue: string | string[] | boolean | Object[]
|
||||
newValue: string | string[] | boolean | Object[],
|
||||
): void => {
|
||||
if (data.node!.template[name].value !== newValue) {
|
||||
takeSnapshot();
|
||||
|
|
@ -398,7 +398,7 @@ export default function NodeToolbarComponent({
|
|||
data-testid="save-button-modal"
|
||||
className={classNames(
|
||||
"relative -ml-px inline-flex items-center bg-background px-2 py-2 text-foreground shadow-md ring-1 ring-inset ring-ring transition-all duration-500 ease-in-out hover:bg-muted focus:z-10",
|
||||
hasCode ? " " : " rounded-l-md "
|
||||
hasCode ? " " : " rounded-l-md ",
|
||||
)}
|
||||
onClick={(event) => {
|
||||
event.preventDefault();
|
||||
|
|
@ -416,7 +416,7 @@ export default function NodeToolbarComponent({
|
|||
<button
|
||||
data-testid="duplicate-button-modal"
|
||||
className={classNames(
|
||||
"relative -ml-px inline-flex items-center bg-background px-2 py-2 text-foreground shadow-md ring-1 ring-inset ring-ring transition-all duration-500 ease-in-out hover:bg-muted focus:z-10"
|
||||
"relative -ml-px inline-flex items-center bg-background px-2 py-2 text-foreground shadow-md ring-1 ring-inset ring-ring transition-all duration-500 ease-in-out hover:bg-muted focus:z-10",
|
||||
)}
|
||||
onClick={(event) => {
|
||||
event.preventDefault();
|
||||
|
|
@ -464,7 +464,7 @@ export default function NodeToolbarComponent({
|
|||
<div
|
||||
data-testid="more-options-modal"
|
||||
className={classNames(
|
||||
"relative -ml-px inline-flex h-8 w-[31px] items-center rounded-r-md bg-background text-foreground shadow-md ring-1 ring-inset ring-ring transition-all duration-500 ease-in-out hover:bg-muted focus:z-10"
|
||||
"relative -ml-px inline-flex h-8 w-[31px] items-center rounded-r-md bg-background text-foreground shadow-md ring-1 ring-inset ring-ring transition-all duration-500 ease-in-out hover:bg-muted focus:z-10",
|
||||
)}
|
||||
>
|
||||
<IconComponent
|
||||
|
|
|
|||
|
|
@ -6,18 +6,25 @@ import { useDarkStore } from "../../stores/darkStore";
|
|||
import useFlowsManagerStore from "../../stores/flowsManagerStore";
|
||||
import Page from "./components/PageComponent";
|
||||
import ExtraSidebar from "./components/extraSidebarComponent";
|
||||
import useFlowStore from "../../stores/flowStore";
|
||||
|
||||
export default function FlowPage({ view }: { view?: boolean }): JSX.Element {
|
||||
const setCurrentFlowId = useFlowsManagerStore(
|
||||
(state) => state.setCurrentFlowId
|
||||
);
|
||||
const version = useDarkStore((state) => state.version);
|
||||
const setOnFlowPage = useFlowStore((state) => state.setOnFlowPage);
|
||||
const currentFlow = useFlowsManagerStore((state) => state.currentFlow);
|
||||
const { id } = useParams();
|
||||
|
||||
// Set flow tab id
|
||||
useEffect(() => {
|
||||
setCurrentFlowId(id!);
|
||||
setOnFlowPage(true);
|
||||
|
||||
return () => {
|
||||
setOnFlowPage(false);
|
||||
};
|
||||
}, [id]);
|
||||
return (
|
||||
<>
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
import { useState } from "react";
|
||||
import { Link, useNavigate } from "react-router-dom";
|
||||
import PaginatorComponent from "../../../../components/PaginatorComponent";
|
||||
import CollectionCardComponent from "../../../../components/cardComponent";
|
||||
import CardsWrapComponent from "../../../../components/cardsWrapComponent";
|
||||
import IconComponent from "../../../../components/genericIconComponent";
|
||||
import PaginatorComponent from "../../../../components/paginatorComponent";
|
||||
import { SkeletonCardComponent } from "../../../../components/skeletonCardComponent";
|
||||
import { Button } from "../../../../components/ui/button";
|
||||
import {
|
||||
|
|
@ -14,7 +14,6 @@ import {
|
|||
import useAlertStore from "../../../../stores/alertStore";
|
||||
import useFlowsManagerStore from "../../../../stores/flowsManagerStore";
|
||||
import { FlowType } from "../../../../types/flow";
|
||||
|
||||
export default function ComponentsComponent({
|
||||
is_component = true,
|
||||
}: {
|
||||
|
|
@ -31,7 +30,6 @@ export default function ComponentsComponent({
|
|||
const [pageIndex, setPageIndex] = useState(1);
|
||||
|
||||
const navigate = useNavigate();
|
||||
|
||||
const all: FlowType[] = flows
|
||||
.filter((f) => (f.is_component ?? false) === is_component)
|
||||
.sort((a, b) => {
|
||||
|
|
@ -87,12 +85,10 @@ export default function ComponentsComponent({
|
|||
}
|
||||
}
|
||||
};
|
||||
|
||||
function resetFilter() {
|
||||
setPageIndex(1);
|
||||
setPageSize(20);
|
||||
}
|
||||
|
||||
return (
|
||||
<CardsWrapComponent
|
||||
onFileDrop={onFileDrop}
|
||||
|
|
@ -144,6 +140,7 @@ export default function ComponentsComponent({
|
|||
key={idx}
|
||||
data={{ is_component: item.is_component ?? false, ...item }}
|
||||
disabled={isLoading}
|
||||
data-testid={"edit-flow-button-" + item.id + "-" + idx}
|
||||
button={
|
||||
!is_component ? (
|
||||
<Link to={"/flow/" + item.id}>
|
||||
|
|
@ -167,6 +164,14 @@ export default function ComponentsComponent({
|
|||
<></>
|
||||
)
|
||||
}
|
||||
onClick={
|
||||
!is_component
|
||||
? () => {
|
||||
navigate("/flow/" + item.id);
|
||||
}
|
||||
: undefined
|
||||
}
|
||||
playground={!is_component}
|
||||
/>
|
||||
))
|
||||
) : (
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { Group, ToyBrick } from "lucide-react";
|
||||
import { useEffect, useState } from "react";
|
||||
import { Outlet, useLocation, useNavigate } from "react-router-dom";
|
||||
import DropdownButton from "../../components/DropdownButtonComponent";
|
||||
import DropdownButton from "../../components/dropdownButtonComponent";
|
||||
import IconComponent from "../../components/genericIconComponent";
|
||||
import PageLayout from "../../components/pageLayout";
|
||||
import SidebarNav from "../../components/sidebarComponent";
|
||||
|
|
@ -11,7 +11,7 @@ import {
|
|||
MY_COLLECTION_DESC,
|
||||
USER_PROJECTS_HEADER,
|
||||
} from "../../constants/constants";
|
||||
import NewFlowModal from "../../modals/NewFlowModal";
|
||||
import NewFlowModal from "../../modals/newFlowModal";
|
||||
import useAlertStore from "../../stores/alertStore";
|
||||
import useFlowsManagerStore from "../../stores/flowsManagerStore";
|
||||
import { downloadFlows } from "../../utils/reactflowUtils";
|
||||
|
|
|
|||
64
src/frontend/src/pages/Playground/index.tsx
Normal file
64
src/frontend/src/pages/Playground/index.tsx
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
import { useEffect, useState } from "react";
|
||||
import { useParams } from "react-router-dom";
|
||||
import useFlowsManagerStore from "../../stores/flowsManagerStore";
|
||||
import { getComponent } from "../../controllers/API";
|
||||
import cloneFLowWithParent from "../../utils/storeUtils";
|
||||
import LoadingComponent from "../../components/loadingComponent";
|
||||
import useFlowStore from "../../stores/flowStore";
|
||||
import IOModal from "../../modals/IOModal";
|
||||
|
||||
export default function PlaygroundPage() {
|
||||
const currentFlow = useFlowsManagerStore((state) => state.currentFlow);
|
||||
const getFlowById = useFlowsManagerStore((state) => state.getFlowById);
|
||||
const setCurrentFlowId = useFlowsManagerStore((state) => state.setCurrentFlowId);
|
||||
const currentFlowId = useFlowsManagerStore((state) => state.currentFlowId);
|
||||
const setCurrentFlow = useFlowsManagerStore((state) => state.setCurrentFlow);
|
||||
const setNodes = useFlowStore((state) => state.setNodes);
|
||||
const setEdges = useFlowStore((state) => state.setEdges);
|
||||
const cleanFlowPool = useFlowStore((state) => state.CleanFlowPool);
|
||||
|
||||
const { id } = useParams();
|
||||
const [loading, setLoading] = useState(true);
|
||||
async function getFlowData() {
|
||||
const res = await getComponent(id!);
|
||||
const newFlow = cloneFLowWithParent(res, res.id, false, true);
|
||||
return newFlow;
|
||||
}
|
||||
|
||||
// Set flow tab id
|
||||
useEffect(() => {
|
||||
console.log("id", id);
|
||||
if (getFlowById(id!)) {
|
||||
setCurrentFlowId(id!);
|
||||
}
|
||||
else {
|
||||
getFlowData().then((flow) => {
|
||||
setCurrentFlow(flow);
|
||||
});
|
||||
}
|
||||
}, [id]);
|
||||
|
||||
useEffect(() => {
|
||||
if (currentFlow) {
|
||||
setNodes(currentFlow?.data?.nodes ?? [], true);
|
||||
setEdges(currentFlow?.data?.edges ?? [], true);
|
||||
cleanFlowPool();
|
||||
setLoading(false);
|
||||
}
|
||||
return () => {
|
||||
setNodes([], true);
|
||||
setEdges([], true);
|
||||
cleanFlowPool();
|
||||
};
|
||||
}, [currentFlow]);
|
||||
|
||||
|
||||
return (
|
||||
<div className="w-full h-full flex flex-col align-middle items-center justify-center">
|
||||
{loading ? <div><LoadingComponent remSize={24}></LoadingComponent></div> :
|
||||
<IOModal open={true}setOpen={()=>{}} isPlayground>
|
||||
<></>
|
||||
</IOModal>}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
|
@ -113,8 +113,7 @@ export default function GeneralPage() {
|
|||
</div>
|
||||
|
||||
<div className="grid gap-6">
|
||||
{!autoLogin && (
|
||||
<>
|
||||
|
||||
<Form.Root
|
||||
onSubmit={(event) => {
|
||||
handlePatchGradient();
|
||||
|
|
@ -152,7 +151,7 @@ export default function GeneralPage() {
|
|||
</Form.Submit>
|
||||
</CardFooter>
|
||||
</Card>
|
||||
</Form.Root>
|
||||
</Form.Root>{!autoLogin && (
|
||||
<Form.Root
|
||||
onSubmit={(event) => {
|
||||
handlePatchPassword();
|
||||
|
|
@ -218,7 +217,6 @@ export default function GeneralPage() {
|
|||
</CardFooter>
|
||||
</Card>
|
||||
</Form.Root>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,15 +1,15 @@
|
|||
import { uniqueId } from "lodash";
|
||||
import { useContext, useEffect, useState } from "react";
|
||||
import PaginatorComponent from "../../components/PaginatorComponent";
|
||||
import ShadTooltip from "../../components/ShadTooltipComponent";
|
||||
import CollectionCardComponent from "../../components/cardComponent";
|
||||
import IconComponent from "../../components/genericIconComponent";
|
||||
import PageLayout from "../../components/pageLayout";
|
||||
import ShadTooltip from "../../components/shadTooltipComponent";
|
||||
import { SkeletonCardComponent } from "../../components/skeletonCardComponent";
|
||||
import { Button } from "../../components/ui/button";
|
||||
import { Input } from "../../components/ui/input";
|
||||
|
||||
import { Link, useParams } from "react-router-dom";
|
||||
import { Link, useNavigate, useParams } from "react-router-dom";
|
||||
import PaginatorComponent from "../../components/paginatorComponent";
|
||||
import { TagsSelector } from "../../components/tagsSelectorComponent";
|
||||
import { Badge } from "../../components/ui/badge";
|
||||
import {
|
||||
|
|
@ -29,7 +29,7 @@ import {
|
|||
import { STORE_DESC, STORE_TITLE } from "../../constants/constants";
|
||||
import { AuthContext } from "../../contexts/authContext";
|
||||
import { getStoreComponents, getStoreTags } from "../../controllers/API";
|
||||
import StoreApiKeyModal from "../../modals/StoreApiKeyModal";
|
||||
import StoreApiKeyModal from "../../modals/storeApiKeyModal";
|
||||
import useAlertStore from "../../stores/alertStore";
|
||||
import useFlowsManagerStore from "../../stores/flowsManagerStore";
|
||||
import { useStoreStore } from "../../stores/storeStore";
|
||||
|
|
@ -65,6 +65,8 @@ export default function StorePage(): JSX.Element {
|
|||
const [searchNow, setSearchNow] = useState("");
|
||||
const [selectFilter, setSelectFilter] = useState("all");
|
||||
|
||||
const navigate = useNavigate();
|
||||
|
||||
useEffect(() => {
|
||||
if (!loadingApiKey) {
|
||||
if (!hasApiKey) {
|
||||
|
|
@ -371,6 +373,10 @@ export default function StorePage(): JSX.Element {
|
|||
data={item}
|
||||
authorized={validApiKey}
|
||||
disabled={loading}
|
||||
playground={
|
||||
item.last_tested_version?.includes("1.0.0") &&
|
||||
!item.is_component
|
||||
}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -7,18 +7,19 @@ import { StoreGuard } from "./components/storeGuard";
|
|||
import AdminPage from "./pages/AdminPage";
|
||||
import LoginAdminPage from "./pages/AdminPage/LoginPage";
|
||||
import ApiKeysPage from "./pages/ApiKeysPage";
|
||||
import DeleteAccountPage from "./pages/DeleteAccountPage";
|
||||
import FlowPage from "./pages/FlowPage";
|
||||
import LoginPage from "./pages/LoginPage";
|
||||
import HomePage from "./pages/MainPage";
|
||||
import ComponentsComponent from "./pages/MainPage/components/components";
|
||||
import PlaygroundPage from "./pages/Playground";
|
||||
import SettingsPage from "./pages/SettingsPage";
|
||||
import GeneralPage from "./pages/SettingsPage/pages/GeneralPage";
|
||||
import GlobalVariablesPage from "./pages/SettingsPage/pages/GlobalVariablesPage";
|
||||
import ShortcutsPage from "./pages/SettingsPage/pages/ShortcutsPage";
|
||||
import SignUp from "./pages/SignUpPage";
|
||||
import StorePage from "./pages/StorePage";
|
||||
import ViewPage from "./pages/ViewPage";
|
||||
import DeleteAccountPage from "./pages/deleteAccountPage";
|
||||
import LoginPage from "./pages/loginPage";
|
||||
import SignUp from "./pages/signUpPage";
|
||||
|
||||
const Router = () => {
|
||||
return (
|
||||
|
|
@ -74,7 +75,19 @@ const Router = () => {
|
|||
</ProtectedRoute>
|
||||
}
|
||||
/>
|
||||
|
||||
<Route path="/playground/:id/">
|
||||
element=
|
||||
{
|
||||
<Route
|
||||
path=""
|
||||
element={
|
||||
<ProtectedRoute>
|
||||
<PlaygroundPage />
|
||||
</ProtectedRoute>
|
||||
}
|
||||
/>
|
||||
}
|
||||
</Route>
|
||||
<Route path="/flow/:id/">
|
||||
<Route
|
||||
path=""
|
||||
|
|
|
|||
|
|
@ -44,9 +44,12 @@ import { getInputsAndOutputs } from "../utils/storeUtils";
|
|||
import useAlertStore from "./alertStore";
|
||||
import { useDarkStore } from "./darkStore";
|
||||
import useFlowsManagerStore from "./flowsManagerStore";
|
||||
import FlowPage from "../pages/FlowPage";
|
||||
|
||||
// this is our useStore hook that we can use in our components to get parts of the store and call actions
|
||||
const useFlowStore = create<FlowStoreType>((set, get) => ({
|
||||
onFlowPage: false,
|
||||
setOnFlowPage:(FlowPage=>set({onFlowPage:FlowPage})),
|
||||
flowState: undefined,
|
||||
flowBuildStatus: {},
|
||||
nodes: [],
|
||||
|
|
@ -149,7 +152,7 @@ const useFlowStore = create<FlowStoreType>((set, get) => ({
|
|||
edges: applyEdgeChanges(changes, get().edges),
|
||||
});
|
||||
},
|
||||
setNodes: (change) => {
|
||||
setNodes: (change,skipSave=false) => {
|
||||
let newChange = typeof change === "function" ? change(get().nodes) : change;
|
||||
let newEdges = cleanEdges(newChange, get().edges);
|
||||
const { inputs, outputs } = getInputsAndOutputs(newChange);
|
||||
|
|
@ -164,7 +167,7 @@ const useFlowStore = create<FlowStoreType>((set, get) => ({
|
|||
});
|
||||
|
||||
const flowsManager = useFlowsManagerStore.getState();
|
||||
if (!get().isBuilding) {
|
||||
if (!get().isBuilding && !skipSave && get().onFlowPage) {
|
||||
flowsManager.autoSaveCurrentFlow(
|
||||
newChange,
|
||||
newEdges,
|
||||
|
|
@ -172,7 +175,7 @@ const useFlowStore = create<FlowStoreType>((set, get) => ({
|
|||
);
|
||||
}
|
||||
},
|
||||
setEdges: (change) => {
|
||||
setEdges: (change,skipSave=false) => {
|
||||
let newChange = typeof change === "function" ? change(get().edges) : change;
|
||||
set({
|
||||
edges: newChange,
|
||||
|
|
@ -180,7 +183,7 @@ const useFlowStore = create<FlowStoreType>((set, get) => ({
|
|||
});
|
||||
|
||||
const flowsManager = useFlowsManagerStore.getState();
|
||||
if (!get().isBuilding) {
|
||||
if (!get().isBuilding && !skipSave && get().onFlowPage) {
|
||||
flowsManager.autoSaveCurrentFlow(
|
||||
get().nodes,
|
||||
newChange,
|
||||
|
|
@ -565,6 +568,8 @@ const useFlowStore = create<FlowStoreType>((set, get) => ({
|
|||
useFlowStore.getState().updateBuildStatus(idList, BuildStatus.BUILDING);
|
||||
},
|
||||
onValidateNodes: validateSubgraph,
|
||||
nodes: !get().onFlowPage ? get().nodes : undefined,
|
||||
edges: !get().onFlowPage ? get().edges : undefined,
|
||||
});
|
||||
get().setIsBuilding(false);
|
||||
get().revertBuiltStatusFromBuilding();
|
||||
|
|
|
|||
|
|
@ -47,6 +47,16 @@ const useFlowsManagerStore = create<FlowsManagerStoreType>((set, get) => ({
|
|||
set({ examples });
|
||||
},
|
||||
currentFlowId: "",
|
||||
setCurrentFlow: (flow: FlowType) => {
|
||||
set((state) => ({
|
||||
currentFlow: flow,
|
||||
currentFlowId: flow.id,
|
||||
}));
|
||||
|
||||
},
|
||||
getFlowById: (id: string) => {
|
||||
return get().flows.find((flow) => flow.id === id);
|
||||
},
|
||||
setCurrentFlowId: (currentFlowId: string) => {
|
||||
set((state) => ({
|
||||
currentFlowId,
|
||||
|
|
|
|||
|
|
@ -68,3 +68,31 @@ select:-webkit-autofill:focus {
|
|||
background-color: #bbb;
|
||||
border-radius: 999px;
|
||||
}
|
||||
|
||||
.json-view-playground-white-left {
|
||||
background-color: #fff !important;
|
||||
height: fit-content !important;
|
||||
}
|
||||
|
||||
.json-view-playground-dark {
|
||||
background-color: #141924 !important;
|
||||
height: fit-content !important;
|
||||
}
|
||||
|
||||
.json-view-playground-white {
|
||||
background-color: #f8fafc !important;
|
||||
height: fit-content !important;
|
||||
}
|
||||
|
||||
.json-view-playground-dark-left {
|
||||
background-color: #0c101a !important;
|
||||
height: fit-content !important;
|
||||
}
|
||||
|
||||
.json-view-white {
|
||||
background-color: #f8fafc !important;
|
||||
}
|
||||
|
||||
.json-view-dark {
|
||||
background-color: #141924 !important;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -72,15 +72,7 @@ export type InputListComponentType = {
|
|||
disabled: boolean;
|
||||
editNode?: boolean;
|
||||
componentName?: string;
|
||||
};
|
||||
|
||||
export type InputGlobalComponentType = {
|
||||
disabled: boolean;
|
||||
onChange: (value: string) => void;
|
||||
setDb: (value: boolean) => void;
|
||||
name: string;
|
||||
data: NodeDataType;
|
||||
editNode?: boolean;
|
||||
playgroundDisabled?: boolean;
|
||||
};
|
||||
|
||||
export type KeyPairListComponentType = {
|
||||
|
|
@ -96,9 +88,11 @@ export type KeyPairListComponentType = {
|
|||
export type DictComponentType = {
|
||||
value: any;
|
||||
onChange: (value) => void;
|
||||
disabled: boolean;
|
||||
disabled?: boolean;
|
||||
editNode?: boolean;
|
||||
id?: string;
|
||||
left?: boolean;
|
||||
output?: boolean;
|
||||
};
|
||||
|
||||
export type TextAreaComponentType = {
|
||||
|
|
@ -597,6 +591,8 @@ export type IOModalPropsType = {
|
|||
open: boolean;
|
||||
setOpen: (open: boolean) => void;
|
||||
disable?: boolean;
|
||||
isPlayground?: boolean;
|
||||
cleanOnClose?: boolean;
|
||||
};
|
||||
|
||||
export type buttonBoxPropsType = {
|
||||
|
|
|
|||
|
|
@ -45,6 +45,8 @@ export type FlowPoolType = {
|
|||
};
|
||||
|
||||
export type FlowStoreType = {
|
||||
onFlowPage: boolean;
|
||||
setOnFlowPage: (onFlowPage: boolean) => void;
|
||||
flowPool: FlowPoolType;
|
||||
inputs: Array<{ type: string; id: string; displayName: string }>;
|
||||
outputs: Array<{ type: string; id: string; displayName: string }>;
|
||||
|
|
@ -74,8 +76,8 @@ export type FlowStoreType = {
|
|||
edges: Edge[];
|
||||
onNodesChange: OnNodesChange;
|
||||
onEdgesChange: OnEdgesChange;
|
||||
setNodes: (update: Node[] | ((oldState: Node[]) => Node[])) => void;
|
||||
setEdges: (update: Edge[] | ((oldState: Edge[]) => Edge[])) => void;
|
||||
setNodes: (update: Node[] | ((oldState: Node[]) => Node[]),skipSave?:boolean) => void;
|
||||
setEdges: (update: Edge[] | ((oldState: Edge[]) => Edge[]),skipSave?:boolean) => void;
|
||||
setNode: (id: string, update: Node | ((oldState: Node) => Node)) => void;
|
||||
getNode: (id: string) => Node | undefined;
|
||||
deleteNode: (nodeId: string | Array<string>) => void;
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ import { Edge, Node, Viewport, XYPosition } from "reactflow";
|
|||
import { FlowType } from "../../flow";
|
||||
|
||||
export type FlowsManagerStoreType = {
|
||||
getFlowById: (id: string) => FlowType | undefined;
|
||||
flows: Array<FlowType>;
|
||||
setFlows: (flows: FlowType[]) => void;
|
||||
currentFlow: FlowType | undefined;
|
||||
|
|
@ -50,6 +51,7 @@ export type FlowsManagerStoreType = {
|
|||
takeSnapshot: () => void;
|
||||
examples: Array<FlowType>;
|
||||
setExamples: (examples: FlowType[]) => void;
|
||||
setCurrentFlow: (flow: FlowType) => void;
|
||||
};
|
||||
|
||||
export type UseUndoRedoOptions = {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import { AxiosError } from "axios";
|
||||
import { Edge, Node } from "reactflow";
|
||||
import { BuildStatus } from "../constants/enums";
|
||||
import { getVerticesOrder, postBuildVertex } from "../controllers/API";
|
||||
import useAlertStore from "../stores/alertStore";
|
||||
|
|
@ -21,6 +22,8 @@ type BuildVerticesParams = {
|
|||
onBuildError?: (title, list, idList: VertexLayerElementType[]) => void;
|
||||
onBuildStart?: (idList: VertexLayerElementType[]) => void;
|
||||
onValidateNodes?: (nodes: string[]) => void;
|
||||
nodes?: Node[];
|
||||
edges?: Edge[];
|
||||
};
|
||||
|
||||
function getInactiveVertexData(vertexId: string): VertexBuildTypeAPI {
|
||||
|
|
@ -48,7 +51,9 @@ function getInactiveVertexData(vertexId: string): VertexBuildTypeAPI {
|
|||
export async function updateVerticesOrder(
|
||||
flowId: string,
|
||||
startNodeId?: string | null,
|
||||
stopNodeId?: string | null
|
||||
stopNodeId?: string | null,
|
||||
nodes?: Node[],
|
||||
edges?: Edge[]
|
||||
): Promise<{
|
||||
verticesLayers: VertexLayerElementType[][];
|
||||
verticesIds: string[];
|
||||
|
|
@ -59,12 +64,19 @@ export async function updateVerticesOrder(
|
|||
const setErrorData = useAlertStore.getState().setErrorData;
|
||||
let orderResponse;
|
||||
try {
|
||||
orderResponse = await getVerticesOrder(flowId, startNodeId, stopNodeId);
|
||||
orderResponse = await getVerticesOrder(
|
||||
flowId,
|
||||
startNodeId,
|
||||
stopNodeId,
|
||||
nodes,
|
||||
edges
|
||||
);
|
||||
} catch (error: any) {
|
||||
setErrorData({
|
||||
title: "Oops! Looks like you missed something",
|
||||
list: [error.response?.data?.detail ?? "Unknown Error"],
|
||||
});
|
||||
debugger;
|
||||
useFlowStore.getState().setIsBuilding(false);
|
||||
throw new Error("Invalid nodes");
|
||||
}
|
||||
|
|
@ -101,6 +113,8 @@ export async function buildVertices({
|
|||
onBuildError,
|
||||
onBuildStart,
|
||||
onValidateNodes,
|
||||
nodes,
|
||||
edges,
|
||||
}: BuildVerticesParams) {
|
||||
let verticesBuild = useFlowStore.getState().verticesBuild;
|
||||
// if startNodeId and stopNodeId are provided
|
||||
|
|
@ -113,7 +127,9 @@ export async function buildVertices({
|
|||
let verticesOrderResponse = await updateVerticesOrder(
|
||||
flowId,
|
||||
startNodeId,
|
||||
stopNodeId
|
||||
stopNodeId,
|
||||
nodes,
|
||||
edges
|
||||
);
|
||||
if (onValidateNodes) {
|
||||
try {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { cloneDeep } from "lodash";
|
||||
import { cloneDeep, uniqueId } from "lodash";
|
||||
import { Node } from "reactflow";
|
||||
import { FlowType, NodeDataType } from "../types/flow";
|
||||
import { isInputNode, isOutputNode } from "./reactflowUtils";
|
||||
|
|
@ -6,11 +6,17 @@ import { isInputNode, isOutputNode } from "./reactflowUtils";
|
|||
export default function cloneFLowWithParent(
|
||||
flow: FlowType,
|
||||
parent: string,
|
||||
is_component: boolean
|
||||
is_component: boolean,
|
||||
keepId=false
|
||||
) {
|
||||
let childFLow = cloneDeep(flow);
|
||||
childFLow.parent = parent;
|
||||
childFLow.id = "";
|
||||
if(!keepId){
|
||||
childFLow.id = "";
|
||||
}
|
||||
else{
|
||||
childFLow.id = uniqueId()+"-"+childFLow.id;
|
||||
}
|
||||
childFLow.is_component = is_component;
|
||||
return childFLow;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -141,6 +141,7 @@ import {
|
|||
X,
|
||||
XCircle,
|
||||
Zap,
|
||||
PlaySquare
|
||||
} from "lucide-react";
|
||||
import { FaApple, FaGithub } from "react-icons/fa";
|
||||
import { AWSIcon } from "../icons/AWS";
|
||||
|
|
@ -149,7 +150,7 @@ import { AnthropicIcon } from "../icons/Anthropic";
|
|||
import { AstraDBIcon } from "../icons/AstraDB";
|
||||
import { AzureIcon } from "../icons/Azure";
|
||||
import { BingIcon } from "../icons/Bing";
|
||||
import { BotMessageSquareIcon } from "../icons/BotMessageSquare";
|
||||
import { BotMessageSquareIcon} from "../icons/BotMessageSquare";
|
||||
import { ChromaIcon } from "../icons/ChromaIcon";
|
||||
import { CohereIcon } from "../icons/Cohere";
|
||||
import { ElasticsearchIcon } from "../icons/ElasticsearchStore";
|
||||
|
|
@ -297,7 +298,8 @@ export const nodeIconsLucide: iconsType = {
|
|||
ListFlows: Group,
|
||||
ClearMessageHistory: FileClock,
|
||||
Python: PythonIcon,
|
||||
ChatOutput: BotMessageSquareIcon,
|
||||
ChatOutput: MessagesSquare,
|
||||
BotMessageSquareIcon,
|
||||
ChatInput: MessagesSquare,
|
||||
inputs: Download,
|
||||
outputs: Upload,
|
||||
|
|
|
|||
|
|
@ -61,7 +61,7 @@ export function normalCaseToSnakeCase(str: string): string {
|
|||
|
||||
export function toTitleCase(
|
||||
str: string | undefined,
|
||||
isNodeField?: boolean
|
||||
isNodeField?: boolean,
|
||||
): string {
|
||||
if (!str) return "";
|
||||
let result = str
|
||||
|
|
@ -70,7 +70,7 @@ export function toTitleCase(
|
|||
if (isNodeField) return word;
|
||||
if (index === 0) {
|
||||
return checkUpperWords(
|
||||
word[0].toUpperCase() + word.slice(1).toLowerCase()
|
||||
word[0].toUpperCase() + word.slice(1).toLowerCase(),
|
||||
);
|
||||
}
|
||||
return checkUpperWords(word.toLowerCase());
|
||||
|
|
@ -83,7 +83,7 @@ export function toTitleCase(
|
|||
if (isNodeField) return word;
|
||||
if (index === 0) {
|
||||
return checkUpperWords(
|
||||
word[0].toUpperCase() + word.slice(1).toLowerCase()
|
||||
word[0].toUpperCase() + word.slice(1).toLowerCase(),
|
||||
);
|
||||
}
|
||||
return checkUpperWords(word.toLowerCase());
|
||||
|
|
@ -123,7 +123,7 @@ export function groupByFamily(
|
|||
data: APIDataType,
|
||||
baseClasses: string,
|
||||
left: boolean,
|
||||
flow?: NodeType[]
|
||||
flow?: NodeType[],
|
||||
): groupedObjType[] {
|
||||
const baseClassesSet = new Set(baseClasses.split("\n"));
|
||||
let arrOfPossibleInputs: Array<{
|
||||
|
|
@ -149,7 +149,7 @@ export function groupByFamily(
|
|||
baseClassesSet.has(template.type)) ||
|
||||
(template.input_types &&
|
||||
template.input_types.some((inputType) =>
|
||||
baseClassesSet.has(inputType)
|
||||
baseClassesSet.has(inputType),
|
||||
)))
|
||||
);
|
||||
};
|
||||
|
|
@ -169,7 +169,7 @@ export function groupByFamily(
|
|||
hasBaseClassInBaseClasses:
|
||||
foundNode?.hasBaseClassInBaseClasses ||
|
||||
nodeData.node!.base_classes.some((baseClass) =>
|
||||
baseClassesSet.has(baseClass)
|
||||
baseClassesSet.has(baseClass),
|
||||
), //seta como anterior ou verifica se o node tem base class
|
||||
displayName: nodeData.node?.display_name,
|
||||
});
|
||||
|
|
@ -186,10 +186,10 @@ export function groupByFamily(
|
|||
if (!foundNode) {
|
||||
foundNode = {
|
||||
hasBaseClassInTemplate: Object.values(node!.template).some(
|
||||
checkBaseClass
|
||||
checkBaseClass,
|
||||
),
|
||||
hasBaseClassInBaseClasses: node!.base_classes.some((baseClass) =>
|
||||
baseClassesSet.has(baseClass)
|
||||
baseClassesSet.has(baseClass),
|
||||
),
|
||||
displayName: node?.display_name,
|
||||
};
|
||||
|
|
@ -246,7 +246,7 @@ export function getRandomDescription(): string {
|
|||
export function getRandomName(
|
||||
retry: number = 0,
|
||||
noSpace: boolean = false,
|
||||
maxRetries: number = 3
|
||||
maxRetries: number = 3,
|
||||
): string {
|
||||
const left: string[] = ADJECTIVES;
|
||||
const right: string[] = NOUNS;
|
||||
|
|
@ -325,7 +325,7 @@ export function getChatInputField(flowState?: FlowState) {
|
|||
export function getPythonApiCode(
|
||||
flow: FlowType,
|
||||
isAuth: boolean,
|
||||
tweak?: any[]
|
||||
tweak?: any[],
|
||||
): string {
|
||||
const flowId = flow.id;
|
||||
|
||||
|
|
@ -393,7 +393,7 @@ print(run_flow(message=message, flow_id=FLOW_ID, tweaks=TWEAKS${
|
|||
export function getCurlCode(
|
||||
flow: FlowType,
|
||||
isAuth: boolean,
|
||||
tweak?: any[]
|
||||
tweak?: any[],
|
||||
): string {
|
||||
const flowId = flow.id;
|
||||
const tweaks = buildTweaks(flow);
|
||||
|
|
@ -463,7 +463,7 @@ result = run_flow_from_json(flow="${flowName}.json",
|
|||
export function getWidgetCode(
|
||||
flow: FlowType,
|
||||
isAuth: boolean,
|
||||
flowState?: FlowState
|
||||
flowState?: FlowState,
|
||||
): string {
|
||||
const flowId = flow.id;
|
||||
const flowName = flow.name;
|
||||
|
|
@ -593,7 +593,7 @@ export function checkLocalStorageKey(key: string): boolean {
|
|||
|
||||
export function IncrementObjectKey(
|
||||
object: object,
|
||||
key: string
|
||||
key: string,
|
||||
): { newKey: string; increment: number } {
|
||||
let count = 1;
|
||||
const type = removeCountFromString(key);
|
||||
|
|
@ -665,7 +665,7 @@ export function getSetFromObject(obj: object, key?: string): Set<string> {
|
|||
|
||||
export function getFieldTitle(
|
||||
template: APITemplateType,
|
||||
templateField: string
|
||||
templateField: string,
|
||||
): string {
|
||||
return template[templateField].display_name
|
||||
? template[templateField].display_name!
|
||||
|
|
@ -715,3 +715,7 @@ export function freezeObject(obj: any) {
|
|||
if (!obj) return obj;
|
||||
return JSON.parse(JSON.stringify(obj));
|
||||
}
|
||||
|
||||
export function convertTestName(name: string): string {
|
||||
return name.replace(/ /g, "-").toLowerCase();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,7 +5,10 @@
|
|||
{
|
||||
"id": "ChatOutput-xPeM1",
|
||||
"type": "genericNode",
|
||||
"position": { "x": 231.45405028405742, "y": -109.00715949940081 },
|
||||
"position": {
|
||||
"x": 231.45405028405742,
|
||||
"y": -109.00715949940081
|
||||
},
|
||||
"data": {
|
||||
"type": "ChatOutput",
|
||||
"node": {
|
||||
|
|
@ -17,7 +20,7 @@
|
|||
"list": false,
|
||||
"show": true,
|
||||
"multiline": true,
|
||||
"value": "from typing import Optional, Union\n\nfrom langflow.base.io.chat import ChatComponent\nfrom langflow.field_typing import Text\nfrom langflow.schema import Record\n\n\nclass ChatOutput(ChatComponent):\n display_name = \"Chat Output\"\n description = \"Display a chat message in the Interaction Panel.\"\n icon = \"ChatOutput\"\n\n def build(\n self,\n sender: Optional[str] = \"Machine\",\n sender_name: Optional[str] = \"AI\",\n input_value: Optional[str] = None,\n session_id: Optional[str] = None,\n return_record: Optional[bool] = False,\n record_template: Optional[str] = \"{text}\",\n ) -> Union[Text, Record]:\n return super().build_with_record(\n sender=sender,\n sender_name=sender_name,\n input_value=input_value,\n session_id=session_id,\n return_record=return_record,\n record_template=record_template or \"\",\n )\n",
|
||||
"value": "from typing import Optional, Union\n\nfrom langflow.base.io.chat import ChatComponent\nfrom langflow.field_typing import Text\nfrom langflow.schema import Record\n\n\nclass ChatOutput(ChatComponent):\n display_name = \"Chat Output\"\n description = \"Display a chat message in the Playground.\"\n icon = \"ChatOutput\"\n\n def build(\n self,\n sender: Optional[str] = \"Machine\",\n sender_name: Optional[str] = \"AI\",\n input_value: Optional[str] = None,\n session_id: Optional[str] = None,\n return_record: Optional[bool] = False,\n record_template: Optional[str] = \"{text}\",\n ) -> Union[Text, Record]:\n return super().build_with_record(\n sender=sender,\n sender_name=sender_name,\n input_value=input_value,\n session_id=session_id,\n return_record=return_record,\n record_template=record_template or \"\",\n )\n",
|
||||
"fileTypes": [],
|
||||
"file_path": "",
|
||||
"password": false,
|
||||
|
|
@ -41,7 +44,9 @@
|
|||
"name": "input_value",
|
||||
"display_name": "Message",
|
||||
"advanced": false,
|
||||
"input_types": ["Text"],
|
||||
"input_types": [
|
||||
"Text"
|
||||
],
|
||||
"dynamic": false,
|
||||
"info": "",
|
||||
"load_from_db": false,
|
||||
|
|
@ -65,7 +70,9 @@
|
|||
"info": "In case of Message being a Record, this template will be used to convert it to text.",
|
||||
"load_from_db": false,
|
||||
"title_case": false,
|
||||
"input_types": ["Text"]
|
||||
"input_types": [
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"return_record": {
|
||||
"type": "bool",
|
||||
|
|
@ -97,7 +104,10 @@
|
|||
"fileTypes": [],
|
||||
"file_path": "",
|
||||
"password": false,
|
||||
"options": ["Machine", "User"],
|
||||
"options": [
|
||||
"Machine",
|
||||
"User"
|
||||
],
|
||||
"name": "sender",
|
||||
"display_name": "Sender Type",
|
||||
"advanced": true,
|
||||
|
|
@ -105,7 +115,9 @@
|
|||
"info": "",
|
||||
"load_from_db": false,
|
||||
"title_case": false,
|
||||
"input_types": ["Text"]
|
||||
"input_types": [
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"sender_name": {
|
||||
"type": "str",
|
||||
|
|
@ -125,7 +137,9 @@
|
|||
"info": "",
|
||||
"load_from_db": false,
|
||||
"title_case": false,
|
||||
"input_types": ["Text"]
|
||||
"input_types": [
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"session_id": {
|
||||
"type": "str",
|
||||
|
|
@ -144,13 +158,20 @@
|
|||
"info": "If provided, the message will be stored in the memory.",
|
||||
"load_from_db": false,
|
||||
"title_case": false,
|
||||
"input_types": ["Text"]
|
||||
"input_types": [
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"_type": "CustomComponent"
|
||||
},
|
||||
"description": "Display a chat message in the Interaction Panel.",
|
||||
"description": "Display a chat message in the Playground.",
|
||||
"icon": "ChatOutput",
|
||||
"base_classes": ["object", "Record", "str", "Text"],
|
||||
"base_classes": [
|
||||
"object",
|
||||
"Record",
|
||||
"str",
|
||||
"Text"
|
||||
],
|
||||
"display_name": "Chat Output",
|
||||
"documentation": "",
|
||||
"custom_fields": {
|
||||
|
|
@ -161,7 +182,10 @@
|
|||
"return_record": null,
|
||||
"record_template": null
|
||||
},
|
||||
"output_types": ["Text", "Record"],
|
||||
"output_types": [
|
||||
"Text",
|
||||
"Record"
|
||||
],
|
||||
"field_formatters": {},
|
||||
"frozen": false,
|
||||
"field_order": [],
|
||||
|
|
@ -181,7 +205,10 @@
|
|||
{
|
||||
"id": "ChatInput-XYvUc",
|
||||
"type": "genericNode",
|
||||
"position": { "x": -389.67919096408036, "y": 10.79598792234681 },
|
||||
"position": {
|
||||
"x": -389.67919096408036,
|
||||
"y": 10.79598792234681
|
||||
},
|
||||
"data": {
|
||||
"type": "ChatInput",
|
||||
"node": {
|
||||
|
|
@ -193,7 +220,7 @@
|
|||
"list": false,
|
||||
"show": true,
|
||||
"multiline": true,
|
||||
"value": "from typing import Optional, Union\n\nfrom langflow.base.io.chat import ChatComponent\nfrom langflow.field_typing import Text\nfrom langflow.schema import Record\n\n\nclass ChatInput(ChatComponent):\n display_name = \"Chat Input\"\n description = \"Get chat inputs from the Interaction Panel.\"\n icon = \"ChatInput\"\n\n def build_config(self):\n build_config = super().build_config()\n build_config[\"input_value\"] = {\n \"input_types\": [],\n \"display_name\": \"Message\",\n \"multiline\": True,\n }\n\n return build_config\n\n def build(\n self,\n sender: Optional[str] = \"User\",\n sender_name: Optional[str] = \"User\",\n input_value: Optional[str] = None,\n session_id: Optional[str] = None,\n return_record: Optional[bool] = False,\n ) -> Union[Text, Record]:\n return super().build_no_record(\n sender=sender,\n sender_name=sender_name,\n input_value=input_value,\n session_id=session_id,\n return_record=return_record,\n )\n",
|
||||
"value": "from typing import Optional, Union\n\nfrom langflow.base.io.chat import ChatComponent\nfrom langflow.field_typing import Text\nfrom langflow.schema import Record\n\n\nclass ChatInput(ChatComponent):\n display_name = \"Chat Input\"\n description = \"Get chat inputs from the Playground.\"\n icon = \"ChatInput\"\n\n def build_config(self):\n build_config = super().build_config()\n build_config[\"input_value\"] = {\n \"input_types\": [],\n \"display_name\": \"Message\",\n \"multiline\": True,\n }\n\n return build_config\n\n def build(\n self,\n sender: Optional[str] = \"User\",\n sender_name: Optional[str] = \"User\",\n input_value: Optional[str] = None,\n session_id: Optional[str] = None,\n return_record: Optional[bool] = False,\n ) -> Union[Text, Record]:\n return super().build_no_record(\n sender=sender,\n sender_name=sender_name,\n input_value=input_value,\n session_id=session_id,\n return_record=return_record,\n )\n",
|
||||
"fileTypes": [],
|
||||
"file_path": "",
|
||||
"password": false,
|
||||
|
|
@ -253,7 +280,10 @@
|
|||
"fileTypes": [],
|
||||
"file_path": "",
|
||||
"password": false,
|
||||
"options": ["Machine", "User"],
|
||||
"options": [
|
||||
"Machine",
|
||||
"User"
|
||||
],
|
||||
"name": "sender",
|
||||
"display_name": "Sender Type",
|
||||
"advanced": true,
|
||||
|
|
@ -261,7 +291,9 @@
|
|||
"info": "",
|
||||
"load_from_db": false,
|
||||
"title_case": false,
|
||||
"input_types": ["Text"]
|
||||
"input_types": [
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"sender_name": {
|
||||
"type": "str",
|
||||
|
|
@ -281,7 +313,9 @@
|
|||
"info": "",
|
||||
"load_from_db": false,
|
||||
"title_case": false,
|
||||
"input_types": ["Text"]
|
||||
"input_types": [
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"session_id": {
|
||||
"type": "str",
|
||||
|
|
@ -300,13 +334,20 @@
|
|||
"info": "If provided, the message will be stored in the memory.",
|
||||
"load_from_db": false,
|
||||
"title_case": false,
|
||||
"input_types": ["Text"]
|
||||
"input_types": [
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"_type": "CustomComponent"
|
||||
},
|
||||
"description": "Get chat inputs from the Interaction Panel.",
|
||||
"description": "Get chat inputs from the Playground.",
|
||||
"icon": "ChatInput",
|
||||
"base_classes": ["object", "Record", "str", "Text"],
|
||||
"base_classes": [
|
||||
"object",
|
||||
"Record",
|
||||
"str",
|
||||
"Text"
|
||||
],
|
||||
"display_name": "Chat Input",
|
||||
"documentation": "",
|
||||
"custom_fields": {
|
||||
|
|
@ -316,7 +357,10 @@
|
|||
"session_id": null,
|
||||
"return_record": null
|
||||
},
|
||||
"output_types": ["Text", "Record"],
|
||||
"output_types": [
|
||||
"Text",
|
||||
"Record"
|
||||
],
|
||||
"field_formatters": {},
|
||||
"frozen": false,
|
||||
"field_order": [],
|
||||
|
|
@ -339,16 +383,25 @@
|
|||
"targetHandle": {
|
||||
"fieldName": "input_value",
|
||||
"id": "ChatOutput-xPeM1",
|
||||
"inputTypes": ["Text"],
|
||||
"inputTypes": [
|
||||
"Text"
|
||||
],
|
||||
"type": "str"
|
||||
},
|
||||
"sourceHandle": {
|
||||
"baseClasses": ["object", "Record", "str", "Text"],
|
||||
"baseClasses": [
|
||||
"object",
|
||||
"Record",
|
||||
"str",
|
||||
"Text"
|
||||
],
|
||||
"dataType": "ChatInput",
|
||||
"id": "ChatInput-XYvUc"
|
||||
}
|
||||
},
|
||||
"style": { "stroke": "#555" },
|
||||
"style": {
|
||||
"stroke": "#555"
|
||||
},
|
||||
"className": "stroke-gray-900 stroke-connection",
|
||||
"id": "reactflow__edge-ChatInput-XYvUc{œbaseClassesœ:[œobjectœ,œRecordœ,œstrœ,œTextœ],œdataTypeœ:œChatInputœ,œidœ:œChatInput-XYvUcœ}-ChatOutput-xPeM1{œfieldNameœ:œinput_valueœ,œidœ:œChatOutput-xPeM1œ,œinputTypesœ:[œTextœ],œtypeœ:œstrœ}"
|
||||
}
|
||||
|
|
@ -363,4 +416,4 @@
|
|||
"name": "ChatTest",
|
||||
"last_tested_version": "1.0.0a14",
|
||||
"is_component": false
|
||||
}
|
||||
}
|
||||
|
|
@ -45,7 +45,7 @@ test("user must interact with chat with Input/Output", async ({ page }) => {
|
|||
await page
|
||||
.getByTestId("input-openai_api_key")
|
||||
.fill(process.env.OPENAI_API_KEY ?? "");
|
||||
await page.getByText("Run", { exact: true }).click();
|
||||
await page.getByText("Playground", { exact: true }).click();
|
||||
await page.getByPlaceholder("Send a message...").fill("Hello, how are you?");
|
||||
await page.getByTestId("icon-LucideSend").click();
|
||||
let valueUser = await page.getByTestId("sender_name_user").textContent();
|
||||
|
|
@ -65,7 +65,7 @@ test("user must interact with chat with Input/Output", async ({ page }) => {
|
|||
await page.getByTestId("input-sender_name").nth(1).fill("TestSenderNameUser");
|
||||
await page.getByTestId("input-sender_name").nth(0).fill("TestSenderNameAI");
|
||||
|
||||
await page.getByText("Run", { exact: true }).click();
|
||||
await page.getByText("Playground", { exact: true }).click();
|
||||
await page.getByTestId("icon-LucideSend").click();
|
||||
|
||||
valueUser = await page
|
||||
|
|
@ -138,7 +138,7 @@ test("chat_io_teste", async ({ page }) => {
|
|||
}
|
||||
);
|
||||
await page.getByLabel("fit view").click();
|
||||
await page.getByText("Run", { exact: true }).click();
|
||||
await page.getByText("Playground", { exact: true }).click();
|
||||
await page.getByPlaceholder("Send a message...").click();
|
||||
await page.getByPlaceholder("Send a message...").fill("teste");
|
||||
await page.getByRole("button").nth(1).click();
|
||||
|
|
|
|||
46
src/frontend/tests/end-to-end/deleteComponentFlows.spec.ts
Normal file
46
src/frontend/tests/end-to-end/deleteComponentFlows.spec.ts
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
import { test } from "@playwright/test";
|
||||
|
||||
test("shoud delete a flow", async ({ page }) => {
|
||||
await page.goto("/");
|
||||
await page.waitForTimeout(2000);
|
||||
await page.getByText("Store").nth(0).click();
|
||||
await page.getByTestId("install-Website Content QA").click();
|
||||
await page.waitForTimeout(5000);
|
||||
await page.getByText("My Collection").nth(0).click();
|
||||
await page.getByText("Website Content QA").first().isVisible();
|
||||
await page
|
||||
.getByTestId("card-website-content-qa")
|
||||
.first()
|
||||
.hover()
|
||||
.then(async () => {
|
||||
await page.getByTestId("icon-Trash2").first().click();
|
||||
await page.waitForTimeout(2000);
|
||||
});
|
||||
await page.getByText("Confirm deletion of component?").isVisible();
|
||||
await page.getByText("Delete").nth(1).click();
|
||||
await page.waitForTimeout(1000);
|
||||
await page.getByText("Successfully").first().isVisible();
|
||||
});
|
||||
|
||||
test("shoud delete a component", async ({ page }) => {
|
||||
await page.goto("/");
|
||||
await page.waitForTimeout(2000);
|
||||
await page.getByText("Store").nth(0).click();
|
||||
await page.getByTestId("install-Basic RAG").click();
|
||||
await page.waitForTimeout(5000);
|
||||
await page.getByText("My Collection").nth(0).click();
|
||||
await page.getByText("Components").first().click();
|
||||
await page.getByText("Basic RAG").first().isVisible();
|
||||
await page
|
||||
.getByTestId("card-basic-rag")
|
||||
.first()
|
||||
.hover()
|
||||
.then(async () => {
|
||||
await page.getByTestId("icon-Trash2").first().click();
|
||||
await page.waitForTimeout(2000);
|
||||
});
|
||||
await page.getByText("Confirm deletion of component?").isVisible();
|
||||
await page.getByText("Delete").nth(1).click();
|
||||
await page.waitForTimeout(1000);
|
||||
await page.getByText("Successfully").first().isVisible();
|
||||
});
|
||||
|
|
@ -79,7 +79,7 @@ test("dropDownComponent", async ({ page }) => {
|
|||
// Release the mouse
|
||||
await page.mouse.up();
|
||||
|
||||
await page.getByText("Run", { exact: true }).click();
|
||||
await page.getByText("Playground", { exact: true }).click();
|
||||
await page.getByText("Run Flow", { exact: true }).click();
|
||||
await page.waitForTimeout(3000);
|
||||
const textOutput = await page.getByPlaceholder("Empty").first().inputValue();
|
||||
|
|
|
|||
|
|
@ -117,7 +117,7 @@ test("TextInputOutputComponent", async ({ page }) => {
|
|||
await page
|
||||
.getByTestId("input-openai_api_key")
|
||||
.fill(process.env.OPENAI_API_KEY ?? "");
|
||||
await page.getByText("Run", { exact: true }).click();
|
||||
await page.getByText("Playground", { exact: true }).click();
|
||||
await page.getByText("Run Flow", { exact: true }).click();
|
||||
|
||||
await page.waitForTimeout(5000);
|
||||
|
|
@ -138,7 +138,7 @@ test("TextInputOutputComponent", async ({ page }) => {
|
|||
.getByTestId("input-input_value")
|
||||
.nth(0)
|
||||
.fill("This is a test, again just to be sure!");
|
||||
await page.getByText("Run", { exact: true }).click();
|
||||
await page.getByText("Playground", { exact: true }).click();
|
||||
await page.getByText("Run Flow", { exact: true }).click();
|
||||
|
||||
await page.waitForTimeout(5000);
|
||||
|
|
|
|||
91
src/frontend/tests/end-to-end/userSettings.spec.ts
Normal file
91
src/frontend/tests/end-to-end/userSettings.spec.ts
Normal file
|
|
@ -0,0 +1,91 @@
|
|||
import { test } from "@playwright/test";
|
||||
|
||||
test("should see general profile gradient", async ({ page }) => {
|
||||
await page.goto("/");
|
||||
await page.waitForTimeout(2000);
|
||||
await page.getByTestId("user-profile-settings").click();
|
||||
await page.getByText("Settings").click();
|
||||
await page.getByText("General").nth(2).isVisible();
|
||||
await page.getByText("Profile Gradient").isVisible();
|
||||
});
|
||||
|
||||
test("should interact with global variables", async ({ page }) => {
|
||||
const randomName = Math.random().toString(36).substring(2);
|
||||
|
||||
await page.goto("/");
|
||||
await page.waitForTimeout(2000);
|
||||
await page.getByTestId("user-profile-settings").click();
|
||||
await page.getByText("Settings").click();
|
||||
await page.getByText("Global Variables").click();
|
||||
await page.getByText("Global Variables").nth(2);
|
||||
await page.getByText("Global Variables", { exact: true }).nth(1).isVisible();
|
||||
await page.getByText("Add New").click();
|
||||
await page
|
||||
.getByPlaceholder("Insert a name for the variable...")
|
||||
.fill(randomName);
|
||||
await page.getByTestId("popover-anchor-type-global-variables").click();
|
||||
await page.getByPlaceholder("Search options...").fill("Generic");
|
||||
await page.waitForTimeout(2000);
|
||||
await page.getByText("Generic", { exact: true }).last().isVisible();
|
||||
await page.getByText("Generic", { exact: true }).last().click();
|
||||
|
||||
await page.getByTestId("popover-anchor-type-global-variables").click();
|
||||
await page.waitForTimeout(2000);
|
||||
await page.getByPlaceholder("Search options...").fill("Generic");
|
||||
await page.getByText("Generic", { exact: true }).last().isVisible();
|
||||
await page.getByText("Generic", { exact: true }).last().click();
|
||||
|
||||
await page
|
||||
.getByPlaceholder("Insert a value for the variable...")
|
||||
.fill("testtesttesttesttesttesttesttest");
|
||||
await page.getByTestId("popover-anchor-apply-to-fields").click();
|
||||
await page.waitForTimeout(2000);
|
||||
|
||||
await page.getByPlaceholder("Search options...").fill("System Message");
|
||||
|
||||
await page.getByText("System Message").first().click();
|
||||
|
||||
await page.getByPlaceholder("Search options...").fill("openAI");
|
||||
|
||||
await page.getByText("OpenAI API Base").first().click();
|
||||
|
||||
await page.getByPlaceholder("Search options...").fill("llama");
|
||||
|
||||
await page.getByText("Ollama").first().click();
|
||||
|
||||
await page.keyboard.press("Escape");
|
||||
await page.getByText("Save Variable", { exact: true }).click();
|
||||
|
||||
await page.getByText(randomName).isVisible();
|
||||
|
||||
await page
|
||||
.getByLabel("Press Space to toggle all rows selection (unchecked)")
|
||||
.nth(0)
|
||||
.click();
|
||||
await page.getByTestId("icon-Trash2").click();
|
||||
await page.getByText("No Rows To Show").isVisible();
|
||||
});
|
||||
|
||||
test("should see shortcuts", async ({ page }) => {
|
||||
await page.goto("/");
|
||||
await page.waitForTimeout(2000);
|
||||
await page.getByTestId("user-profile-settings").click();
|
||||
await page.getByText("Settings").click();
|
||||
await page.getByText("General").nth(2).isVisible();
|
||||
await page.getByText("Shortcuts").nth(0).click();
|
||||
await page.getByText("Shortcuts", { exact: true }).nth(1).isVisible();
|
||||
await page
|
||||
.getByText("Advanced Settings Component", { exact: true })
|
||||
.isVisible();
|
||||
await page.getByText("Minimize Component", { exact: true }).isVisible();
|
||||
await page.getByText("Code Component", { exact: true }).isVisible();
|
||||
await page.getByText("Copy Component", { exact: true }).isVisible();
|
||||
await page.getByText("Duplicate Component", { exact: true }).isVisible();
|
||||
await page.getByText("Share Component", { exact: true }).isVisible();
|
||||
await page.getByText("Docs Component", { exact: true }).isVisible();
|
||||
await page.getByText("Save Component", { exact: true }).isVisible();
|
||||
await page.getByText("Delete Component", { exact: true }).isVisible();
|
||||
await page.getByText("Open Playground", { exact: true }).isVisible();
|
||||
await page.getByText("Undo", { exact: true }).isVisible();
|
||||
await page.getByText("Redo", { exact: true }).isVisible();
|
||||
});
|
||||
Loading…
Add table
Add a link
Reference in a new issue