zhangkai 1 jaar geleden
bovenliggende
commit
fb39b66431
78 gewijzigde bestanden met toevoegingen van 2867 en 352 verwijderingen
  1. BIN
      .DS_Store
  2. 1 0
      .gitignore
  3. 0 8
      build/assets/acebuilds-fbc0ccc6.js
  4. 0 0
      build/assets/index-e34caae6.css
  5. 0 8
      build/assets/index-f344eac6.js
  6. 0 0
      build/assets/pdfjs-36654f0a.js
  7. 0 0
      build/assets/reactdrop-61b79f7b.js
  8. 0 8
      build/assets/reactflow-10657c96.js
  9. 6 6
      build/index.html
  10. 166 1
      package-lock.json
  11. 5 1
      package.json
  12. 156 2
      public/locales/en/bs.json
  13. 171 17
      public/locales/zh/bs.json
  14. BIN
      public/points.png
  15. BIN
      src/.DS_Store
  16. 6 0
      src/App.css
  17. 3 0
      src/App.tsx
  18. BIN
      src/assets/.DS_Store
  19. BIN
      src/assets/npc/.DS_Store
  20. BIN
      src/assets/npc/gongjuAdd.png
  21. BIN
      src/assets/npc/gongjuBianji.png
  22. BIN
      src/assets/npc/gongjuIcon.png
  23. BIN
      src/assets/npc/gongjuIcon1.png
  24. BIN
      src/assets/npc/gongjuIcon3.png
  25. BIN
      src/assets/npc/nengliIcon.png
  26. BIN
      src/assets/npc/npcIcon.png
  27. BIN
      src/assets/npc/zidingyijia.png
  28. BIN
      src/assets/npc/矩形 1 拷贝 8@2x.png
  29. BIN
      src/components/.DS_Store
  30. 6 6
      src/components/bs-comp/cardComponent/index.tsx
  31. 18 11
      src/components/bs-comp/chatComponent/ChatInput.tsx
  32. 3 3
      src/components/bs-comp/chatComponent/GuideQuestions.tsx
  33. 1 1
      src/components/bs-comp/chatComponent/MessageBs.tsx
  34. 2 2
      src/components/bs-comp/chatComponent/MessageUser.tsx
  35. 104 37
      src/components/bs-comp/sheets/SkillSheet.tsx
  36. 28 11
      src/components/bs-comp/sheets/ToolsSheet.tsx
  37. BIN
      src/components/bs-ui/.DS_Store
  38. 1 1
      src/components/bs-ui/accordion/index.tsx
  39. 4 1
      src/components/bs-ui/alertDialog/useConfirm.tsx
  40. 1 1
      src/components/bs-ui/button/index.tsx
  41. 5 3
      src/components/bs-ui/input/index.tsx
  42. 2 2
      src/components/bs-ui/radio/index.tsx
  43. 2 2
      src/components/bs-ui/select/index.tsx
  44. 3 3
      src/components/bs-ui/select/multi.tsx
  45. 2 2
      src/components/bs-ui/slider/index.tsx
  46. 1 1
      src/components/bs-ui/table/index.tsx
  47. 6 6
      src/components/bs-ui/toast/toast.tsx
  48. 4 2
      src/controllers/API/flow.ts
  49. 11 3
      src/controllers/API/index.ts
  50. 30 1
      src/modals/UploadModal/upload.ts
  51. 10 5
      src/pages/ChatAppPage/chatShare.tsx
  52. 10 5
      src/pages/ChatAppPage/index.tsx
  53. 10 5
      src/pages/ChatAppPage/mobile/chatShareM.tsx
  54. 24 16
      src/pages/SkillPage/components/CreateAssistant.tsx
  55. 3 3
      src/pages/SkillPage/components/CreateTemp.tsx
  56. 46 46
      src/pages/SkillPage/components/EditTool.tsx
  57. 20 12
      src/pages/SkillPage/components/ToolItem.tsx
  58. 255 0
      src/pages/SkillPage/components/editAssistant/AutoPromptDialog.tsx
  59. 103 0
      src/pages/SkillPage/components/editAssistant/EditAssistantDialog.tsx
  60. 58 0
      src/pages/SkillPage/components/editAssistant/Header.tsx
  61. 37 0
      src/pages/SkillPage/components/editAssistant/KnowledgeBaseMulti.tsx
  62. 29 0
      src/pages/SkillPage/components/editAssistant/ModelSelect.tsx
  63. 45 0
      src/pages/SkillPage/components/editAssistant/Prompt.tsx
  64. 302 0
      src/pages/SkillPage/components/editAssistant/Setting.tsx
  65. 21 0
      src/pages/SkillPage/components/editAssistant/Temperature.tsx
  66. 55 0
      src/pages/SkillPage/components/editAssistant/TestChat.tsx
  67. 127 0
      src/pages/SkillPage/editAssistant.tsx
  68. 18 9
      src/pages/SkillPage/l2Edit.tsx
  69. 144 12
      src/pages/SkillPage/tabAssistant.tsx
  70. 108 61
      src/pages/SkillPage/tabSkills.tsx
  71. 37 19
      src/pages/SkillPage/tabTools.tsx
  72. 6 2
      src/pages/SkillPage/temps.tsx
  73. 11 0
      src/routes.tsx
  74. 77 0
      src/store/assistantStore.tsx
  75. 267 0
      src/store/diffFlowStore.tsx
  76. 293 7
      src/style/zk.scss
  77. 2 0
      src/types/flow/index.ts
  78. 1 0
      src/util/hook.ts

BIN
.DS_Store


+ 1 - 0
.gitignore

@@ -1 +1,2 @@
 node_modules
+build

File diff suppressed because it is too large
+ 0 - 8
build/assets/acebuilds-fbc0ccc6.js


File diff suppressed because it is too large
+ 0 - 0
build/assets/index-e34caae6.css


File diff suppressed because it is too large
+ 0 - 8
build/assets/index-f344eac6.js


File diff suppressed because it is too large
+ 0 - 0
build/assets/pdfjs-36654f0a.js


File diff suppressed because it is too large
+ 0 - 0
build/assets/reactdrop-61b79f7b.js


File diff suppressed because it is too large
+ 0 - 8
build/assets/reactflow-10657c96.js


+ 6 - 6
build/index.html

@@ -8,12 +8,12 @@
     <link rel="icon" href="/favicon.ico" />
     <script src="/node_modules/ace-builds/src-min-noconflict/ace.js" type="text/javascript"></script>
     <title>NPCs</title>
-  <script type="module" crossorigin src="/assets/index-f344eac6.js"></script>
-  <link rel="modulepreload" crossorigin href="/assets/acebuilds-fbc0ccc6.js">
-  <link rel="modulepreload" crossorigin href="/assets/reactflow-10657c96.js">
-  <link rel="modulepreload" crossorigin href="/assets/reactdrop-61b79f7b.js">
-  <link rel="modulepreload" crossorigin href="/assets/pdfjs-36654f0a.js">
-  <link rel="stylesheet" href="/assets/index-e34caae6.css">
+  <script type="module" crossorigin src="/assets/index-5bc9ca75.js"></script>
+  <link rel="modulepreload" crossorigin href="/assets/acebuilds-3ee1f507.js">
+  <link rel="modulepreload" crossorigin href="/assets/reactflow-0712174e.js">
+  <link rel="modulepreload" crossorigin href="/assets/reactdrop-bf2f88a4.js">
+  <link rel="modulepreload" crossorigin href="/assets/pdfjs-e8782419.js">
+  <link rel="stylesheet" href="/assets/index-a78847ea.css">
 </head>
 
 <body id='body' style="width: 100%; height:100%">

+ 166 - 1
package-lock.json

@@ -28,9 +28,11 @@
         "@radix-ui/react-radio-group": "^1.1.3",
         "@radix-ui/react-select": "^2.0.0",
         "@radix-ui/react-separator": "^1.0.3",
+        "@radix-ui/react-slider": "^1.1.2",
         "@radix-ui/react-slot": "^1.0.2",
         "@radix-ui/react-switch": "^1.0.3",
         "@radix-ui/react-tabs": "^1.0.4",
+        "@radix-ui/react-toast": "^1.1.5",
         "@radix-ui/react-toggle": "^1.0.3",
         "@radix-ui/react-toggle-group": "^1.0.4",
         "@radix-ui/react-tooltip": "^1.0.6",
@@ -85,7 +87,9 @@
         "tailwindcss-animate": "^1.0.7",
         "uuid": "^9.0.0",
         "vite-plugin-svgr": "^3.2.0",
-        "web-vitals": "^2.1.4"
+        "web-vitals": "^2.1.4",
+        "xlsx": "^0.18.5",
+        "zustand": "^4.5.2"
       },
       "devDependencies": {
         "@swc/cli": "^0.1.62",
@@ -2610,6 +2614,39 @@
         }
       }
     },
+    "node_modules/@radix-ui/react-slider": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmmirror.com/@radix-ui/react-slider/-/react-slider-1.1.2.tgz",
+      "integrity": "sha512-NKs15MJylfzVsCagVSWKhGGLNR1W9qWs+HtgbmjjVUB3B9+lb3PYoXxVju3kOrpf0VKyVCtZp+iTwVoqpa1Chw==",
+      "dependencies": {
+        "@babel/runtime": "^7.13.10",
+        "@radix-ui/number": "1.0.1",
+        "@radix-ui/primitive": "1.0.1",
+        "@radix-ui/react-collection": "1.0.3",
+        "@radix-ui/react-compose-refs": "1.0.1",
+        "@radix-ui/react-context": "1.0.1",
+        "@radix-ui/react-direction": "1.0.1",
+        "@radix-ui/react-primitive": "1.0.3",
+        "@radix-ui/react-use-controllable-state": "1.0.1",
+        "@radix-ui/react-use-layout-effect": "1.0.1",
+        "@radix-ui/react-use-previous": "1.0.1",
+        "@radix-ui/react-use-size": "1.0.1"
+      },
+      "peerDependencies": {
+        "@types/react": "*",
+        "@types/react-dom": "*",
+        "react": "^16.8 || ^17.0 || ^18.0",
+        "react-dom": "^16.8 || ^17.0 || ^18.0"
+      },
+      "peerDependenciesMeta": {
+        "@types/react": {
+          "optional": true
+        },
+        "@types/react-dom": {
+          "optional": true
+        }
+      }
+    },
     "node_modules/@radix-ui/react-slot": {
       "version": "1.0.2",
       "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.0.2.tgz",
@@ -2687,6 +2724,40 @@
         }
       }
     },
+    "node_modules/@radix-ui/react-toast": {
+      "version": "1.1.5",
+      "resolved": "https://registry.npmmirror.com/@radix-ui/react-toast/-/react-toast-1.1.5.tgz",
+      "integrity": "sha512-fRLn227WHIBRSzuRzGJ8W+5YALxofH23y0MlPLddaIpLpCDqdE0NZlS2NRQDRiptfxDeeCjgFIpexB1/zkxDlw==",
+      "dependencies": {
+        "@babel/runtime": "^7.13.10",
+        "@radix-ui/primitive": "1.0.1",
+        "@radix-ui/react-collection": "1.0.3",
+        "@radix-ui/react-compose-refs": "1.0.1",
+        "@radix-ui/react-context": "1.0.1",
+        "@radix-ui/react-dismissable-layer": "1.0.5",
+        "@radix-ui/react-portal": "1.0.4",
+        "@radix-ui/react-presence": "1.0.1",
+        "@radix-ui/react-primitive": "1.0.3",
+        "@radix-ui/react-use-callback-ref": "1.0.1",
+        "@radix-ui/react-use-controllable-state": "1.0.1",
+        "@radix-ui/react-use-layout-effect": "1.0.1",
+        "@radix-ui/react-visually-hidden": "1.0.3"
+      },
+      "peerDependencies": {
+        "@types/react": "*",
+        "@types/react-dom": "*",
+        "react": "^16.8 || ^17.0 || ^18.0",
+        "react-dom": "^16.8 || ^17.0 || ^18.0"
+      },
+      "peerDependenciesMeta": {
+        "@types/react": {
+          "optional": true
+        },
+        "@types/react-dom": {
+          "optional": true
+        }
+      }
+    },
     "node_modules/@radix-ui/react-toggle": {
       "version": "1.0.3",
       "resolved": "https://registry.npmjs.org/@radix-ui/react-toggle/-/react-toggle-1.0.3.tgz",
@@ -4555,6 +4626,14 @@
       "resolved": "https://registry.npmjs.org/add/-/add-2.0.6.tgz",
       "integrity": "sha512-j5QzrmsokwWWp6kUcJQySpbG+xfOBqqKnup3OIk1pz+kB/80SLorZ9V8zHFLO92Lcd+hbvq8bT+zOGoPkmBV0Q=="
     },
+    "node_modules/adler-32": {
+      "version": "1.3.1",
+      "resolved": "https://registry.npmmirror.com/adler-32/-/adler-32-1.3.1.tgz",
+      "integrity": "sha512-ynZ4w/nUUv5rrsR8UUGoe1VC9hZj6V5hU9Qw1HlMDJGEJw5S7TfTErWTjMys6M7vr0YWcPqs3qAr4ss0nDfP+A==",
+      "engines": {
+        "node": ">=0.8"
+      }
+    },
     "node_modules/agent-base": {
       "version": "6.0.2",
       "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz",
@@ -5415,6 +5494,18 @@
         "url": "https://github.com/sponsors/wooorm"
       }
     },
+    "node_modules/cfb": {
+      "version": "1.2.2",
+      "resolved": "https://registry.npmmirror.com/cfb/-/cfb-1.2.2.tgz",
+      "integrity": "sha512-KfdUZsSOw19/ObEWasvBP/Ac4reZvAGauZhs6S/gqNhXhI7cKwvlH7ulj+dOEYnca4bm4SGo8C1bTAQvnTjgQA==",
+      "dependencies": {
+        "adler-32": "~1.3.0",
+        "crc-32": "~1.2.0"
+      },
+      "engines": {
+        "node": ">=0.8"
+      }
+    },
     "node_modules/chalk": {
       "version": "4.1.2",
       "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
@@ -5846,6 +5937,14 @@
       "resolved": "https://registry.npmjs.org/code-block-writer/-/code-block-writer-12.0.0.tgz",
       "integrity": "sha512-q4dMFMlXtKR3XNBHyMHt/3pwYNA69EDk00lloMOaaUMKPUXBw6lpXtbu3MMVG6/uOihGnRDOlkyqsONEUj60+w=="
     },
+    "node_modules/codepage": {
+      "version": "1.15.0",
+      "resolved": "https://registry.npmmirror.com/codepage/-/codepage-1.15.0.tgz",
+      "integrity": "sha512-3g6NUTPd/YtuuGrhMnOMRjFc+LJw/bnMp3+0r/Wcz3IXUuCosKRJvMphm5+Q+bvTVGcJJuRvVLuYba+WojaFaA==",
+      "engines": {
+        "node": ">=0.8"
+      }
+    },
     "node_modules/color-convert": {
       "version": "2.0.1",
       "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
@@ -5978,6 +6077,17 @@
         "node": ">=10"
       }
     },
+    "node_modules/crc-32": {
+      "version": "1.2.2",
+      "resolved": "https://registry.npmmirror.com/crc-32/-/crc-32-1.2.2.tgz",
+      "integrity": "sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==",
+      "bin": {
+        "crc32": "bin/crc32.njs"
+      },
+      "engines": {
+        "node": ">=0.8"
+      }
+    },
     "node_modules/cross-fetch": {
       "version": "4.0.0",
       "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-4.0.0.tgz",
@@ -7286,6 +7396,14 @@
         "node": ">=12.20.0"
       }
     },
+    "node_modules/frac": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmmirror.com/frac/-/frac-1.1.2.tgz",
+      "integrity": "sha512-w/XBfkibaTl3YDqASwfDUqkna4Z2p9cFSr1aHDt0WoMTECnRfBOv2WArlZILlqgWlmdIlALXGpM2AOhEk5W3IA==",
+      "engines": {
+        "node": ">=0.8"
+      }
+    },
     "node_modules/fraction.js": {
       "version": "4.3.7",
       "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.3.7.tgz",
@@ -15254,6 +15372,17 @@
         "node": ">=0.10.0"
       }
     },
+    "node_modules/ssf": {
+      "version": "0.11.2",
+      "resolved": "https://registry.npmmirror.com/ssf/-/ssf-0.11.2.tgz",
+      "integrity": "sha512-+idbmIXoYET47hH+d7dfm2epdOMUDjqcB4648sTZ+t2JwoyBFL/insLfB/racrDmsKB3diwsDA696pZMieAC5g==",
+      "dependencies": {
+        "frac": "~1.1.2"
+      },
+      "engines": {
+        "node": ">=0.8"
+      }
+    },
     "node_modules/stdin-discarder": {
       "version": "0.1.0",
       "resolved": "https://registry.npmjs.org/stdin-discarder/-/stdin-discarder-0.1.0.tgz",
@@ -16924,6 +17053,22 @@
         "string-width": "^1.0.2 || 2 || 3 || 4"
       }
     },
+    "node_modules/wmf": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmmirror.com/wmf/-/wmf-1.0.2.tgz",
+      "integrity": "sha512-/p9K7bEh0Dj6WbXg4JG0xvLQmIadrner1bi45VMJTfnbVHsc7yIajZyoSoK60/dtVBs12Fm6WkUI5/3WAVsNMw==",
+      "engines": {
+        "node": ">=0.8"
+      }
+    },
+    "node_modules/word": {
+      "version": "0.3.0",
+      "resolved": "https://registry.npmmirror.com/word/-/word-0.3.0.tgz",
+      "integrity": "sha512-OELeY0Q61OXpdUfTp+oweA/vtLVg5VDOXh+3he3PNzLGG/y0oylSOC1xRVj0+l4vQ3tj/bB1HVHv1ocXkQceFA==",
+      "engines": {
+        "node": ">=0.8"
+      }
+    },
     "node_modules/wrap-ansi": {
       "version": "7.0.0",
       "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
@@ -16984,6 +17129,26 @@
         }
       }
     },
+    "node_modules/xlsx": {
+      "version": "0.18.5",
+      "resolved": "https://registry.npmmirror.com/xlsx/-/xlsx-0.18.5.tgz",
+      "integrity": "sha512-dmg3LCjBPHZnQp5/F/+nnTa+miPJxUXB6vtk42YjBBKayDNagxGEeIdWApkYPOf3Z3pm3k62Knjzp7lMeTEtFQ==",
+      "dependencies": {
+        "adler-32": "~1.3.0",
+        "cfb": "~1.2.1",
+        "codepage": "~1.15.0",
+        "crc-32": "~1.2.1",
+        "ssf": "~0.11.2",
+        "wmf": "~1.0.1",
+        "word": "~0.3.0"
+      },
+      "bin": {
+        "xlsx": "bin/xlsx.njs"
+      },
+      "engines": {
+        "node": ">=0.8"
+      }
+    },
     "node_modules/xml-name-validator": {
       "version": "4.0.0",
       "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-4.0.0.tgz",

+ 5 - 1
package.json

@@ -23,9 +23,11 @@
     "@radix-ui/react-radio-group": "^1.1.3",
     "@radix-ui/react-select": "^2.0.0",
     "@radix-ui/react-separator": "^1.0.3",
+    "@radix-ui/react-slider": "^1.1.2",
     "@radix-ui/react-slot": "^1.0.2",
     "@radix-ui/react-switch": "^1.0.3",
     "@radix-ui/react-tabs": "^1.0.4",
+    "@radix-ui/react-toast": "^1.1.5",
     "@radix-ui/react-toggle": "^1.0.3",
     "@radix-ui/react-toggle-group": "^1.0.4",
     "@radix-ui/react-tooltip": "^1.0.6",
@@ -80,7 +82,9 @@
     "tailwindcss-animate": "^1.0.7",
     "uuid": "^9.0.0",
     "vite-plugin-svgr": "^3.2.0",
-    "web-vitals": "^2.1.4"
+    "web-vitals": "^2.1.4",
+    "xlsx": "^0.18.5",
+    "zustand": "^4.5.2"
   },
   "scripts": {
     "dev:docker": "vite --host 0.0.0.0",

+ 156 - 2
public/locales/en/bs.json

@@ -57,7 +57,11 @@
     },
     "skills": {
         "manageTemplate": "Manage Skill Templates",
-        "createNew": "Create New",
+        "createNew": "Create New Skill",
+        "customSkills": "Custom Skills",
+        "chooseOnline": "Choose the online version",
+        "executionSteps": "Skills are visualized through process orchestration to clarify task execution steps",
+        "sceneTemplates": "We provide scene templates for your use and reference",
         "manageProjects": "Manage your personal projects here, including skill deployment and editing",
         "skillSearch": "Skill Search",
         "confirmDeleteSkill": "Confirm the deletion of this skill?",
@@ -80,6 +84,7 @@
         "skillDescTooLong": "Skill description is too long, should not exceed 200 characters",
         "errorTitle": "Key Information Error",
         "onlineFailure": "Online failure",
+        "onlineSuccessful": "Online Successful",
         "custom": "Custom",
         "skillTemplate": "Skill Template",
         "skillTemplateChoose": "You can choose a template from here to start, or create an advanced template",
@@ -92,7 +97,23 @@
         "createdBy": "Created by",
         "offline": "Offline",
         "online": "Online",
-        "guideWords": "Guide Words"
+        "guideWords": "Guide Words",
+        "currentVersion": "Current Version: ",
+        "importLocal": "Import local components",
+        "save": "Saved",
+        "import": "Import",
+        "export": "Export",
+        "code": "Code",
+        "simplify": "Simplify",
+        "saveVersion": "SaveVersion",
+        "deleteOrNot": "Do you want to delete",
+        "version": "version",
+        "saveSuccessful": "Save Successful",
+        "supportVersions": "Support splitting into multiple version branches for development and comparison between versions.",
+        "guideQuestions50": "Guide questions to a maximum of 50 characters",
+        "promptWords1000": "The maximum number of prompt words is 1000 characters",
+        "contactAdmin": "Please contact the administrator's online assistant",
+        "deleteSure": "Are you sure you want to delete this tool?"
     },
     "chat": {
         "newChat": "New Chat",
@@ -422,6 +443,139 @@
     "previousPage": "Previous Page",
     "nextPage": "Next Page",
     "formatError": "Format Error",
+    "port": "PORT",
+    "cancle": "Cancle",
+    "tip": "Prompt",
+    "deleteAssistant": "Are you sure to delete this assistant?",
+    "build": {
+        "create": "Create",
+        "assistant": "Assistant",
+        "skill": "Skills",
+        "tools": "Tools",
+        "save": "Save",
+        "online": "Online",
+        "retry": "Retry",
+        "use": "Use",
+        "useAll": "Use All",
+        "assistantConfiguration": "Assistant Configuration",
+        "createAssistant": "Create a New Assistant",
+        "assistantPortrait": "Assistant Portrait",
+        "portraitOptimization": "Assistant portrait optimization",
+        "automaticOptimization": "Automatic optimization",
+        "createDescription": "Create your assistant by describing roles and tasks",
+        "nextDescription": "Assistant can call multiple skills and tools",
+        "searchAssistant": "Search for the assistant you need",
+        "manageAssistant": "Manage your assistant on this page, including online and offline activities, editing, and more",
+        "establishAssistant": "Create Assistant",
+        "assistantName": "Assistant Name",
+        "giveAssistantName": "Give the assistant a name",
+        "whatWant": "What is the role of an assistant and what specific tasks do you want to complete?",
+        "example": "Example",
+        "exampleOne": "You are XX, with XX experience, skilled in XX, ...",
+        "exampleTwo": "Your task is XX, and you need to follow the following steps to execute it:",
+        "automaticallyConfigurations": "Automatically select relevant configurations for you",
+        "prompt": "Provide a detailed and specific description of the interaction between the assistant and the user, such as the assistant's identity, specific methods and steps for completing tasks, tone of voice when answering questions, and what issues should be noted",
+        "openingRemarks": "Opening remarks",
+        "guidingQuestions": "Guiding Questions",
+        "promptReplaced": "Prompt word replaced",
+        "guideReplaced": "Guide word replaced",
+        "openingReplaced": "Opening word replaced",
+        "toolsReplaced": "Tools replaced",
+        "skillsReplaced": "Skills replaced",
+        "allReplaced": "All replaced",
+        "basicConfiguration": "Basic configuration",
+        "modelConfiguration": "AI Model configuration",
+        "model": "Model",
+        "temperature": "Temperature",
+        "openingIntroduction": "Opening Introduction",
+        "openingStatement": "Opening Statement",
+        "assistantMessageFormat": "The assistant will send this message at the beginning of each conversation, supports Markdown format",
+        "maximumPromptLength": "The prompt can be up to 1000 characters",
+        "recommendQuestionsForUsers": "Provide recommended questions for users to guide them to ask. When there are more than 3, 3 will be randomly selected.",
+        "maxCharacters50": "Up to 50 characters",
+        "enterGuidingQuestions": "Please enter guiding questions",
+        "knowledge": "Knowledge",
+        "knowledgeBase": "Knowledge Base",
+        "autoCall": "Auto Call",
+        "callingMethod": "Calling Method",
+        "autoCallDescription": "Retrieve and recall the added knowledge base in every conversation.",
+        "onDemandCall": "On-Demand Call",
+        "onDemandCallDescription": "Prompt to call the RecallKnowledge (copyable) method in the assistant's profile (prompt words), and only retrieve the knowledge base when needed.",
+        "createNewKnowledge": "Create New Knowledge Base",
+        "refresh": "Refresh",
+        "abilities": "Abilities",
+        "skillDescription": "Implement complex and stable business process orchestration through a visual interface, such as project planning and report analysis.",
+        "selectKnowledgeBase": "Please select a knowledge base",
+        "searchBaseName": "Search for knowledge base name",
+        "debugPreview": "Debug Preview",
+        "addTool": "Add Tool",
+        "search": "Search",
+        "empty": "Nothing here",
+        "onlineSA": "Go online skills&assistants",
+        "params": "Parameters",
+        "added": "Added",
+        "add": "Add",
+        "configurationUpdated": "Configuration updated",
+        "addSkill": "Add Skill",
+        "createSkill": "Create Skill",
+        "nameRequired": "Name cannot be empty",
+        "nameMaxLength": "Name can have at most 50 characters",
+        "descMaxLength": "Description can have at most 1000 characters",
+        "editAssistant": "Edit Assistant",
+        "enterName": "Enter a name for the assistant",
+        "assistantDesc": "Assistant Description",
+        "enterDesc": "Introduce assistant functionality, visible in conversations and assistant pages",
+        "cancel": "Cancel",
+        "confirm": "Confirm",
+        "forBetter": "For better assistant effect, the description needs to be greater than 20 words",
+        "forExample": "For example, the identity of an assistant, specific methods and steps for completing tasks, tone of voice when answering questions, and what issues to pay attention to, etc"
+    },
+    "tools1": {
+        "addTool": "Add Tool",
+        "createCustomTool": " Custom Tool",
+        "builtinTools": "Built-in Tools",
+        "customTools": "Custom Tools",
+        "search": "Search",
+        "empty": "Empty",
+        "manageCustomTools": "Manage your custom tools on this page, including creating and editing them.",
+        "name": "Name",
+        "enterToolName": "Enter tool name",
+        "openapiSchema": "OpenAPI Schema",
+        "enterOpenAPISchema": "Enter your OpenAPI schema",
+        "importFromUrl": "Import from URL",
+        "examples": "Examples",
+        "weatherJson": "Weather (JSON)",
+        "petShopYaml": "Pet Shop (YAML)",
+        "authenticationType": "Authentication Type",
+        "authType": "Authentication Type",
+        "none": "None",
+        "apiKey": "API Key",
+        "basic": "Basic",
+        "bearer": "Bearer",
+        "availableTools": "Available Tools",
+        "description": "Description",
+        "method": "Method",
+        "path": "Path",
+        "delete": "Delete",
+        "cancel": "Cancel",
+        "save": "Save"
+    },
+    "test": {
+        "test": "Test",
+        "addTest": "Please add test cases first",
+        "uploadTest": "Upload test cases",
+        "explain": "The test case is the input of the current component and only supports txt files, with a maximum of 20 lines",
+        "testRun": "Test Run",
+        "testCase": "Test Case",
+        "run": "Run",
+        "downloadResults": "Download Run Results",
+        "testCases": "Enter test cases...",
+        "parametersAndValues": "Parameters and Values",
+        "parameter": "Parameter",
+        "value": "Value",
+        "result": "Test Result",
+        "outResultPlaceholder": "Click the button to output the result"
+    },
     "agents": {
         "AgentInitializer":{
             "display_name": "AgentInitializer",

+ 171 - 17
public/locales/zh/bs.json

@@ -55,40 +55,61 @@
         "language": "语言"
     },
     "skills": {
-        "manageTemplate": "管理能力模板",
-        "createNew": "新建",
-        "manageProjects": "这里管理您的个人项目,对能力上下线、编辑等等",
-        "skillSearch": "能力搜索",
-        "confirmDeleteSkill": "确认删除该能力?",
-        "backToSkillList": "返回能力列表",
-        "skillTemplateManagement": "能力模板管理,模板对所有用户可见,支持拖拽排序、删除操作",
+        "manageTemplate": "管理技能模板",
+        "createNew": "新建技能",
+        "customSkills": "自定义技能",
+        "chooseOnline": "选择上线版本",
+        "executionSteps": "技能通过可视化的流程编排,明确任务执行步骤",
+        "sceneTemplates": "我们提供场景模板供您使用和参考",
+        "manageProjects": "在此页面管理您的技能,对技能上下线、编辑等等",
+        "skillSearch": "搜索您需要的技能",
+        "confirmDeleteSkill": "确认删除该技能?",
+        "backToSkillList": "返回技能列表",
+        "skillTemplateManagement": "技能模板管理,模板对所有用户可见,支持拖拽排序、删除操作",
         "templateName": "模板名称",
         "templateDescription": "模板描述",
-        "confirmText": "是否确认删除该能力模板?",
-        "skillSettings": "能力设置",
+        "confirmText": "是否确认删除该能模板?",
+        "skillSettings": "能设置",
         "basicInfo": "基础信息",
-        "skillName": "能名称",
+        "skillName": "能名称",
         "description": "描述",
         "parameterInfo": "参数信息",
         "advancedConfiguration": "高级配置",
         "nextStep": "下一步,高级配置",
-        "skillNameRequired": "请填写能名称",
-        "skillNameTooLong": "能名称过长,不要超过30字",
+        "skillNameRequired": "请填写能名称",
+        "skillNameTooLong": "能名称过长,不要超过30字",
         "skillNameExists": "该名称已存在",
-        "skillDescRequired": "请填写能描述",
-        "skillDescTooLong": "能描述过长,不要超过200字",
+        "skillDescRequired": "请填写能描述",
+        "skillDescTooLong": "能描述过长,不要超过200字",
         "errorTitle": "关键信息有误",
         "onlineFailure": "上线失败",
+        "onlineSuccessful": "上线成功",
         "custom": "自定义",
-        "skillTemplate": "能模板",
+        "skillTemplate": "能模板",
         "skillTemplateChoose": "您可以从这里挑选一个模板开始,或者自定义高级模板",
         "createTemplate": "创建模板",
-        "createSuccessTitle": "能创建成功",
+        "createSuccessTitle": "能创建成功",
         "createFailureTitle": "创建失败",
         "createdBy": "创建用户",
         "offline": "下线",
         "online": "上线",
-        "guideWords": "引导词"
+        "guideWords": "引导词",
+        "currentVersion": "当前版本:",
+        "importLocal": "导入本地组件",
+        "save": "保存",
+        "import": "导入",
+        "export": "导出",
+        "code": "代码",
+        "simplify": "简化",
+        "saveVersion": "另存为新版本",
+        "deleteOrNot": "是否删除",
+        "version": "版本",
+        "saveSuccessful": "保存成功",
+        "supportVersions": "支持分成多个版本分支,分别进行开发以及版本间的比较。",
+        "guideQuestions50": "引导问题最多50个字符",
+        "promptWords1000": "提示词最多为1000个字符",
+        "contactAdmin": "请联系管理员上线助手",
+        "deleteSure": "确认删除该工具?"
     },
     "chat": {
         "newChat": "新建会话",
@@ -414,6 +435,139 @@
     "previousPage": "上一页",
     "nextPage": "下一页",
     "formatError": "格式错误",
+    "port": "服务端口",
+    "cancle": "取消",
+    "tip": "提示",
+    "deleteAssistant": "确认删除该助手?",
+    "build": {
+        "create": "创建",
+        "assistant": "助手",
+        "skill": "技能",
+        "tools": "工具",
+        "save": "保存",
+        "online": "上线",
+        "retry": "重试",
+        "use": "使用",
+        "useAll": "全部使用",
+        "assistantConfiguration": "助手配置",
+        "createAssistant": "新建助手",
+        "assistantPortrait": "助手画像",
+        "portraitOptimization": "助手画像优化",
+        "automaticOptimization": "自动优化",
+        "createDescription": "通过描述角色和任务来创建你的助手",
+        "nextDescription": "助手可以调用多个技能和工具",
+        "searchAssistant": "搜索您需要的助手",
+        "manageAssistant": "在此页面管理您的助手,对助手上下线、编辑等等",
+        "establishAssistant": "创建助手",
+        "assistantName": "助手名称",
+        "giveAssistantName": "给助手取一个名字",
+        "whatWant": "你希望助手的角色是什么,具体完成什么任务?",
+        "example": "示例",
+        "exampleOne": "你是 XX,具有 XX 经验,擅长 XX,…",
+        "exampleTwo": "你的任务是 XX ,需要按照以下步骤执行:",
+        "automaticallyConfigurations": "自动为您选择相关配置",
+        "prompt": "详细、具体地描述助手与用户的交互方式,例如助手的身份、完成任务的具体方法和步骤、回答问题时的语气以及应该注意什么问题等",
+        "openingRemarks": "开场白",
+        "guidingQuestions": "引导问题",
+        "promptReplaced": "提示词已替换",
+        "guideReplaced": "引导词已替换",
+        "openingReplaced": "开场白已替换",
+        "toolsReplaced": "工具已替换",
+        "skillsReplaced": "技能已替换",
+        "allReplaced": "已全部替换",
+        "basicConfiguration": "基础配置",
+        "modelConfiguration": "AI模型配置",
+        "model": "模型",
+        "temperature": "温度",
+        "openingIntroduction": "开场引导",
+        "openingStatement": "开场白",
+        "assistantMessageFormat": "助手将在每次对话开始时发送此信息,支持 Markdown 格式",
+        "maximumPromptLength": "提示词最多为1000个字符",
+        "recommendQuestionsForUsers": "为用户提供推荐问题,引导用户提问,超过3个时将随机选取3个",
+        "maxCharacters50": "最多50个字符",
+        "enterGuidingQuestions": "请输入引导问题",
+        "knowledge": "知识",
+        "knowledgeBase": "知识库",
+        "autoCall": "自动调用",
+        "callingMethod": "调用方式",
+        "autoCallDescription": "每轮对话都会对添加的知识库进行检索召回。",
+        "onDemandCall": "按需调用",
+        "onDemandCallDescription": "在助手画像(提示词)中提示调用 RecallKnowledge(可复制)方法,在有需要时才对知识库进行检索。",
+        "createNewKnowledge": "新建知识库",
+        "refresh": "刷新",
+        "abilities": "能力",
+        "skillDescription": "通过可视化界面实现复杂和稳定的业务流程编排,例如项目计划和报告分析",
+        "selectKnowledgeBase": "请选择知识库",
+        "searchBaseName": "搜索知识库名称",
+        "debugPreview": "调试预览",
+        "addTool": "添加工具",
+        "search": "搜索",
+        "empty": "空空如也",
+        "onlineSA": "上线技能&助手",
+        "params": "参数",
+        "added": "已添加",
+        "add": "添加",
+        "configurationUpdated": "配置已更新",
+        "addSkill": "添加技能",
+        "createSkill": "创建技能",
+        "nameRequired": "名称不可为空",
+        "nameMaxLength": "名称最多50个字符",
+        "descMaxLength": "最多1000个字符",
+        "editAssistant": "编辑助手",
+        "enterName": "给助手取一个名字",
+        "assistantDesc": "助手描述",
+        "enterDesc": "介绍助手功能,描述在会话和助手页面可见",
+        "cancel": "取消",
+        "confirm": "确认",
+        "forBetter": "为了更好的助手效果,描述需要大于20 个字",
+        "forExample": "例如助手的身份、完成任务的具体方法和步骤、回答问题时的语气以及应该注意什么问题等"
+    },
+    "tools1": {
+        "addTool": "添加工具",
+        "createCustomTool": "自定义工具",
+        "builtinTools": "内置工具",
+        "customTools": "自定义工具",
+        "search": "搜索",
+        "empty": "空空如也",
+        "manageCustomTools": "在此页面管理您的自定义工具,对自定义工具创建、编辑等等",
+        "name": "名称",
+        "enterToolName": "输入工具名称",
+        "openapiSchema": "OpenAPI Schema",
+        "enterOpenAPISchema": "输入您的 OpenAPI schema",
+        "importFromUrl": "从 URL 导入",
+        "examples": "示例",
+        "weatherJson": "天气(JSON)",
+        "petShopYaml": "宠物商店(YAML)",
+        "authenticationType": "鉴权方式",
+        "authType": "认证类型",
+        "none": "无",
+        "apiKey": "API Key",
+        "basic": "Basic",
+        "bearer": "Bearer",
+        "availableTools": "可用工具",
+        "description": "描述",
+        "method": "方法",
+        "path": "路径",
+        "delete": "删除",
+        "cancel": "取消",
+        "save": "保存"
+    },
+    "test": {
+        "test": "测试",
+        "addTest": "请先填写测试用例",
+        "uploadTest": "上传测试用例",
+        "explain": "为测试用例是当前组件的输入,只支持 txt 文件,最多 20 行",
+        "testRun": "测试运行",
+        "testCase": "测试用例",
+        "run": "运行",
+        "downloadResults": "下载运行结果",
+        "testCases": "输入测试用例...",
+        "parametersAndValues": "参数和值",
+        "parameter": "参数",
+        "value": "值",
+        "result": "测试结果",
+        "outResultPlaceholder": "点击按钮,输出结果"
+    },
     "agents": {
         "AgentInitializer": {
             "display_name": "AgentInitializer",

BIN
public/points.png


BIN
src/.DS_Store


+ 6 - 0
src/App.css

@@ -208,3 +208,9 @@ path.react-flow__edge-interaction:hover {
     z-index: 99;
     cursor: pointer;
 }
+
+.bs-chat-bg {
+	width: 100%;
+	background: url(/points.png) 0 100% repeat-x;
+	background-size: 10px 432px;
+}

+ 3 - 0
src/App.tsx

@@ -8,6 +8,7 @@ import "./App.css";
 import ErrorAlert from "./alerts/error";
 import NoticeAlert from "./alerts/notice";
 import SuccessAlert from "./alerts/success";
+import { Toaster } from "./components/bs-ui/toast";
 import { alertContext } from "./contexts/alertContext";
 import { locationContext } from "./contexts/locationContext";
 import { userContext } from "./contexts/userContext";
@@ -215,6 +216,8 @@ export default function App() {
           </div>
         ))}
       </div>
+      {/* 新弹窗 */}
+      <Toaster></Toaster>
     </div>
   );
 }

BIN
src/assets/.DS_Store


BIN
src/assets/npc/.DS_Store


BIN
src/assets/npc/gongjuAdd.png


BIN
src/assets/npc/gongjuBianji.png


BIN
src/assets/npc/gongjuIcon.png


BIN
src/assets/npc/gongjuIcon1.png


BIN
src/assets/npc/gongjuIcon3.png


BIN
src/assets/npc/nengliIcon.png


BIN
src/assets/npc/npcIcon.png


BIN
src/assets/npc/zidingyijia.png


BIN
src/assets/npc/矩形 1 拷贝 8@2x.png


BIN
src/components/.DS_Store


+ 6 - 6
src/components/bs-comp/cardComponent/index.tsx

@@ -35,11 +35,11 @@ interface IProps<T> {
 }
 
 export const gradients = [
-  'bg-amber-500',
-  'bg-orange-600',
-  'bg-teal-500',
-  'bg-purple-600',
-  'bg-blue-700'
+  'bg-[#FF9B25]',
+  'bg-[#04BCD2]',
+  'bg-[#2586FF]',
+  'bg-[#D855D3]',
+  'bg-[#C04B51]'
 ]
 
 // 'bg-slate-600',
@@ -61,7 +61,7 @@ export const gradients = [
 // 'bg-pink-600',
 // 'bg-rose-600'
 export function TitleIconBg({ id, className = '', children = <SkillIcon /> }) {
-  return <div className={cname(`rounded-sm flex justify-center items-center ${gradients[parseInt(id + '', 16) % gradients.length]}`, className)}>{children}</div>
+  return <div className={cname(`flex justify-center items-center cursor-pointer ${gradients[parseInt(id + '', 16) % gradients.length]}`, className)} style={{borderRadius:"7px"}}>{children}</div>
 }
 
 export default function CardComponent<T>({

+ 18 - 11
src/components/bs-comp/chatComponent/ChatInput.tsx

@@ -8,6 +8,7 @@ import { useTranslation } from "react-i18next";
 import { useMessageStore } from "./messageStore";
 import GuideQuestions from "./GuideQuestions";
 import { ClearIcon } from "@/components/bs-icons/clear";
+import duihua_send from "../../../assets/chat/duihua-send.png";
 
 export default function ChatInput({ clear, form, questions, inputForm, wsUrl, onBeforSend }) {
     const { toast } = useToast()
@@ -230,8 +231,8 @@ export default function ChatInput({ clear, form, questions, inputForm, wsUrl, on
         // setInputEmpty(textarea.value.trim() === '')
     }
 
-    return <div className="absolute bottom-0 w-full pt-1 bg-[#fff] dark:bg-[#1B1B1B]">
-        <div className={`relative ${clear && 'pl-9'}`}>
+    return <div className="absolute bottom-0 w-full  bg-[#fff] dark:bg-[#000000]">
+        <div className={`relative`}>
             {/* form */}
             {
                 formShow && <div className="relative">
@@ -248,14 +249,14 @@ export default function ChatInput({ clear, form, questions, inputForm, wsUrl, on
                 onClick={handleClickGuideWord}
             />
             {/* clear */}
-            <div className="flex absolute left-0 top-4 z-10">
+            {/* <div className="flex absolute left-0 top-4 z-10">
                 {
                     clear && <div
                         className={`w-6 h-6 rounded-sm hover:bg-gray-200 cursor-pointer flex justify-center items-center `}
                         onClick={() => { !inputLock.locked && destory() }}
                     ><ClearIcon className={!showWhenLocked && inputLock.locked ? 'text-gray-400' : 'text-gray-950'} ></ClearIcon></div>
                 }
-            </div>
+            </div> */}
             {/* form */}
             <div className="flex absolute left-3 top-4 z-10">
                 {
@@ -266,30 +267,36 @@ export default function ChatInput({ clear, form, questions, inputForm, wsUrl, on
                 }
             </div>
             {/* send */}
-            <div className="flex gap-2 absolute right-3 top-4 z-10">
+            <div className="flex gap-2 absolute right-[2.5%] z-10">
                 <div
                     id="bs-send-btn"
-                    className="w-6 h-6 rounded-sm hover:bg-gray-200 cursor-pointer flex justify-center items-center"
+                    className="w-[68px] h-[40px]  bg-[#FFD54C] cursor-pointer flex justify-center items-center"
                     onClick={() => { !inputLock.locked && handleSendClick() }}
-                ><SendIcon className={inputLock.locked ? 'text-gray-400' : 'text-gray-950'}></SendIcon></div>
+                    style={{borderRadius:"20px"}}
+                >
+                    {/* <SendIcon className={inputLock.locked ? 'text-gray-400' : 'text-gray-950'}></SendIcon> */}
+                    <img src={duihua_send} className="w-[20px]" alt="" />
+                </div>
             </div>
             {/* question */}
-            <Textarea
+            <textarea
                 id="bs-send-input"
                 ref={inputRef}
                 rows={1}
-                style={{ height: 56 }}
+                style={{ height: 34 }}
                 disabled={inputLock.locked}
                 onInput={handleTextAreaHeight}
                 placeholder={inputLock.locked ? inputLock.reason : t('chat.inputPlaceholder')}
-                className={"resize-none py-4 pr-10 text-md min-h-6 max-h-[200px] scrollbar-hide dark:bg-[#2A2B2E] text-gray-800" + (form && ' pl-10')}
+                // className={"resize-none py-4 pr-10 text-md min-h-6 max-h-[200px] scrollbar-hide dark:bg-[#2A2B2E] text-gray-800" + (form && ' pl-10')}
+                className="questionTextarea w-full resize-none border-none bg-transparent outline-none max-h-[160px]"
                 onKeyDown={(event) => {
                     if (event.key === "Enter" && !event.shiftKey) {
                         event.preventDefault();
                         !inputLock.locked && handleSendClick()
                     }
                 }}
-            ></Textarea>
+            ></textarea>
+            <p className="w-[100%] text-center text-[#666666]">内容由AI生成,仅供参考</p>
         </div>
         <p className="text-center text-sm pt-2 pb-4 text-gray-400">{appConfig.dialogTips}</p>
     </div>

+ 3 - 3
src/components/bs-comp/chatComponent/GuideQuestions.tsx

@@ -27,13 +27,13 @@ export default function GuideQuestions({ locked, chatId, questions, onClick }) {
     if (locked || !words.length) return null
 
     if (showGuideQuestion) return <div className="relative">
-        <div className="absolute left-0 bottom-0">
-            <p className="text-gray-950 text-sm mb-2 bg-[rgba(255,255,255,0.8)] rounded-md w-fit px-2 py-1">{t('chat.recommendationQuestions')}</p>
+        <div className="absolute left-[14px] bottom-0">
+            <p className="text-[#fff] mb-[10px] bg-[]">推荐问题</p>
             {
                 words.map((question, index) => (
                     <div
                         key={index}
-                        className="w-fit bg-[#d4dffa] border-2 border-gray-50 shadow-md text-gray-600 rounded-md mb-1 px-4 py-1 text-sm cursor-pointer"
+                        className="w-fit bg-[#52430c] shadow-md text-[#fff] rounded-md mb-1 px-4 py-1 text-sm cursor-pointer"
                         onClick={() => {
                             setShowGuideQuestion(false)
                             onClick(question)

+ 1 - 1
src/components/bs-comp/chatComponent/MessageBs.tsx

@@ -80,7 +80,7 @@ export default function MessageBs({ data, onUnlike = () => { }, onSource }: { da
     return <div className="flex w-full py-1">
         <div className="w-fit max-w-[90%]">
             {data.sender && <p className="text-gray-600 text-xs mb-2">{data.sender}</p>}
-            <div className="min-h-8 px-6 py-4 rounded-2xl bg-[#F5F6F8] dark:bg-[#313336]">
+            <div className="ml-[14px] min-h-8 px-6 py-4 rounded-2xl bg-[#13110D] dark:bg-[#13110D] text-[#fff]">
                 <div className="flex gap-2">
                     <div className="w-6 h-6 min-w-6 flex justify-center items-center rounded-full" style={{ background: avatarColor }} ><AvatarIcon /></div>
                     {data.message.toString() ?

+ 2 - 2
src/components/bs-comp/chatComponent/MessageUser.tsx

@@ -27,7 +27,7 @@ export default function MessageUser({ useName, data }: { data: ChatMessageType }
     return <div className="flex justify-end w-full py-1">
         <div className="w-fit min-h-8 max-w-[90%]">
             {useName && <p className="text-gray-600 text-xs mb-2 text-right">{useName}</p>}
-            <div className="rounded-2xl px-6 py-4 bg-[#EEF2FF] dark:bg-[#333A48]">
+            <div className="mr-[14px] rounded-2xl px-6 py-4 bg-[#EEF2FF] dark:bg-[#333A48]">
                 <div className="flex gap-2 ">
                     <div className="text-[#0D1638] dark:text-[#CFD5E8] text-sm break-all whitespace-break-spaces">{msg}</div>
                     <div className="w-6 h-6 min-w-6"><img src="/user.png" alt="" /></div>
@@ -36,7 +36,7 @@ export default function MessageUser({ useName, data }: { data: ChatMessageType }
             {/* 附加信息 */}
             {
                 // 数组类型的 data通常是文件上传消息,不展示附加按钮
-                !Array.isArray(data.message.data) && <div className="flex justify-between mt-2">
+                !Array.isArray(data.message.data) && <div className="flex justify-between mt-2 mr-[14px]">
                     <span></span>
                     <div className="flex gap-2 text-gray-400 cursor-pointer self-end">
                         {!running && <Pencil2Icon className="hover:text-gray-500" onClick={() => handleResend(false)} />}

+ 104 - 37
src/components/bs-comp/sheets/SkillSheet.tsx

@@ -12,6 +12,13 @@ import {
 } from "../../bs-ui/sheet";
 import CardComponent from "../cardComponent";
 import { useTranslation } from "react-i18next";
+import { SpotlightCard } from "@lobehub/ui";
+import { Flexbox } from 'react-layout-kit';
+import robot from "../../../assets/robot.png";
+import robot2 from "../../../assets/robot2.png";
+import robot3 from "../../../assets/robot3.png";
+import zidingyi1 from "../../../assets/npc/zidingyi1.png";
+import zidingyi2 from "../../../assets/npc/zidingyi2.png";
 
 export default function SkillSheet({ select, children, onSelect }) {
   const [keyword, setKeyword] = useState("");
@@ -24,7 +31,6 @@ export default function SkillSheet({ select, children, onSelect }) {
       return res;
     })
   );
-
   const handleSearch = (e) => {
     const { value } = e.target;
     setKeyword(value);
@@ -37,12 +43,73 @@ export default function SkillSheet({ select, children, onSelect }) {
 
   const { t } = useTranslation()
 
+
+  const render = (item: any) => (
+    <Flexbox align={'flex-start'} className={`selectNpcFlexbox`}>
+      {/* <Avatar size={24} src={item.favicon} style={{ flex: 'none' }} /> */}
+      {/* <Flexbox>
+        <div style={{ fontSize: 15, fontWeight: 600 }}>{item.name}</div>
+        <div style={{ opacity: 0.6 }}>{item.name}</div>
+      </Flexbox> */}
+        {/* <Card key={item.id} className="w-[300px]  overflow-hidden cursor-pointer" onClick={() => onSelect(item)}>
+            <CardHeader>
+                <CardTitle className=" flex items-center gap-2">
+                    <div className={"rounded-full w-[30px] h-[30px] " + gradients[parseInt(item.id, 16) % gradients.length]}></div>
+                    <span>{item.name}</span>
+                </CardTitle>
+                <CardDescription className="">{item.description}</CardDescription>        
+            </CardHeader>
+        </Card> */}
+        <div className="npcInfoItemBg">
+            <span>
+                <span>
+                    <div>
+                        {/* <img src={robot} className="w-[160px]" alt=""/> */}
+                        {(item.id == "06b1d374-ba97-46e6-8782-c56dec8dcc17" || item.id == "ed8e21f6-9757-43d0-b076-8c6e81bb0580") && <img src={robot2} className="w-[160px]" alt=""/>}
+                        {item.id == "ca214b41-2b73-4585-b172-bf1e546cf6ec" && <img src={robot3} className="w-[160px]" alt=""/>}
+                        {(item.id != "06b1d374-ba97-46e6-8782-c56dec8dcc17" && item.id != "ed8e21f6-9757-43d0-b076-8c6e81bb0580" && item.id != "ca214b41-2b73-4585-b172-bf1e546cf6ec") && <img src={robot} className="w-[160px]" alt=""/>}
+                    </div>
+                </span>
+            </span>
+        </div>
+        <div>
+            {(item.id == "06b1d374-ba97-46e6-8782-c56dec8dcc17" || item.id == "ed8e21f6-9757-43d0-b076-8c6e81bb0580") && <img src={robot2} className="w-[42px]" alt=""/>}
+            {item.id == "ca214b41-2b73-4585-b172-bf1e546cf6ec" && <img src={robot3} className="w-[42px]" alt=""/>}
+            {(item.id != "06b1d374-ba97-46e6-8782-c56dec8dcc17" && item.id != "ed8e21f6-9757-43d0-b076-8c6e81bb0580" && item.id != "ca214b41-2b73-4585-b172-bf1e546cf6ec") && <img src={robot} className="w-[42px]" alt=""/>}
+            {/* <img src={robot} className="w-[42px]" alt=""/> */}
+            <div>
+                <p>{item.name}</p>
+                <div>
+                    {/* <div>绘画类</div>
+                    <div>绘画类</div> */}
+                </div>
+            </div>
+        </div>
+        <p className="mt-[10px] test-[13px]">{item.description}</p>
+        <div className="flex justify-end mb-[14px] mr-[14px] mt-[14px]">
+          {select.some((_) => _.id === item.id) ? (
+            <Button size="sm" className="h-6 bg-[#FFD025]" disabled>
+              {t("build.added")}
+            </Button>
+          ) : (
+            <Button
+              size="sm"
+              className="h-6 bg-[#FFD025]"
+              onClick={() => onSelect(item)}
+            >
+              {t("build.add")}
+            </Button>
+          )}
+        </div>
+    </Flexbox>
+  );
+
   return (
     <Sheet>
       <SheetTrigger asChild>{children}</SheetTrigger>
-      <SheetContent className="bg-gray-100 sm:min-w-[966px]">
+      <SheetContent className="bg-[#1A1A1A] sm:min-w-[966px]">
         <div className="flex h-full" onClick={(e) => e.stopPropagation()}>
-          <div className="w-fit p-6">
+          <div className="w-[270px] p-6">
             <SheetTitle>{t("build.addSkill")}</SheetTitle>
             <SearchInput
               value={keyword}
@@ -50,47 +117,47 @@ export default function SkillSheet({ select, children, onSelect }) {
               className="my-6"
               onChange={handleSearch}
             />
-            <Button className="w-full" onClick={toCreateFlow}>
+            {/* <Button className="w-full" onClick={toCreateFlow}>
               {t("build.createSkill")}
-            </Button>
+            </Button> */}
+            <div className="w-[244px] h-[34px] flex items-center justify-center bg-[#FFD025] text-[#000] cursor-pointer" style={{borderRadius:"34px"}} onClick={toCreateFlow}>{t("build.createSkill")}</div>
           </div>
-          <div className="flex h-full min-w-[696px] flex-1 flex-wrap content-start gap-1.5 overflow-y-auto bg-[#fff] p-5 pt-12 scrollbar-hide">
+          <div className="min-w-[696px] overflow-y-auto bg-[#000000] scrollbar-hide skillSheet">
             {onlineFlows[0] ? (
-              onlineFlows.map((flow, i) => (
-                <CardComponent
-                  key={i}
-                  id={i + 1}
-                  data={flow}
-                  title={flow.name}
-                  description={flow.description}
-                  type="sheet"
-                  footer={
-                    <div className="flex justify-end">
-                      {select.some((_) => _.id === flow.id) ? (
-                        <Button size="sm" className="h-6" disabled>
-                          {t("build.added")}
-                        </Button>
-                      ) : (
-                        <Button
-                          size="sm"
-                          className="h-6"
-                          onClick={() => onSelect(flow)}
-                        >
-                          {t("build.add")}
-                        </Button>
-                      )}
-                    </div>
-                  }
-                />
-              ))
+              // onlineFlows.map((flow, i) => (
+              //   <CardComponent
+              //     key={i}
+              //     id={i + 1}
+              //     data={flow}
+              //     title={flow.name}
+              //     description={flow.description}
+              //     type="sheet"
+              //     footer={
+              //       <div className="flex justify-end">
+              //         {select.some((_) => _.id === flow.id) ? (
+              //           <Button size="sm" className="h-6" disabled>
+              //             {t("build.added")}
+              //           </Button>
+              //         ) : (
+              //           <Button
+              //             size="sm"
+              //             className="h-6"
+              //             onClick={() => onSelect(flow)}
+              //           >
+              //             {t("build.add")}
+              //           </Button>
+              //         )}
+              //       </div>
+              //     }
+              //   />
+              // ))
+              <SpotlightCard items={onlineFlows} renderItem={render} className="mt-[14px] skillSheetSpotlightCard"/>
             ) : (
               <div className="flex w-full flex-col items-center justify-center pt-40">
-                <p className="mb-3 text-sm text-muted-foreground">
+                <p className="mb-3 text-sm text-[#fff]">
                   {t("build.empty")}
                 </p>
-                <Button className="w-[200px]" onClick={toCreateFlow}>
-                  {t("build.createSkill")}
-                </Button>
+                <div className="w-[200px] h-[34px] flex items-center justify-center bg-[#FFD025] text-[#000] cursor-pointer" style={{borderRadius:"34px"}} onClick={toCreateFlow}>{t("build.createSkill")}</div>
               </div>
             )}
           </div>

+ 28 - 11
src/components/bs-comp/sheets/ToolsSheet.tsx

@@ -7,6 +7,10 @@ import { useTranslation } from "react-i18next";
 import { PersonIcon, StarFilledIcon } from "@radix-ui/react-icons";
 import { useEffect, useMemo, useState } from "react";
 import { Button } from "@/components/bs-ui/button";
+import sousuo from "../../../assets/npc/sousuo.png";
+import gongjuAdd from "../../../assets/npc/gongjuAdd.png";
+import gongjuIcon from "../../../assets/npc/gongjuIcon.png";
+import gongjuIcon1 from "../../../assets/npc/gongjuIcon1.png";
 
 export default function ToolsSheet({ select, onSelect, children }) {
     const { t } = useTranslation()
@@ -34,35 +38,48 @@ export default function ToolsSheet({ select, onSelect, children }) {
             <SheetTrigger asChild>
                 {children}
             </SheetTrigger>
-            <SheetContent className="w-[1000px] sm:max-w-[1000px] bg-gray-100">
+            <SheetContent className="w-[1000px] sm:max-w-[1000px] bg-[#121212]">
                 <div className="flex h-full" onClick={e => e.stopPropagation()}>
                     <div className="w-fit p-6">
                         <SheetTitle>{t('build.addTool')}</SheetTitle>
-                        <SearchInput placeholder={t('build.search')} className="mt-6" onChange={(e) => setKeyword(e.target.value)} />
-                        <Button
+                        <div className="relative mt-[14px]">
+                            <img src={sousuo} className="absolute w-[14px] left-[14px] top-[10px]" alt="" />
+                            <input placeholder="搜索"
+                            className="w-[237px] h-[34px] bg-[#1A1A1A]  text-[#fff] pl-[40px]"
+                            style={{borderRadius:"17px",outline:"none"}}
+                            onChange={(e) => setKeyword(e.target.value)} type="text" />
+                        </div>
+                        {/* <SearchInput placeholder={t('build.search')} className="mt-6" onChange={(e) => setKeyword(e.target.value)} /> */}
+                        {/* <Button
                             className="mt-4 w-full"
                             onClick={() => window.open("/build/tools")}
                         >
                             {t('create')}{t("tools.createCustomTool")}
-                        </Button>
+                        </Button> */}
+                        <div className="w-[237px] h-[27px] bg-[#FFD025] mt-[14px] flex justify-center items-center border-radius-14 cursor-pointer" onClick={() => window.open("/build/tools")}>
+                            <img src={gongjuAdd} className="w-[14px]" alt="" />
+                            <span className="text-[#333333] ml-[12px]">自定义工具</span>
+                        </div>
                         <div className="mt-4">
                             <div
-                                className={`flex items-center gap-2 px-4 py-2 rounded-md cursor-pointer hover:bg-muted-foreground/10 transition-all duration-200 ${type === 'default' && 'bg-muted-foreground/10'}`}
+                                className={`flex items-center gap-2 px-4 py-2 rounded-md cursor-pointer hover:bg-muted-foreground/10 transition-all duration-200 ${type === 'default' && 'bg-[#2A271D] text-[#FFD54C]'}`}
                                 onClick={() => setType('default')}
                             >
-                                <PersonIcon />
-                                <span>{t('tools.builtinTools')}</span>
+                                {/* <PersonIcon /> */}
+                                {type === "default" ? <img src={gongjuIcon1} className="w-[14px]" alt="" /> : <img src={gongjuIcon} className="w-[14px]" alt="" />}
+                                <span className="ml-[8px] text-[#999999]">内置工具</span>
                             </div>
                             <div
-                                className={`flex items-center gap-2 px-4 py-2 rounded-md cursor-pointer hover:bg-muted-foreground/10 transition-all duration-200 mt-1 ${type === 'custom' && 'bg-muted-foreground/10'}`}
+                                className={`flex items-center gap-2 px-4 py-2 rounded-md cursor-pointer hover:bg-muted-foreground/10 transition-all duration-200 mt-1 ${type === 'custom' && 'bg-[#2A271D] text-[#FFD54C]'}`}
                                 onClick={() => setType('custom')}
                             >
-                                <StarFilledIcon />
-                                <span>{t('tools.customTools')}</span>
+                                {type === "custom" ? <img src={gongjuIcon1} className="w-[14px]" alt="" /> : <img src={gongjuIcon} className="w-[14px]" alt="" />}
+                                {/* <StarFilledIcon /> */}
+                                <span className="ml-[8px] text-[#999999]">自定义工具</span>
                             </div>
                         </div>
                     </div>
-                    <div className="flex-1 bg-[#fff] p-5 pt-12 h-full overflow-auto scrollbar-hide">
+                    <div className="flex-1 bg-[#121212] p-5 pt-12 h-full overflow-auto scrollbar-hide">
                         <Accordion type="single" collapsible className="w-full">
                             {
                                 options.length ? options.map(el => (

BIN
src/components/bs-ui/.DS_Store


+ 1 - 1
src/components/bs-ui/accordion/index.tsx

@@ -32,7 +32,7 @@ const AccordionTrigger = React.forwardRef<
             )}
             {...props}
         >
-            <ChevronRightIcon color="#111" className="mx-2 h-4 w-4 shrink-0 text-muted-foreground transition-transform duration-200" />
+            <ChevronRightIcon color="#fff" className="mx-2 h-4 w-4 shrink-0 text-[#666666] transition-transform duration-200" />
             {children}
         </AccordionPrimitive.Trigger>
     </AccordionPrimitive.Header>

+ 4 - 1
src/components/bs-ui/alertDialog/useConfirm.tsx

@@ -6,6 +6,7 @@ import i18next from "i18next"
 import { useRef, useState } from "react"
 import { TipIcon } from "@/components/bs-icons/tip"
 import { Cross2Icon } from "@radix-ui/react-icons"
+import { createRoot } from "react-dom/client"
 
 interface ConfirmParams {
     title?: string
@@ -77,7 +78,9 @@ function ConfirmWrapper() {
         el.id = 'confirm-wrap'
         document.body.append(el)
     }
-    ReactDOM.render(<ConfirmWrapper />, el);
+    // ReactDOM.render(<ConfirmWrapper />, el);
+    const root = createRoot(el);
+    root.render(<ConfirmWrapper />);
 })();
 
 

+ 1 - 1
src/components/bs-ui/button/index.tsx

@@ -91,7 +91,7 @@ const ButtonNumber = React.forwardRef<HTMLButtonElement, {
             setValue(updateValue)
             onChange?.(updateValue)
         }
-        return (<div className={cname("flex items-center border input-border bg-gray-50 rounded-md", className)}>
+        return (<div className={cname("flex items-center border input-border bg-[#1a1a1a] rounded-md", className)}>
             <Button variant="ghost" size={size} disabled={value === min} onClick={valueReduce}>-</Button>
             <span className="min-w-10 block text-center">{value}</span>
             <Button variant="ghost" size={size} disabled={value === max} onClick={valueAdd}>+</Button>

+ 5 - 3
src/components/bs-ui/input/index.tsx

@@ -3,6 +3,7 @@ import { cname } from "../utils"
 import { SearchIcon } from "../../bs-icons/search"
 import { generateUUID } from "../utils"
 import { MinusCircledIcon } from "@radix-ui/react-icons"
+import sousuo from "../../../assets/npc/sousuo1.png"
 export interface InputProps
     extends React.InputHTMLAttributes<HTMLInputElement> { }
 
@@ -12,7 +13,7 @@ const Input = React.forwardRef<HTMLInputElement, InputProps>(
             <input
                 type={type}
                 className={cname(
-                    "flex h-9 w-full rounded-md border border-input bg-[#FAFBFC] px-3 py-1 text-sm text-[#111] shadow-sm transition-colors file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50",
+                    "flex h-9 w-full rounded-md border border-input bg-[#FAFBFC] px-3 py-1 text-sm text-[#111] shadow-sm transition-colors file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50 ",
                     className
                 )}
                 ref={ref}
@@ -27,8 +28,9 @@ Input.displayName = "Input"
 const SearchInput = React.forwardRef<HTMLInputElement, InputProps & { inputClassName?: string, iconClassName?: string }>(
     ({ className, inputClassName, iconClassName, ...props }, ref) => {
         return <div className={cname("relative", className)}>
-            <SearchIcon className={cname("h-5 w-5 absolute left-2 top-2", iconClassName)} />
-            <Input type="text" ref={ref} className={cname("pl-8 bg-search-input", inputClassName)} {...props}></Input>
+            {/* <SearchIcon className={cname("h-5 w-5 absolute left-2 top-2 text-[#666666]", iconClassName)} /> */}
+            <img src={sousuo} alt="" className="w-[14px] absolute left-[14px] top-[10px]" />
+            <Input type="text" ref={ref} className={cname("w-[244px] h-[34px] pl-[40px] npcInput3", inputClassName)} {...props}></Input>
         </div>
     }
 )

+ 2 - 2
src/components/bs-ui/radio/index.tsx

@@ -28,13 +28,13 @@ const RadioGroupItem = React.forwardRef<
         <RadioGroupPrimitive.Item
             ref={ref}
             className={cname(
-                "aspect-square h-4 w-4 rounded-full border-2 border-primary text-primary shadow focus:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50",
+                "aspect-square h-4 w-4 rounded-full border-2 border-[#FFD025] text-primary shadow focus:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50",
                 className
             )}
             {...props}
         >
             <RadioGroupPrimitive.Indicator className="flex items-center justify-center">
-                <span className="w-1.5 h-1.5 bg-primary rounded-full"></span>
+                <span className="w-1.5 h-1.5 bg-[#FFD025] rounded-full"></span>
             </RadioGroupPrimitive.Indicator>
         </RadioGroupPrimitive.Item>
     )

+ 2 - 2
src/components/bs-ui/select/index.tsx

@@ -23,7 +23,7 @@ const SelectTrigger = React.forwardRef<
     <SelectPrimitive.Trigger
         ref={ref}
         className={cname(
-            "group flex h-9 w-full items-center justify-between whitespace-nowrap rounded-md border border-input bg-[#fcfdff] px-3 py-2 text-sm shadow-sm ring-offset-background placeholder:text-muted-foreground focus:outline-none focus:ring-1 focus:ring-ring disabled:cursor-not-allowed disabled:opacity-50 [&>span]:line-clamp-1 data-[placeholder]:text-gray-400",
+            "group flex h-9 w-full items-center justify-between whitespace-nowrap rounded-md border border-input bg-[#1a1a1a] px-3 py-2 text-sm shadow-sm ring-offset-background placeholder:text-muted-foreground focus:outline-none focus:ring-1 focus:ring-ring disabled:cursor-not-allowed disabled:opacity-50 [&>span]:line-clamp-1 data-[placeholder]:text-gray-400",
             className
         )}
         {...props}
@@ -124,7 +124,7 @@ const SelectItem = React.forwardRef<
     <SelectPrimitive.Item
         ref={ref}
         className={cname(
-            "relative flex w-full cursor-default select-none items-center rounded-sm py-1.5 pl-2 pr-8 text-sm outline-none focus:bg-[#EBF0FF] focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
+            "relative flex w-full cursor-default select-none items-center rounded-sm py-1.5 pl-2 pr-8 text-sm outline-none focus:bg-[#1a1a1a] focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
             className
         )}
         {...props}

+ 3 - 3
src/components/bs-ui/select/multi.tsx

@@ -7,8 +7,8 @@ import { SearchInput } from "../input"
 const MultiItem = ({ active, children, value, onClick }) => {
 
     return <div key={value}
-        className={`relative flex w-full cursor-default select-none items-center rounded-sm py-1.5 pl-2 pr-8 mb-1 text-sm outline-none hover:bg-[#EBF0FF] hover:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50 break-all 
-    ${active && 'bg-[#EBF0FF]'}`}
+        className={`relative flex w-full cursor-default select-none items-center rounded-sm py-1.5 pl-2 pr-8 mb-1 text-sm outline-none hover:bg-[#1a1a1a] hover:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50 break-all 
+    ${active && 'bg-[#1a1a1a]'}`}
         onClick={() => { onClick(value) }}
     >
         <span className="absolute right-2 flex h-3.5 w-3.5 items-center justify-center">
@@ -93,7 +93,7 @@ const MultiSelect = ({
         setOptionFilter(newValues)
     }
     return <Select {...props} required onOpenChange={(e) => !e && setOptionFilter(options)}>
-        <SelectTrigger className="mt-2 h-auto">
+        <SelectTrigger className="mt-2 h-auto bg-[#1a1a1a]">
             {
                 values.length
                     ? <div className="flex flex-wrap">

+ 2 - 2
src/components/bs-ui/slider/index.tsx

@@ -17,8 +17,8 @@ const Slider = React.forwardRef<
         )}
         {...props}
     >
-        <SliderPrimitive.Track className="relative h-1.5 w-full grow overflow-hidden rounded-full bg-primary/20">
-            <SliderPrimitive.Range className="absolute h-full bg-primary" />
+        <SliderPrimitive.Track className="relative h-1.5 w-full grow overflow-hidden rounded-full bg-[#997e1f]">
+            <SliderPrimitive.Range className="absolute h-full bg-[#ffd025]" />
         </SliderPrimitive.Track>
         <SliderPrimitive.Thumb className="block h-4 w-4 rounded-full border border-primary/50 bg-background shadow transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50" />
     </SliderPrimitive.Root>

+ 1 - 1
src/components/bs-ui/table/index.tsx

@@ -87,7 +87,7 @@ const TableCell = React.forwardRef<
     <td
         ref={ref}
         className={cname(
-            "p-2 align-middle [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px] bg-[#FBFBFB] first:rounded-l-md last:rounded-r-md group-odd:bg-[#f4f5f8] group-hover:bg-[#ebf0ff]",
+            "p-2 align-middle [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px] bg-[#1a1a1a] first:rounded-l-md last:rounded-r-md group-odd:bg-[#1a1a1a] group-hover:bg-[#1a1a1a]",
             className
         )}
         {...props}

+ 6 - 6
src/components/bs-ui/toast/toast.tsx

@@ -32,10 +32,10 @@ const toastVariants = cva(
                 error: "error border-[#D8341E] bg-[#FFF2F0] self-end",
             },
             message: {
-                info: "shadow-xl bg-[#fff] self-center",
-                success: "shadow-xl bg-[#fff] self-center",
-                warning: "shadow-xl bg-[#fff] self-center",
-                error: "shadow-xl bg-[#fff] self-center",
+                info: "shadow-xl bg-[#333] self-center",
+                success: "shadow-xl bg-[#333] self-center",
+                warning: "shadow-xl bg-[#333] self-center",
+                error: "shadow-xl bg-[#333] self-center",
             }
         },
         defaultVariants: {},
@@ -99,7 +99,7 @@ const ToastTitle = React.forwardRef<
 >(({ className, ...props }, ref) => (
     <ToastPrimitives.Title
         ref={ref}
-        className={cname("text-sm font-semibold [&+div]:text-xs group-[.info]:text-[#024FE5] group-[.success]:text-[#0BA95D] group-[.warning]:text-[#EA991F] group-[.error]:text-[#D8341E]", className)}
+        className={cname("text-sm font-semibold text-[#fff] [&+div]:text-xs group-[.info]:text-[#fff] group-[.success]:text-[#fff] group-[.warning]:text-[#fff] group-[.error]:text-[#fff]", className)}
         {...props}
     />
 ))
@@ -111,7 +111,7 @@ const ToastDescription = React.forwardRef<
 >(({ className, ...props }, ref) => (
     <ToastPrimitives.Description
         ref={ref}
-        className={cname("text-sm opacity-90", className)}
+        className={cname("text-sm text-[#999]", className)}
         {...props}
     />
 ))

+ 4 - 2
src/controllers/API/flow.ts

@@ -172,7 +172,9 @@ export async function deleteFlowFromDatabase(flowId: string) {
 export const createCustomFlowApi = async (params: {
     name: string,
     description: string,
-    guide_word: string
+    guide_word: string,
+    avatar_img: string,
+    avatar_color: string
 }, userName: string) => {
     const response: FlowType = await axios.post("/api/v1/flows/", {
         ...params,
@@ -224,7 +226,7 @@ export async function updataOnlineState(id, updatedFlow, open) {
  * @throws .
  */
 export async function readOnlineFlows(page: number = 1, searchKey: string = "") {
-    const { data, total }: { data: any, total: number } = await axios.get(`/api/v1/flows/?page_num=${page}&page_size=${100}&status=2&name=${searchKey}`);
+    const data: { data: any, total: number } = await axios.get(`/api/v1/flows/?page_num=${page}&page_size=${100}&status=2&name=${searchKey}`);
     return data;
 }
 // export async function readOnlineFlows(page: number = 1) {

+ 11 - 3
src/controllers/API/index.ts

@@ -144,6 +144,13 @@ export async function uploadLibFile(data, config) {
 }
 
 /**
+ * 上传能力头像
+ */
+export async function uploadNpcHeaderLibFile(data, config) {
+  return await axios.post(`/api/v1/knowledge/upload_npc_header`, data, config);
+}
+
+/**
  * 确定上传文件
  * file_path knowledge_id chunck_size
  */
@@ -168,14 +175,15 @@ export async function updateFileLib(data) {
 }
 
 /**
- * 修改支持库
- *
- */
+  * 修改支持库
+  *
+  */
 export async function getFileLibById(id) {
   return await axios.get(`/api/v1/knowledge/${id}`);
 }
 
 /**
+ * 
  * 修改支持库文件
  *
  */

+ 30 - 1
src/modals/UploadModal/upload.ts

@@ -1,4 +1,4 @@
-import { uploadLibFile } from "../../controllers/API";
+import { uploadLibFile, uploadNpcHeaderLibFile } from "../../controllers/API";
 
 // Function to upload the file with progress tracking
 export const uploadFileWithProgress = async (file, callback): Promise<any> => {
@@ -29,3 +29,32 @@ export const uploadFileWithProgress = async (file, callback): Promise<any> => {
     // Handle errors
   }
 };
+
+export const uploadNpcHeaderLibFileWithProgress = async (file, callback): Promise<any> => {
+  try {
+    const formData = new FormData();
+    formData.append('file', file);
+
+    const config = {
+      headers: { 'Content-Type': 'multipart/form-data;charset=utf-8' },
+      onUploadProgress: (progressEvent) => {
+        const { loaded, total } = progressEvent;
+        const progress = Math.round((loaded * 100) / total);
+        console.log(`Upload progress: ${file.name} ${progress}%`);
+        callback(progress)
+        // You can update your UI with the progress information here
+      },
+    };
+
+    // Convert the FormData to binary using the FileReader API
+    const data = await uploadNpcHeaderLibFile(formData, config);
+
+    console.log('Upload complete:', data);
+    return data
+    // Handle the response data as needed
+  } catch (error) {
+    console.error('Error uploading file:', error);
+    return ''
+    // Handle errors
+  }
+};

+ 10 - 5
src/pages/ChatAppPage/chatShare.tsx

@@ -8,7 +8,7 @@ import ChatPanne from "./components/ChatPanne";
 import { useTranslation } from "react-i18next";
 import { deleteChatApi, getChatsApi } from "../../controllers/API";
 import { captureAndAlertRequestErrorHoc } from "../../controllers/request";
-import { useDebounce } from "../../util/hook";
+import { useDebounce, useTable } from "../../util/hook";
 import { TabsContext } from "../../contexts/tabsContext";
 import SkillTemps from "../SkillPage/components/SkillTemps";
 import duihuaDel from "../../assets/chat/duihua-del.png";
@@ -59,10 +59,15 @@ export default function chatShare() {
     // 
     const { flow: initFlow } = useContext(TabsContext);
     const [flow, setFlow] = useState<FlowType>(null)
-    const [onlineFlows, setOnlineFlows] = useState([])
-    useEffect(() => {
-        readOnlineFlows().then(setOnlineFlows)
-    }, [])
+    const {
+        data: onlineFlows,
+        loading,
+        search,
+    } = useTable<FlowType>({}, (param) =>
+        readOnlineFlows(param.page, param.keyword).then((res) => {
+          return res;
+        })
+    );
     const [chatId, setChatId] = useState<string>('')
     console.log(flowId,libId,tweak)
     useEffect(() => {

+ 10 - 5
src/pages/ChatAppPage/index.tsx

@@ -10,7 +10,7 @@ import { generateUUID } from "../../utils";
 import SkillTemps from "../SkillPage/components/SkillTemps";
 import ChatPanne from "./components/ChatPanne";
 import { captureAndAlertRequestErrorHoc } from "../../controllers/request";
-import { useDebounce } from "../../util/hook";
+import { useDebounce, useTable } from "../../util/hook";
 import duihuaDel from "../../assets/chat/duihua-del.png";
 import robot from "../../assets/robot.png";
 import robot2 from "../../assets/robot2.png";
@@ -28,10 +28,15 @@ export default function SkillChatPage() {
 
     const { flow: initFlow } = useContext(TabsContext);
     const [flow, setFlow] = useState<FlowType>(null)
-    const [onlineFlows, setOnlineFlows] = useState([])
-    useEffect(() => {
-        readOnlineFlows().then(setOnlineFlows)
-    }, [])
+    const {
+        data: onlineFlows,
+        loading,
+        search,
+    } = useTable<FlowType>({}, (param) =>
+        readOnlineFlows(param.page, param.keyword).then((res) => {
+          return res;
+        })
+    );
     // 对话列表
     const { chatList, chatId, chatsRef, setChatId, addChat, deleteChat } = useChatList()
     const chatIdRef = useRef('')

+ 10 - 5
src/pages/ChatAppPage/mobile/chatShareM.tsx

@@ -8,7 +8,7 @@ import ChatPanneM from "./ChatPanneM";
 import { useTranslation } from "react-i18next";
 import { deleteChatApi, getChatsApi } from "../../../controllers/API";
 import { captureAndAlertRequestErrorHoc } from "../../../controllers/request";
-import { useDebounce } from "../../../util/hook";
+import { useDebounce, useTable } from "../../../util/hook";
 import { TabsContext } from "../../../contexts/tabsContext";
 import SkillTemps from "../../SkillPage/components/SkillTemps";
 import titIconL from "../../../assets/chatM/tit-icon-l.png";
@@ -55,10 +55,15 @@ export default function chatShare() {
     // 
     const { flow: initFlow } = useContext(TabsContext);
     const [flow, setFlow] = useState<FlowType>(null)
-    const [onlineFlows, setOnlineFlows] = useState([])
-    useEffect(() => {
-        readOnlineFlows().then(setOnlineFlows)
-    }, [])
+    const {
+        data: onlineFlows,
+        loading,
+        search,
+    } = useTable<FlowType>({}, (param) =>
+        readOnlineFlows(param.page, param.keyword).then((res) => {
+          return res;
+        })
+    );
     const [chatId, setChatId] = useState<string>('')
     console.log(flowId,libId,tweak)
     useEffect(() => {

+ 24 - 16
src/pages/SkillPage/components/CreateAssistant.tsx

@@ -15,9 +15,9 @@ export default function CreateAssistant() {
     // State for form fields
     const [formData, setFormData] = useState({
         name: '',
-        roleAndTasks: `${t('build.example')}
-${t('build.exampleOne')}
-${t('build.exampleTwo')}
+        roleAndTasks: `示例
+示例一
+示例二
 1. XX
 2. XX
 3. …`
@@ -86,37 +86,45 @@ ${t('build.exampleTwo')}
         }
     };
 
-    return <DialogContent className="sm:max-w-[625px]">
+    return <DialogContent className="sm:max-w-[625px] bg-[#000000]">
         <DialogHeader>
-            <DialogTitle>{t('build.establishAssistant')}</DialogTitle>
+            <DialogTitle className="text-[#fff]">创建NPC</DialogTitle>
         </DialogHeader>
         <div className="flex flex-col gap-8 py-6">
             <div className="">
-                <label htmlFor="name" className="bisheng-label">{t('build.assistantName')}<span className="bisheng-tip">*</span></label>
-                <Input id="name" name="name" placeholder={t('build.giveAssistantName')} className="mt-2" value={formData.name} onChange={handleChange} />
-                {errors.name && <p className="bisheng-tip mt-1">{errors.name}</p>}
+                <label htmlFor="name" className="bisheng-label text-[#999999]"><span className="bisheng-tip text-[#FF6060]">* </span>NPC名称</label>
+                <Input id="name" name="name" placeholder="给NPC取一个名字" className="mt-2 npcInput" value={formData.name} onChange={handleChange} />
+                {errors.name && <p className="bisheng-tip mt-1 text-[#999999]">名称不可为空</p>}
             </div>
             <div className="">
-                <label htmlFor="roleAndTasks" className="bisheng-label">{t('build.whatWant')}</label>
+                <label htmlFor="roleAndTasks" className="bisheng-label text-[#999999]">你希望NPC的角色是什么,具体完成什么任务?</label>
                 <Textarea
                     id="roleAndTasks"
                     name="roleAndTasks"
-                    placeholder={t('build.forExample')}
+                    placeholder=""
                     maxLength={1000}
-                    className="mt-2 min-h-32"
+                    className="mt-2 min-h-32 npcInput overflow-auto scrollbar-hide"
                     value={formData.roleAndTasks}
                     onChange={handleChange}
                 />
                 {errors.roleAndTasks && <p className="bisheng-tip mt-1">{errors.roleAndTasks}</p>}
             </div>
         </div>
-        <DialogFooter>
+        {/* <DialogFooter>
             <DialogClose>
-                <Button variant="outline" className="px-11" type="button" onClick={() => setFormData({ name: '', roleAndTasks: '' })}>{t('cancle')}</Button>
+                <Button variant="outline" className="px-11 baogao-btn baogao-btn2" type="button" onClick={() => setFormData({ name: '', roleAndTasks: '' })}>取 消</Button>
             </DialogClose>
-            <Button disabled={loading} type="submit" className="px-11" onClick={handleSubmit}>
+            <Button disabled={loading} type="submit" className="px-11 baogao-btn baogao-btn2" onClick={handleSubmit}>
                 {loading && <LoadIcon className="mr-2" />}
-                {t('build.create')}</Button>
-        </DialogFooter>
+                创 建</Button>
+        </DialogFooter> */}
+        <div className="flex justify-center ">
+            <DialogClose>
+                <Button variant="outline" className="px-11 baogao-btn baogao-btn2" type="button" onClick={() => setFormData({ name: '', roleAndTasks: '' })}>取 消</Button>
+            </DialogClose>
+            <Button disabled={loading} type="submit" className="px-11 baogao-btn baogao-btn2" onClick={handleSubmit}>
+                {loading && <LoadIcon className="mr-2" />}
+                创 建</Button>
+        </div>
     </DialogContent>
 };

+ 3 - 3
src/pages/SkillPage/components/CreateTemp.tsx

@@ -47,14 +47,14 @@ export default function CreateTemp({ flow, open, setOpen, onCreated }: { flow: F
     return <dialog className={`modal bg-blur-shared ${open ? 'modal-open' : 'modal-close'}`} onClick={() => setOpen(false)}>
         <form method="dialog" className="max-w-[600px] flex flex-col modal-box bg-[#fff] shadow-lg dark:bg-background" onClick={e => e.stopPropagation()}>
             <button className="btn btn-sm btn-circle btn-ghost absolute right-2 top-2" onClick={() => setOpen(false)}>✕</button>
-            <h3 className="font-bold text-lg mb-4">{t('skills.createTemplate')}</h3>
+            <h3 className="font-bold text-lg mb-4 text-[#fff]">{t('skills.createTemplate')}</h3>
             <div className="flex flex-wrap flex-col gap-4">
                 <div className="grid grid-cols-4 items-center gap-4">
-                    <Label htmlFor="name" className="text-right">NPC名称</Label>
+                    <Label htmlFor="name" className="text-right text-[#fff]">能力名称</Label>
                     <Input id="name" value={data.name} onChange={(e) => setData({ ...data, name: e.target.value })} className="col-span-2" />
                 </div>
                 <div className="grid grid-cols-4 items-center gap-4">
-                    <Label htmlFor="username" className="text-right">{t('skills.description')}</Label>
+                    <Label htmlFor="username" className="text-right text-[#fff]">{t('skills.description')}</Label>
                     <Textarea id="name" value={data.description} onChange={(e) => setData({ ...data, description: e.target.value })} className="col-span-2" />
                 </div>
                 {/* <Button type="submit" className="h-8 w-[400px] rounded-full mt-6 mx-auto bg-[#FFD025]" onClick={handleSubmit}>{t('create')}</Button> */}

+ 46 - 46
src/pages/SkillPage/components/EditTool.tsx

@@ -78,27 +78,27 @@ const TestDialog = forwardRef((props: any, ref) => {
     }
 
     return <Dialog open={testShow} onOpenChange={setTestShow}>
-        <DialogContent className="sm:max-w-[625px]">
+        <DialogContent className="sm:max-w-[625px] bg-[#262626]">
             <DialogHeader>
-                <DialogTitle>{apiData.name}</DialogTitle>
+                <DialogTitle className="text-[#fff]">{apiData.name}</DialogTitle>
             </DialogHeader>
             {testShow && <div className="flex flex-col gap-8 py-6">
                 <div className="max-h-[600px] overflow-y-auto scrollbar-hide">
-                    <label htmlFor="name" className="bisheng-label">{t('test.parametersAndValues')}</label>
+                    <label htmlFor="name" className="bisheng-label text-[#fff]">{t('test.parametersAndValues')}</label>
                     <Table>
                         <TableHeader>
                             <TableRow>
-                                <TableHead className="w-[100px]">{t('test.parameter')}</TableHead>
-                                <TableHead >{t('test.value')}</TableHead>
+                                <TableHead className="w-[100px] text-[#fff]">{t('test.parameter')}</TableHead>
+                                <TableHead className="text-[#fff]">{t('test.value')}</TableHead>
                             </TableRow>
                         </TableHeader>
                         <TableBody>
                             {
                                 apiData.api_params.map((param) =>
                                     <TableRow key={param.id}>
-                                        <TableCell>{param.name}{param.required && <span className="text-red-500">*</span>}</TableCell>
+                                        <TableCell className="text-[#fff]">{param.name}{param.required && <span className="text-red-500">*</span>}</TableCell>
                                         <TableCell>
-                                            <Input onChange={(e) => {
+                                            <Input className="npcInput1" onChange={(e) => {
                                                 formRef.current[param.name] = e.target.value;
                                             }}></Input>
                                         </TableCell>
@@ -113,10 +113,10 @@ const TestDialog = forwardRef((props: any, ref) => {
                         </TableBody>
                     </Table>
                 </div>
-                <Button onClick={handleTest} disabled={loading}>{t('test.test')}</Button>
+                <Button className="bg-[#ffd025] hover:bg-[#ffd025] text-[#333]" onClick={handleTest} disabled={loading}>{t('test.test')}</Button>
                 <div className="">
-                    <label htmlFor="desc" className="bisheng-label">{t('test.result')}</label>
-                    <Textarea id="desc" name="desc" value={result} placeholder={t('test.outResultPlaceholder')} readOnly className="mt-2" />
+                    <label htmlFor="desc" className="bisheng-label text-[#fff]">{t('test.result')}</label>
+                    <Textarea id="desc" name="desc" value={result} placeholder={t('test.outResultPlaceholder')} readOnly className="mt-2 npcInput2 text-[#fff]" />
                 </div>
             </div>}
         </DialogContent>
@@ -305,33 +305,33 @@ const EditTool = forwardRef((props: any, ref) => {
 
     return <div>
         <Sheet open={editShow} onOpenChange={setEditShow}>
-            <SheetContent className="w-[800px] sm:max-w-[800px] p-4">
+            <SheetContent className="w-[800px] bg-[#000] sm:max-w-[800px] p-4">
                 <SheetHeader>
-                    <SheetTitle>{delShow ? t('edit') : t('create')}{t('tools.createCustomTool')}</SheetTitle>
+                    <SheetTitle>{delShow ? t('edit') : t('create')}自定义工具</SheetTitle>
                 </SheetHeader>
                 <div className="mt-4 overflow-y-auto h-screen pb-40">
                     {/* name */}
-                    <label htmlFor="open" className="px-6">{t('tools.name')}</label>
+                    <label htmlFor="open" className="px-6 text-[#fff]">名称</label>
                     <div className="px-6 mb-4" >
                         <Input
                             id="toolName"
                             name="toolName"
-                            className="mt-2"
-                            placeholder={t('tools.enterToolName')}
+                            className="mt-2 npcInput2"
+                            placeholder="输入工具名称"
                             value={formState.toolName}
                             onChange={handleInputChange}
                         />
                     </div>
                     {/* schema */}
                     <div className="px-6 flex items-center justify-between">
-                        <label htmlFor="open">OpenAPI Schema</label>
+                        <label htmlFor="open" className="text-[#fff]">OpenAPI Schema</label>
                         <div className="flex gap-2">
                             <Popover>
                                 <PopoverTrigger asChild>
-                                    <Button variant="outline"><PlusIcon /> {t('tools.importFromUrl')}</Button>
+                                    <Button variant="outline" className="text-[#fff] bg-[#1a1a1a]"><PlusIcon /> 从URL导入</Button>
                                 </PopoverTrigger>
                                 <PopoverContent className="w-80" align="end">
-                                    <div className="flex items-center gap-4">
+                                    <div className="flex items-center gap-4 ">
                                         <Input
                                             id="schemaUrl"
                                             name="schemaUrl"
@@ -339,19 +339,19 @@ const EditTool = forwardRef((props: any, ref) => {
                                             onChange={(e) => schemaUrl.current = e.target.value}
                                         />
                                         <PopoverClose>
-                                            <Button size="sm" className="w-16" onClick={handleImportSchema}>{t('skills.import')}</Button>
+                                            <Button size="sm" className="w-16 text-[#fff] bg-[#1a1a1a]" onClick={handleImportSchema}>{t('skills.import')}</Button>
                                         </PopoverClose>
                                     </div>
                                 </PopoverContent>
                             </Popover>
                             <Select value="1" onValueChange={(k) => handleSelectTemplate(k)}>
                                 <SelectTrigger >
-                                    <span>{t('tools.examples')}</span>
+                                    <span>{t('tools1.examples')}</span>
                                 </SelectTrigger>
                                 <SelectContent>
                                     <SelectGroup>
-                                        <SelectItem value="json">{t('tools.weatherJson')}</SelectItem>
-                                        <SelectItem value="yaml">{t('tools.petShopYaml')}</SelectItem>
+                                        <SelectItem value="json">{t('tools1.weatherJson')}</SelectItem>
+                                        <SelectItem value="yaml">{t('tools1.petShopYaml')}</SelectItem>
                                     </SelectGroup>
                                 </SelectContent>
                             </Select>
@@ -361,17 +361,17 @@ const EditTool = forwardRef((props: any, ref) => {
                         <Textarea
                             id="schemaContent"
                             name="schemaContent"
-                            placeholder={t('tools.enterOpenAPISchema')}
-                            className="mt-2 min-h-52"
+                            placeholder={t('tools1.enterOpenAPISchema')}
+                            className="mt-2 min-h-52 npcInput"
                             value={formState.schemaContent}
                             onChange={handleInputChange}
                             onBlur={() => handleSelectTemplate()}
                         />
                     </div>
-                    <label htmlFor="open" className="px-6">{t('tools.authenticationType')}</label>
+                    <label htmlFor="open" className="px-6 text-[#fff]">{t('tools1.authenticationType')}</label>
                     <div className="px-6">
                         <div className="px-6 mb-4" >
-                            <label htmlFor="open" className="bisheng-label">{t('tools.authType')}</label>
+                            <label htmlFor="open" className="bisheng-label text-[#999]">{t('tools1.authType')}</label>
                             <RadioGroup
                                 id="authMethod"
                                 name="authMethod"
@@ -381,17 +381,17 @@ const EditTool = forwardRef((props: any, ref) => {
                             >
                                 <div className="flex items-center space-x-2">
                                     <RadioGroupItem value="none" id="r1" />
-                                    <Label htmlFor="r1">{t('tools.none')}</Label>
+                                    <Label htmlFor="r1" className="text-[#fff]">{t('tools1.none')}</Label>
                                 </div>
                                 <div className="flex items-center space-x-2">
                                     <RadioGroupItem value="apikey" id="r2" />
-                                    <Label htmlFor="r2">{t('tools.apiKey')}</Label>
+                                    <Label htmlFor="r2" className="text-[#fff]">{t('tools1.apiKey')}</Label>
                                 </div>
                             </RadioGroup>
                         </div>
                         {formState.authMethod === "apikey" && (<>
                             <div className="px-6 mb-4">
-                                <label className="bisheng-label" htmlFor="apiKey">API Key</label>
+                                <label className="bisheng-label text-[#fff]" htmlFor="apiKey">API Key</label>
                                 <Input
                                     id="apiKey"
                                     name="apiKey"
@@ -402,7 +402,7 @@ const EditTool = forwardRef((props: any, ref) => {
                             </div>
 
                             <div className="px-6 mb-4" >
-                                <label htmlFor="open" className="bisheng-label">Auth Type</label>
+                                <label htmlFor="open" className="bisheng-label text-[#fff]">Auth Type</label>
                                 <RadioGroup
                                     id="authType"
                                     name="authType"
@@ -438,16 +438,16 @@ const EditTool = forwardRef((props: any, ref) => {
                             </div>
                         )} */}
                     </div>
-                    <label htmlFor="open" className="px-6">{t('tools.availableTools')}</label>
+                    <label htmlFor="open" className="px-6 text-[#fff]">{t('tools1.availableTools')}</label>
                     <div className="px-6 mb-4" >
                         <Table>
                             <TableHeader>
                                 <TableRow>
-                                    <TableHead className="w-[100px]">{t('tools.name')}</TableHead>
-                                    <TableHead >{t('tools.description')}</TableHead>
-                                    <TableHead >{t('tools.method')}</TableHead>
-                                    <TableHead >{t('tools.path')}</TableHead>
-                                    <TableHead >{t('operations')}</TableHead>
+                                    <TableHead className="w-[100px] text-[#fff]">{t('tools1.name')}</TableHead>
+                                    <TableHead className="text-[#fff]">{t('tools1.description')}</TableHead>
+                                    <TableHead className="text-[#fff]">{t('tools1.method')}</TableHead>
+                                    <TableHead className="text-[#fff]">{t('tools1.path')}</TableHead>
+                                    <TableHead className="text-[#fff]">{t('operations')}</TableHead>
                                 </TableRow>
                             </TableHeader>
                             <TableBody>
@@ -468,17 +468,17 @@ const EditTool = forwardRef((props: any, ref) => {
                                         </TableRow>
                                     ) :
                                         <TableRow>
-                                            <TableCell colSpan={5}>{t('tools.none')}</TableCell>
+                                            <TableCell colSpan={5}>{t('tools1.none')}</TableCell>
                                         </TableRow>
                                 }
                             </TableBody>
                         </Table>
                     </div>
                 </div>
-                <label htmlFor="open" className="px-6">{t('tools.authenticationType')}</label>
+                <label htmlFor="open" className="px-6">{t('tools1.authenticationType')}</label>
                 <div className="px-6">
                     <div className="px-6 mb-4" >
-                        <label htmlFor="open" className="bisheng-label">{t('tools.authType')}</label>
+                        <label htmlFor="open" className="bisheng-label">{t('tools1.authType')}</label>
                         <RadioGroup
                             id="authMethod"
                             name="authMethod"
@@ -488,17 +488,17 @@ const EditTool = forwardRef((props: any, ref) => {
                         >
                             <div className="flex items-center space-x-2">
                                 <RadioGroupItem value="none" id="r1" />
-                                <Label htmlFor="r1">{t('tools.none')}</Label>
+                                <Label htmlFor="r1">{t('tools1.none')}</Label>
                             </div>
                             <div className="flex items-center space-x-2">
                                 <RadioGroupItem value="apikey" id="r2" />
-                                <Label htmlFor="r2">{t('tools.apiKey')}</Label>
+                                <Label htmlFor="r2">{t('tools1.apiKey')}</Label>
                             </div>
                         </RadioGroup>
                     </div>
                     {formState.authMethod === "apikey" && (
                         <div className="px-6 mb-4">
-                            <label htmlFor="apiKey">{t('tools.apiKey')}</label>
+                            <label htmlFor="apiKey">{t('tools1.apiKey')}</label>
                             <Input
                                 id="apiKey"
                                 name="apiKey"
@@ -509,15 +509,15 @@ const EditTool = forwardRef((props: any, ref) => {
                         </div>
                     )}
                 </div>
-                <SheetFooter className="absolute bottom-0 right-0 w-full px-6 py-4 bg-[#fff]">
+                <SheetFooter className="absolute bottom-0 right-0 w-full px-6 py-4">
                     {delShow && <Button
                         size="sm"
                         variant="destructive"
                         className="absolute left-6"
                         onClick={handleDelete}
-                    >{t('tools.delete')}</Button>}
-                    <Button size="sm" variant="outline" onClick={() => setEditShow(false)}>{t('tools.cancel')}</Button>
-                    <Button size="sm" onClick={handleSave}>{t('tools.save')}</Button>
+                    >{t('tools1.delete')}</Button>}
+                    <Button size="sm" variant="outline" className="baogao-btn baogao-btn2" onClick={() => setEditShow(false)}>{t('tools1.cancel')}</Button>
+                    <Button size="sm" onClick={handleSave} className="baogao-btn baogao-btn2">{t('tools1.save')}</Button>
                 </SheetFooter>
             </SheetContent>
         </Sheet >

+ 20 - 12
src/pages/SkillPage/components/ToolItem.tsx

@@ -10,6 +10,8 @@ import { Badge } from "@/components/bs-ui/badge";
 import { Button } from "@/components/bs-ui/button";
 import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/bs-ui/tooltip";
 import { useTranslation } from "react-i18next";
+import gongjuIcon3 from "../../../assets/npc/gongjuIcon3.png"
+import gongjuBianji from "../../../assets/npc/gongjuBianji.png"
 
 export default function ToolItem({
     type,
@@ -20,20 +22,26 @@ export default function ToolItem({
 }) {
     const { t } = useTranslation();
 
-    return <AccordionItem key={data.id} value={data.id} className="data-[state=open]:border-2 data-[state=open]:border-primary/20 data-[state=open]:rounded-md">
+    return <AccordionItem key={data.id} value={data.id} className="data-[state=open]:border-[1px] data-[state=open]:border-[#FFD025]/40 data-[state=open]:rounded-md">
         <AccordionTrigger>
             <div className="group w-full flex gap-2 text-start relative pr-4">
-                <TitleIconBg className="w-8 h-8 min-w-8" id={data.id} ><ToolIcon /></TitleIconBg>
+                <TitleIconBg className="w-8 h-8 min-w-8" id={data.id} >
+                    <img src={gongjuIcon3} alt="" />    
+                </TitleIconBg>
                 <div className="flex-1 min-w-0">
-                    <div className="w-full text-sm font-medium leading-none flex items-center gap-2">{data.name}
+                    <div className="w-full text-sm font-medium leading-none flex items-center gap-2 text-[#FFFFFF]">{data.name}
                         {
                             type === 'edit' && <div
-                                className="group-hover:opacity-100 opacity-0 hover:bg-[#EAEDF3] rounded cursor-pointer"
+                                className="flex items-center justify-center w-[26px] h-[26px] bg-[#2A2A2A] group-hover:opacity-100 opacity-0 cursor-pointer"
+                                style={{borderRadius:"50px"}}
                                 onClick={(e) => onEdit(data.id)}
-                            ><SettingIcon /></div>
+                            >
+                                {/* <SettingIcon /> */}
+                                <img src={gongjuBianji} className="w-[14px]" alt="" />
+                            </div>
                         }
                     </div>
-                    <p className="text-sm text-muted-foreground mt-2">{data.description}</p>
+                    <p className="text-sm mt-2 text-[#666666]">{data.description}</p>
                 </div>
             </div>
         </AccordionTrigger>
@@ -41,14 +49,14 @@ export default function ToolItem({
             <div className="px-6 mb-4">
                 {data.children.map(api => (
                     <div key={api.name} className="relative p-4 rounded-sm  border-t">
-                        <h1 className="text-sm font-medium leading-none">{api.name}</h1>
-                        <p className="text-sm text-muted-foreground mt-2">{api.desc}</p>
+                        <h1 className="text-sm font-medium leading-none text-[#FFFFFF]">{api.name}</h1>
+                        <p className="text-sm mt-2 text-[#666666]">{api.desc}</p>
                         {
                             api.api_params.length > 0 && <p className="text-sm text-muted-foreground mt-2 flex gap-2">
                                 <TooltipProvider>
                                     <Tooltip delayDuration={100}>
                                         <TooltipTrigger asChild>
-                                            <span className="text-primary cursor-pointer">{t("build.params")}</span>
+                                            <span className="cursor-pointer text-[#FFD025]">{t("build.params")}</span>
                                         </TooltipTrigger>
                                         <TooltipContent side="right" className="bg-gray-50 border shadow-md p-4 text-gray-950 max-w-[520px]">
                                             <p className="flex gap-2 items-center"><Badge>{JSON.parse(api.extra)?.method || 'http'}</Badge><span className="text-xl">{api.name}</span></p>
@@ -72,7 +80,7 @@ export default function ToolItem({
                                 {
                                     api.api_params.map(param => (
                                         <div>
-                                            <span className=" rounded-xl bg-gray-200 px-2 py-1 text-xs font-medium text-white">{param.name}</span>
+                                            <span className=" rounded-xl bg-[#2E2406] px-2 py-1 text-xs font-medium text-[#FFFFFF]">{param.name}</span>
                                             {/* <span>{param.schema.type}</span> */}
                                         </div>
                                     ))
@@ -81,8 +89,8 @@ export default function ToolItem({
                         }
                         {
                             select && (select.some(_ => _.id === api.id) ?
-                                <Button size="sm" className="absolute right-4 bottom-2 h-6" disabled>{t("build.added")}</Button>
-                                : <Button size="sm" className="absolute right-4 bottom-2 h-6" onClick={() => onSelect(api)}>{t("build.add")}</Button>)
+                                <Button size="sm" className="absolute right-4 bottom-2 h-6 bg-[#FFD025]" disabled>{t("build.added")}</Button>
+                                : <Button size="sm" className="absolute right-4 bottom-2 h-6 bg-[#FFD025]" onClick={() => onSelect(api)}>{t("build.add")}</Button>)
                         }
                     </div>
                 ))}

+ 255 - 0
src/pages/SkillPage/components/editAssistant/AutoPromptDialog.tsx

@@ -0,0 +1,255 @@
+import { TitleIconBg } from "@/components/bs-comp/cardComponent";
+import { LoadIcon } from "@/components/bs-icons/loading";
+import { Button } from "@/components/bs-ui/button";
+import { DialogClose, DialogContent, DialogFooter } from "@/components/bs-ui/dialog";
+import { Textarea } from "@/components/bs-ui/input";
+import { useToast } from "@/components/bs-ui/toast/use-toast";
+import { useAssistantStore } from "@/store/assistantStore";
+import { AssistantTool } from "@/types/assistant";
+import { FlowType } from "@/types/flow";
+import { ReloadIcon } from "@radix-ui/react-icons";
+import { t } from "i18next";
+import { useEffect, useRef, useState } from "react";
+import { useParams } from "react-router-dom";
+
+const enum LoadType {
+    Prompt = 5,
+    GuideWord = 4,
+    GuideQuestion = 3,
+    Tool = 2,
+    Flow = 1
+}
+
+export default function AutoPromptDialog({ onOpenChange }) {
+    const { toast } = useToast()
+    const { id } = useParams()
+    const { assistantState, dispatchAssistant } = useAssistantStore()
+
+    const init = () => {
+        const prompt = areaRef.current.value
+        const apiUrl = `/api/v1/assistant/auto?assistant_id=${id}&prompt=${encodeURIComponent(prompt)}`;
+        const eventSource = new EventSource(apiUrl);
+        areaRef.current.value = ''
+        let queue = LoadType.Prompt
+        setLoading(queue)
+
+        eventSource.onmessage = (event) => {
+            // If the event is parseable, return
+            if (!event.data) {
+                return;
+            }
+            const parsedData = JSON.parse(event.data);
+            // console.log('parsedData :>> ', parsedData);
+            switch (parsedData.type) {
+                case 'prompt':
+                    areaRef.current.value += parsedData.message.replace('```markdown', ''); break
+                case 'guide_word':
+                    guideAreaRef.current.value += parsedData.message; break
+                case 'guide_question':
+                    setQuestion(parsedData.message); break
+                case 'tool_list':
+                    setTools(parsedData.message); break
+                case 'flow_list':
+                    setFlows(parsedData.message); break
+                case 'end':
+                    setLoading(--queue)
+                    if (parsedData.message) {
+                        toast({
+                            title: t('tip'),
+                            variant: 'error',
+                            description: parsedData.message
+                        });
+                    }
+                    break
+            }
+            // 自动滚动
+            areaRef.current.scrollTop = areaRef.current.scrollHeight;
+        };
+
+        eventSource.onerror = (error: any) => {
+            console.error("EventSource failed:", error);
+            eventSource.close();
+            if (error.data) {
+                const parsedData = JSON.parse(error.data);
+                setLoading(0);
+                toast({
+                    title: parsedData.error,
+                    variant: 'error',
+                    description: ''
+                });
+            }
+        };
+    }
+
+
+    useEffect(() => {
+        // api
+        init()
+    }, [])
+
+    const [loading, setLoading] = useState(0)
+    const handleReload = () => {
+        init()
+    }
+
+    /**
+     * 使用
+     */
+    const { message } = useToast()
+    // state
+    const areaRef = useRef(null)
+    const guideAreaRef = useRef(null)
+    const [question, setQuestion] = useState<string[]>([])
+    const [tools, setTools] = useState<AssistantTool[]>([])
+    const [flows, setFlows] = useState<FlowType[]>([])
+    // 更新提示词
+    const handleUsePropmt = () => {
+        const value = areaRef.current.value
+        dispatchAssistant('setPrompt', { prompt: value })
+        message({
+            variant: 'success',
+            title: t('tip'),
+            description: t('build.promptReplaced')
+        })
+    }
+
+    const handleUserQuestion = () => {
+        dispatchAssistant('setQuestion', { guide_question: [...question, ''] })
+        message({
+            variant: 'success',
+            title: t('tip'),
+            description: t('build.guideReplaced')
+        })
+    }
+
+    const handleUseGuide = () => {
+        const value = guideAreaRef.current.value
+        dispatchAssistant('setGuideword', { guide_word: value })
+        message({
+            variant: 'success',
+            title: t('tip'),
+            description: t('build.openingReplaced')
+        })
+    }
+
+    const handleUseTools = () => {
+        dispatchAssistant('setTools', { tool_list: tools })
+        message({
+            variant: 'success',
+            title: t('tip'),
+            description: t('build.toolsReplaced')
+        })
+    }
+
+    const handleUseFlows = () => {
+        dispatchAssistant('setFlows', { flow_list: flows })
+        message({
+            variant: 'success',
+            title: t('tip'),
+            description: t('build.skillsReplaced')
+        })
+    }
+
+    const handleUseAll = () => {
+        dispatchAssistant('setPrompt', { prompt: areaRef.current.value })
+        dispatchAssistant('setGuideword', { guide_word: guideAreaRef.current.value })
+        dispatchAssistant('setTools', { tool_list: tools })
+        dispatchAssistant('setFlows', { flow_list: flows })
+        dispatchAssistant('setQuestion', { guide_question: [...question, ''] })
+        // 收集结果
+        message({
+            variant: 'success',
+            title: t('tip'),
+            description: t('build.allReplaced')
+        })
+        onOpenChange(false)
+    }
+
+
+    return <DialogContent className="sm:max-w-[925px] bg-[#262626]">
+        <div className="flex">
+            {/* 提示词 */}
+            <div className="w-[50%] relative pr-6">
+                <div className="flex items-center justify-between">
+                    <span className="text-lg font-semibold leading-none tracking-tight flex text-[#fff]">NPC画像优化{LoadType.Prompt === loading && <LoadIcon className="ml-2 text-gray-600" />}</span>
+                    <Button variant="link" size="sm" onClick={handleReload} disabled={!!loading} className="text-[#b39012]" ><ReloadIcon className="mr-2" />重试</Button>
+                </div>
+                <div className="group flex justify-end mt-2 h-[600px] relative">
+                    <Textarea ref={areaRef} className="h-full npcInput" defaultValue={assistantState.prompt}
+                        placeholder={t('build.prompt')}
+                    ></Textarea>
+                    <Button className="group-hover:flex hidden h-6 absolute bottom-4 right-4" disabled={LoadType.Prompt <= loading} size="sm" onClick={handleUsePropmt}>{t('build.use')}</Button>
+                </div>
+            </div>
+            {/* 自动配置 */}
+            <div className="w-[50%] border-l pl-6">
+                <div>
+                    <span className="text-lg font-semibold leading-none tracking-tight text-[#fff]">{t('build.automaticallyConfigurations')}</span>
+                </div>
+                <div className="max-h-[660px] overflow-y-auto">
+                    {/* 开场白 */}
+                    <div className="group relative pb-12 mt-4 px-4 py-2 rounded-md bg-[#1a1a1a]">
+                        <div className="text-md mb-2 font-medium leading-none flex text-[#fff]">{t('build.openingRemarks')}{LoadType.GuideWord === loading && <LoadIcon className="ml-2 text-gray-600" />}</div>
+                        <Textarea ref={guideAreaRef} className="bg-transparent border-none bg-gray-50 npcInput1"></Textarea>
+                        <Button className="group-hover:flex hidden h-6 absolute bottom-4 right-4 baogao-btn2" disabled={LoadType.GuideWord <= loading} size="sm" onClick={handleUseGuide}>{t('build.use')}</Button>
+                    </div>
+                    {/* 引导词 */}
+                    <div className="group relative pb-12 mt-4 px-4 py-2 rounded-md bg-[#1a1a1a]">
+                        <div className="text-md mb-2 font-medium leading-none flex text-[#fff]">{t('build.guidingQuestions')}{LoadType.GuideQuestion === loading && <LoadIcon className="ml-2 text-gray-600" />}</div>
+                        {
+                            question.map(qs => (
+                                <p key={qs} className="text-sm text-[#666666] bg-[#262626] px-2 py-1 rounded-xl mb-2">{qs}</p>
+                            ))
+                        }
+                        <Button className="group-hover:flex hidden h-6 absolute bottom-4 right-4 baogao-btn2" disabled={LoadType.GuideQuestion <= loading} size="sm" onClick={handleUserQuestion}>{t('build.use')}</Button>
+                    </div>
+                    {/* 工具 */}
+                    <div className="group relative pb-10 mt-4 px-4 py-2 rounded-md bg-[#1a1a1a]">
+                        <div className="text-md mb-2 font-medium leading-none flex text-[#fff]">{t('build.tools')}{LoadType.Tool === loading && <LoadIcon className="ml-2 text-gray-600" />}</div>
+                        <div className="pt-1">
+                            {
+                                tools.map(tool => (
+                                    <div key={tool.id} className="flex gap-2 items-center mt-2">
+                                        <TitleIconBg id={tool.id} className=" w-7 h-7" />
+                                        <p className="text-sm">{tool.name}</p>
+                                    </div>
+                                ))
+                            }
+                        </div>
+                        <Button
+                            className="group-hover:flex hidden h-6 absolute bottom-4 right-4 baogao-btn2"
+                            disabled={LoadType.Tool <= loading || !tools.length} size="sm"
+                            onClick={handleUseTools}
+                        >{t('build.use')}</Button>
+                    </div>
+                    {/* 技能 */}
+                    <div className="group relative pb-10 mt-4 px-4 py-2 rounded-md bg-[#1a1a1a]">
+                        <div className="text-md mb-2 font-medium leading-none flex text-[#fff]">{t('build.skill')}{LoadType.Flow === loading && <LoadIcon className="ml-2 text-gray-600" />}</div>
+                        <div className="pt-1">
+                            {
+                                flows.map(flow => (
+                                    <div key={flow.id} className="flex gap-2 items-center mt-2">
+                                        <TitleIconBg id={flow.id} className=" w-7 h-7" />
+                                        <p className="text-sm">{flow.name}</p>
+                                    </div>
+                                ))
+                            }
+                        </div>
+                        <Button
+                            className="group-hover:flex hidden h-6 absolute bottom-4 right-4 baogao-btn2"
+                            disabled={LoadType.Flow <= loading || !flows.length}
+                            size="sm"
+                            onClick={handleUseFlows}
+                        >{t('build.use')}</Button>
+                    </div>
+                </div>
+            </div>
+        </div>
+        <DialogFooter>
+            <DialogClose>
+                <Button variant="outline" className="px-11 baogao-btn baogao-btn2" type="button">{t('cancle')}</Button>
+            </DialogClose>
+            <Button type="submit" className="px-11 baogao-btn baogao-btn2" disabled={!!loading} onClick={handleUseAll}>{t('build.useAll')}</Button>
+        </DialogFooter>
+    </DialogContent>
+};

+ 103 - 0
src/pages/SkillPage/components/editAssistant/EditAssistantDialog.tsx

@@ -0,0 +1,103 @@
+import { Button } from "@/components/bs-ui/button";
+import { DialogClose, DialogContent, DialogFooter, DialogHeader, DialogTitle } from "@/components/bs-ui/dialog";
+import { Input, Textarea } from "@/components/bs-ui/input";
+import { useToast } from "@/components/bs-ui/toast/use-toast";
+import { useEffect, useState } from "react";
+import { useTranslation } from "react-i18next";
+
+export default function EditAssistantDialog({ name, desc, onSave }) {
+
+    const { t } = useTranslation()
+    // State for form fields
+    const [formData, setFormData] = useState({ name: '', desc: '' });
+
+    useEffect(() => {
+        setFormData({ name, desc })
+    }, [name, desc])
+    // console.log(formData, name, desc);
+
+    // State for errors
+    const [errors, setErrors] = useState<any>({});
+
+    // Validate form fields
+    const validateField = (name, value) => {
+        switch (name) {
+            case 'name':
+                if (!value) return t('build.nameRequired');
+                if (value.length > 50) return t('build.nameMaxLength');
+                return '';
+            case 'desc':
+                if (value.length > 1000) return t('build.descMaxLength');
+                return '';
+            default:
+                return '';
+        }
+    };
+
+    // Handle field change
+    const handleChange = (e) => {
+        const { name, value } = e.target;
+        const error = validateField(name, value);
+
+        setFormData(prev => ({ ...prev, [name]: value }));
+        setErrors(prev => ({ ...prev, [name]: error }));
+    };
+
+    // Validate entire form
+    const validateForm = () => {
+        const formErrors = {};
+        let isValid = true;
+
+        Object.keys(formData).forEach(key => {
+            const error = validateField(key, formData[key]);
+            if (error) {
+                formErrors[key] = error;
+                isValid = false;
+            }
+        });
+
+        setErrors(formErrors);
+        return isValid;
+    };
+
+    const { message, toast } = useToast()
+    // Handle form submission
+    const handleSubmit = (e) => {
+        e.preventDefault();
+        const isValid = validateForm();
+        // console.log('Form data:', errors);
+        if (!isValid) return toast({
+            title: t('prompt'),
+            variant: 'error',
+            description: Object.keys(errors).map(key => errors[key]),
+        })
+
+        onSave(formData)
+
+    };
+
+    return <DialogContent className="sm:max-w-[625px]">
+        <DialogHeader>
+            <DialogTitle>{t('build.editAssistant')}</DialogTitle>
+        </DialogHeader>
+        <div className="flex flex-col gap-8 py-6">
+            <div className="">
+                <label htmlFor="name" className="bisheng-label">{t('build.assistantName')}<span className="bisheng-tip">*</span></label>
+                <Input id="name" name="name" placeholder={t('build.enterName')} className="mt-2" value={formData.name} onChange={handleChange} />
+                {errors.name && <p className="bisheng-tip mt-1">{errors.name}</p>}
+            </div>
+            <div className="">
+                <label htmlFor="desc" className="bisheng-label">{t('build.assistantDesc')}</label>
+                <Textarea id="desc" name="desc" placeholder={t('build.enterDesc')} maxLength={1200} className="mt-2" value={formData.desc} onChange={handleChange} />
+                {errors.desc && <p className="bisheng-tip mt-1">{errors.desc}</p>}
+            </div>
+        </div>
+        <DialogFooter>
+            <DialogClose>
+                <Button variant="outline" className="px-11" type="button">{t('build.cancel')}</Button>
+            </DialogClose>
+            <Button type="submit" className="px-11" onClick={handleSubmit}>{t('build.confirm')}</Button>
+        </DialogFooter>
+    </DialogContent>
+};
+

+ 58 - 0
src/pages/SkillPage/components/editAssistant/Header.tsx

@@ -0,0 +1,58 @@
+import { TitleIconBg } from "@/components/bs-comp/cardComponent";
+import { AssistantIcon } from "@/components/bs-icons/assistant";
+import { Button } from "@/components/bs-ui/button";
+import { Dialog, DialogTrigger } from "@/components/bs-ui/dialog";
+import { useAssistantStore } from "@/store/assistantStore";
+import { ChevronLeftIcon, Pencil2Icon } from "@radix-ui/react-icons";
+import { useEffect, useRef, useState } from "react";
+import { useNavigate } from "react-router-dom";
+import EditAssistantDialog from "./EditAssistantDialog";
+import { useTranslation } from "react-i18next";
+
+export default function Header({ onSave, onLine }) {
+    const { t } = useTranslation()
+
+    const navigate = useNavigate()
+
+    const { assistantState, dispatchAssistant } = useAssistantStore()
+    {/* 编辑助手 */ }
+    const [editShow, setEditShow] = useState(false);
+
+    const needSaveRef = useRef(false)
+    useEffect(() => {
+        if (needSaveRef.current) {
+            needSaveRef.current = false
+            onSave()
+        }
+    }, [assistantState])
+    const handleEditSave = (form) => {
+        dispatchAssistant('setBaseInfo', form)
+        setEditShow(false)
+        needSaveRef.current = true
+    }
+
+    return <div className="flex justify-between items-center border-b px-4">
+        <div className="flex items-center gap-2 py-4">
+            <Button variant="outline" size="icon" onClick={() => navigate(-1)}><ChevronLeftIcon className="h-4 w-4" /></Button>
+            <TitleIconBg id={assistantState.id} className="ml-4"><AssistantIcon /></TitleIconBg>
+            <span className="bisheng-title text-[#fff]">{assistantState.name}</span>
+            {/* edit dialog */}
+            <Dialog open={editShow} onOpenChange={setEditShow}>
+                <DialogTrigger asChild>
+                    <Button variant="ghost" size="icon" className="text-[#fff]"><Pencil2Icon /></Button>
+                </DialogTrigger>
+                {
+                    editShow && <EditAssistantDialog
+                        name={assistantState.name}
+                        desc={assistantState.desc}
+                        onSave={handleEditSave}></EditAssistantDialog>
+                }
+            </Dialog>
+        </div>
+        <div className="flex gap-4">
+            <Button variant="outline" className="baogao-btn baogao-btn2" type="button" onClick={onSave}>{t('build.save')}</Button>
+            <Button type="submit" className="baogao-btn baogao-btn2 ml-[27px]" onClick={onLine}>{t('build.online')}</Button>
+        </div>
+    </div>
+};
+

+ 37 - 0
src/pages/SkillPage/components/editAssistant/KnowledgeBaseMulti.tsx

@@ -0,0 +1,37 @@
+import MultiSelect from "@/components/bs-ui/select/multi";
+import { readFileLibDatabase } from "@/controllers/API";
+import { useEffect, useRef, useState } from "react";
+import { useTranslation } from "react-i18next";
+
+export default function KnowledgeBaseMulti({ value, onChange, children }:
+    { value: any, onChange: (a: any) => any, children: (fun: any) => React.ReactNode }) {
+
+    const { t } = useTranslation()
+    const [options, setOptions] = useState<any>([]);
+    const originOptionsRef = useRef([])
+    const reload = () => {
+        readFileLibDatabase(1, 400).then(res => {
+            originOptionsRef.current = res.data
+            setOptions(res.data.map(el => ({ label: el.name, value: el.id })))
+        })
+    }
+
+    useEffect(() => {
+        reload()
+    }, [])
+
+    const handleChange = (res) => {
+        // id => obj
+        onChange(res.map(el => originOptionsRef.current.find(el2 => el2.id === el)))
+    }
+
+    return <MultiSelect
+        value={value.map(el => el.id)}
+        options={options}
+        placeholder={t('build.selectKnowledgeBase')}
+        searchPlaceholder={t('build.searchBaseName')}
+        onChange={handleChange}
+    >
+        {children(reload)}
+    </MultiSelect>
+};

+ 29 - 0
src/pages/SkillPage/components/editAssistant/ModelSelect.tsx

@@ -0,0 +1,29 @@
+import { Select, SelectContent, SelectGroup, SelectItem, SelectTrigger, SelectValue } from "@/components/bs-ui/select";
+import { getAssistantModelsApi } from "@/controllers/API/assistant";
+import { useEffect, useState } from "react";
+
+export default function ModelSelect({ value, onChange }) {
+
+    const [configServers, setConfigServers] = useState([])
+    const loadModels = async () => {
+        const data = await getAssistantModelsApi()
+        setConfigServers(data)
+    }
+
+    useEffect(() => {
+        loadModels()
+    }, [])
+
+    return <Select name="model" required value={value} onValueChange={onChange}>
+        <SelectTrigger className="mt-2 text-[#fff] bg-[#1a1a1a]">
+            <SelectValue placeholder="选择一个模型" ></SelectValue>
+        </SelectTrigger>
+        <SelectContent>
+            <SelectGroup>
+                {
+                    configServers.map(server => <SelectItem key={server.id} value={server.model_name}>{server.model_name}</SelectItem>)
+                }
+            </SelectGroup>
+        </SelectContent>
+    </Select>
+};

+ 45 - 0
src/pages/SkillPage/components/editAssistant/Prompt.tsx

@@ -0,0 +1,45 @@
+import { Button } from "@/components/bs-ui/button";
+import { Dialog, DialogTrigger } from "@/components/bs-ui/dialog";
+import { MixerHorizontalIcon } from "@radix-ui/react-icons";
+import AutoPromptDialog from "./AutoPromptDialog";
+import { useEffect, useState } from "react";
+import { useAssistantStore } from "@/store/assistantStore";
+import { Textarea } from "@/components/bs-ui/input";
+import { useTranslation } from "react-i18next";
+
+export default function Prompt() {
+
+    const { t } = useTranslation()
+
+    const [open, setOpen] = useState(false);
+
+    const { assistantState, dispatchAssistant } = useAssistantStore()
+
+    useEffect(() => {
+        // 新建助手自动开启优化
+        if (window.assistantCreate && assistantState.prompt) {
+            setOpen(true)
+            delete window.assistantCreate
+        }
+    }, [assistantState.prompt])
+
+    return <div className="w-[50%] h-full  bg-[#1a1a1a] shadow-sm p-4 overflow-y-auto scrollbar-hide">
+        <div className="flex-between-center relative">
+            <span className="text-sm font-medium leading-none text-[#fff]">{t('build.assistantPortrait')}</span>
+            <Dialog open={open} onOpenChange={setOpen}>
+                <DialogTrigger asChild>
+                    <Button variant="link" className="p-0 absolute right-[10px] text-[#b39012]"><MixerHorizontalIcon className="mr-1" />{t('build.automaticOptimization')}</Button>
+                </DialogTrigger>
+                {open && <AutoPromptDialog onOpenChange={setOpen}></AutoPromptDialog>}
+            </Dialog>
+        </div>
+        <div className="h-[90%]">
+            <Textarea
+                className=" scrollbar-hide h-full focus-visible:ring-0 resize-none npcInput2 mt-[10px]"
+                value={assistantState.prompt}
+                placeholder={t('build.prompt')}
+                onInput={(e => dispatchAssistant('setPrompt', { prompt: e.target.value }))}
+            ></Textarea>
+        </div>
+    </div>
+};

+ 302 - 0
src/pages/SkillPage/components/editAssistant/Setting.tsx

@@ -0,0 +1,302 @@
+import { TitleIconBg } from "@/components/bs-comp/cardComponent";
+import SkillSheet from "@/components/bs-comp/sheets/SkillSheet";
+import ToolsSheet from "@/components/bs-comp/sheets/ToolsSheet";
+import { ToolIcon } from "@/components/bs-icons/tool";
+import {
+  Accordion,
+  AccordionContent,
+  AccordionItem,
+  AccordionTrigger,
+} from "@/components/bs-ui/accordion";
+import { Button } from "@/components/bs-ui/button";
+import { InputList, Textarea } from "@/components/bs-ui/input";
+import {
+  Tooltip,
+  TooltipContent,
+  TooltipProvider,
+  TooltipTrigger,
+} from "@/components/bs-ui/tooltip";
+import { useAssistantStore } from "@/store/assistantStore";
+import {
+  MinusCircledIcon,
+  PlusCircledIcon,
+  PlusIcon,
+  QuestionMarkCircledIcon,
+  ReloadIcon,
+} from "@radix-ui/react-icons";
+import { useTranslation } from "react-i18next";
+import { Link } from "react-router-dom";
+import KnowledgeBaseMulti from "./KnowledgeBaseMulti";
+import ModelSelect from "./ModelSelect";
+import Temperature from "./Temperature";
+
+export default function Setting() {
+  const { t } = useTranslation();
+
+  const { assistantState, dispatchAssistant } = useAssistantStore();
+
+  return (
+    <div
+      id="skill-scroll"
+      className="h-full w-[50%] overflow-y-auto scrollbar-hide"
+    >
+      <h1 className="border bg-[#1a1a1a] indent-4 text-sm text-[#999] leading-8">
+        {t("build.basicConfiguration")}
+      </h1>
+      <Accordion type="multiple" className="w-full">
+        {/* 基础配置 */}
+        <AccordionItem value="item-1">
+          <AccordionTrigger className="text-[#fff]">
+            <span>{t("build.modelConfiguration")}</span>
+          </AccordionTrigger>
+          <AccordionContent className="py-2">
+            <div className="mb-4 px-6">
+              <label htmlFor="model" className="bisheng-label text-[#999]">
+                {t("build.model")}
+              </label>
+              <ModelSelect
+                value={assistantState.model_name}
+                onChange={(val) =>
+                  dispatchAssistant("setting", { model_name: val })
+                }
+              />
+            </div>
+            <div className="mb-4 px-6">
+              <label htmlFor="slider" className="bisheng-label text-[#999]">
+                {t("build.temperature")}
+              </label>
+              <Temperature
+                value={assistantState.temperature}
+                onChange={(val) =>
+                  dispatchAssistant("setting", { temperature: val })
+                }
+              ></Temperature>
+            </div>
+          </AccordionContent>
+        </AccordionItem>
+        {/* 开场引导 */}
+        <AccordionItem value="item-2">
+          <AccordionTrigger className="text-[#fff]">
+            <span>{t("build.openingIntroduction")}</span>
+          </AccordionTrigger>
+          <AccordionContent className="py-2">
+            <div className="mb-4 px-6">
+              <label htmlFor="open" className="bisheng-label text-[#999]">
+                {t("build.openingStatement")}
+              </label>
+              <Textarea
+                name="open"
+                className="mt-2 min-h-[34px] npcInput2"
+                style={{ height: 56 }}
+                placeholder={t("build.assistantMessageFormat")}
+                value={assistantState.guide_word}
+                onChange={(e) =>
+                  dispatchAssistant("setting", { guide_word: e.target.value })
+                }
+              ></Textarea>
+              {assistantState.guide_word.length > 1000 && (
+                <p className="bisheng-tip mt-1">
+                  {t("build.maximumPromptLength")}
+                </p>
+              )}
+            </div>
+            <div className="mb-4 px-6">
+              <label htmlFor="open" className="bisheng-label flex gap-1  text-[#999]">
+                {t("build.guidingQuestions")}
+                <TooltipProvider delayDuration={200}>
+                  <Tooltip>
+                    <TooltipTrigger asChild>
+                      <QuestionMarkCircledIcon />
+                    </TooltipTrigger>
+                    <TooltipContent>
+                      <p>{t("build.recommendQuestionsForUsers")}</p>
+                    </TooltipContent>
+                  </Tooltip>
+                </TooltipProvider>
+              </label>
+              <InputList
+                rules={[{ maxLength: 50, message: t("build.maxCharacters50") }]}
+                value={assistantState.guide_question}
+                onChange={(list) => {
+                  dispatchAssistant("setting", { guide_question: list });
+                }}
+                placeholder={t("build.enterGuidingQuestions")}
+              ></InputList>
+            </div>
+          </AccordionContent>
+        </AccordionItem>
+      </Accordion>
+      <h1 className="border-b bg-[#1a1a1a] text-[#999] indent-4 text-sm leading-8">
+        {t("build.knowledge")}
+      </h1>
+      <Accordion type="multiple" className="w-full">
+        {/* 知识库 */}
+        <AccordionItem value="item-1">
+          <AccordionTrigger className="text-[#fff]">
+            <div className="flex flex-1 items-center justify-between">
+              <span>{t("build.knowledgeBase")}</span>
+              {/* <Popover>
+              <PopoverTrigger asChild className="group">
+                  <Button variant="link" size="sm"><TriangleRightIcon className="group-data-[state=open]:rotate-90" /> {t('build.autoCall')}</Button>
+              </PopoverTrigger>
+              <PopoverContent className="w-[560px]">
+                  <div className="flex justify-between">
+                      <label htmlFor="model" className="bisheng-label">{t('build.callingMethod')}</label>
+                      <div>
+                          <RadioCard checked={false} title={t('build.autoCall')} description={t('build.autoCallDescription')} calssName="mb-4"></RadioCard>
+                          <RadioCard checked title={t('build.onDemandCall')} description={t('build.onDemandCallDescription')} calssName="mt-4"></RadioCard>
+                      </div>
+                  </div>
+              </PopoverContent>
+          </Popover> */}
+            </div>
+          </AccordionTrigger>
+          <AccordionContent className="py-2">
+            <div className="mb-4 px-6">
+              <div className="flex gap-4">
+                <KnowledgeBaseMulti
+                  value={assistantState.knowledge_list}
+                  onChange={(vals) =>
+                    dispatchAssistant("setting", { knowledge_list: vals })
+                  }
+                >
+                  {(reload) => (
+                    <div className="flex justify-between">
+                      <Link to={"/filelib"} target="_blank">
+                        <Button variant="link">
+                          <PlusCircledIcon className="mr-1" />{" "}
+                          {t("build.createNewKnowledge")}
+                        </Button>
+                      </Link>
+                      <Button variant="link" onClick={reload}>
+                        <ReloadIcon className="mr-1" /> {t("build.refresh")}
+                      </Button>
+                    </div>
+                  )}
+                </KnowledgeBaseMulti>
+              </div>
+            </div>
+          </AccordionContent>
+        </AccordionItem>
+      </Accordion>
+      <h1 className="border-b bg-[#1a1a1a] text-[#999] indent-4 text-sm leading-8">
+        {t("build.abilities")}
+      </h1>
+      <Accordion
+        type="multiple"
+        className="w-full"
+        onValueChange={(e) =>
+          e.includes("skill") &&
+          document.getElementById("skill-scroll").scrollTo({ top: 9999 })
+        }
+      >
+        {/* 工具 */}
+        <AccordionItem value="item-1">
+          <AccordionTrigger className="text-[#fff]">
+            <div className="flex flex-1 items-center justify-between">
+              <span>{t("build.tools")}</span>
+              <ToolsSheet
+                select={assistantState.tool_list}
+                onSelect={(tool) =>
+                  dispatchAssistant("setting", {
+                    tool_list: [...assistantState.tool_list, tool],
+                  })
+                }
+              >
+                <PlusIcon
+                  className="mr-2 text-[#ffd025]"
+                  onClick={(e) => e.stopPropagation()}
+                ></PlusIcon>
+              </ToolsSheet>
+            </div>
+          </AccordionTrigger>
+          <AccordionContent>
+            <div className="px-4">
+              {assistantState.tool_list.map((tool) => (
+                <div
+                  key={tool.id}
+                  className="group mt-2 flex cursor-pointer items-center justify-between"
+                >
+                  <div className="flex items-center gap-2">
+                    <TitleIconBg id={tool.id} className="h-7 w-7">
+                      <ToolIcon />
+                    </TitleIconBg>
+                    <p className="text-sm">{tool.name}</p>
+                  </div>
+                  <MinusCircledIcon
+                    className="hidden text-primary group-hover:block"
+                    onClick={() =>
+                      dispatchAssistant("setting", {
+                        tool_list: assistantState.tool_list.filter(
+                          (t) => t.id !== tool.id
+                        ),
+                      })
+                    }
+                  />
+                </div>
+              ))}
+            </div>
+          </AccordionContent>
+        </AccordionItem>
+        {/* 技能 */}
+        <AccordionItem value="skill">
+          <AccordionTrigger className="text-[#fff]">
+            <div className="flex flex-1 items-center justify-between">
+              <span className="flex items-center gap-1">
+                <span>能力</span>
+                <TooltipProvider delayDuration={200}>
+                  <Tooltip>
+                    <TooltipTrigger asChild>
+                      <QuestionMarkCircledIcon />
+                    </TooltipTrigger>
+                    <TooltipContent>
+                      <p>{t("build.skillDescription")}</p>
+                    </TooltipContent>
+                  </Tooltip>
+                </TooltipProvider>
+              </span>
+              <SkillSheet
+                select={assistantState.flow_list}
+                onSelect={(flow) =>
+                  dispatchAssistant("setting", {
+                    flow_list: [...assistantState.flow_list, flow],
+                  })
+                }
+              >
+                <PlusIcon
+                  className="mr-2 text-[#ffd025]"
+                  onClick={(e) => e.stopPropagation()}
+                ></PlusIcon>
+              </SkillSheet>
+            </div>
+          </AccordionTrigger>
+          <AccordionContent>
+            <div className="px-4">
+              {assistantState.flow_list.map((flow) => (
+                <div
+                  key={flow.id}
+                  className="group mt-2 flex cursor-pointer items-center justify-between"
+                >
+                  <div className="flex items-center gap-2">
+                    <TitleIconBg id={flow.id} className="h-7 w-7"></TitleIconBg>
+                    <p className="text-sm">{flow.name}</p>
+                  </div>
+                  <MinusCircledIcon
+                    className="hidden text-primary group-hover:block"
+                    onClick={() =>
+                      dispatchAssistant("setting", {
+                        flow_list: assistantState.flow_list.filter(
+                          (t) => t.id !== flow.id
+                        ),
+                      })
+                    }
+                  />
+                </div>
+              ))}
+            </div>
+          </AccordionContent>
+        </AccordionItem>
+      </Accordion>
+    </div>
+  );
+}

+ 21 - 0
src/pages/SkillPage/components/editAssistant/Temperature.tsx

@@ -0,0 +1,21 @@
+import { ButtonNumber } from "@/components/bs-ui/button";
+import { Slider } from "@/components/bs-ui/slider";
+
+export default function Temperature({ value, onChange }) {
+
+    const props = { max: 2, min: 0, step: 0.1 }
+
+    return <div className="flex gap-4 mt-2">
+        <Slider
+            name="slider"
+            value={[value]}
+            onValueChange={(v) => onChange(v[0])}
+            {...props}
+        />
+        <ButtonNumber
+            value={value}
+            onChange={onChange}
+            {...props}
+        />
+    </div>
+};

+ 55 - 0
src/pages/SkillPage/components/editAssistant/TestChat.tsx

@@ -0,0 +1,55 @@
+import { TitleIconBg } from "@/components/bs-comp/cardComponent";
+import ChatComponent from "@/components/bs-comp/chatComponent";
+import { useMessageStore } from "@/components/bs-comp/chatComponent/messageStore";
+import { AssistantIcon } from "@/components/bs-icons/assistant";
+import { useAssistantStore } from "@/store/assistantStore";
+import { useEffect } from "react";
+import { useTranslation } from "react-i18next";
+
+export default function TestChat({ assisId, guideQuestion }) {
+    const token = localStorage.getItem("ws_token") || '';
+    const wsUrl = `${location.host}/api/v1/assistant/chat/${assisId}?t=${token}`
+
+    const { messages, changeChatId } = useMessageStore()
+    const { assistantState } = useAssistantStore()
+    const { t } = useTranslation()
+
+    // 编辑页生成唯一id
+    // const chatIdRef = useRef(generateUUID(32))
+    useEffect(() => {
+        // 建立 websocket
+        changeChatId('')
+    }, [])
+
+    // send 前获取参数用来做 params to send ws
+    const getWsParamData = (action, msg, data) => {
+        const inputKey = 'input';
+        const msgData = {
+            chatHistory: messages,
+            flow_id: '',
+            chat_id: '',
+            name: assistantState.name,
+            description: assistantState.desc,
+            inputs: {}
+        } as any
+        if (msg) msgData.inputs = { [inputKey]: msg }
+        if (data) msgData.inputs.data = data
+        if (action === 'continue') msgData.action = action
+        return [msgData, inputKey]
+    }
+
+    return <div className="relative h-full bs-chat-bg">
+        <div className="absolute flex w-full left-0 top-0 gap-2 px-4 py-2 items-center z-10 bg-[#000] shadow-sm">
+            <TitleIconBg className="" id={assistantState.id}><AssistantIcon /></TitleIconBg>
+            <span className="text-sm text-[#fff]">{t('build.debugPreview')}</span>
+        </div>
+        <ChatComponent
+            clear
+            questions={guideQuestion}
+            useName=''
+            guideWord=''
+            wsUrl={wsUrl}
+            onBeforSend={getWsParamData}
+        ></ChatComponent>
+    </div>
+};

+ 127 - 0
src/pages/SkillPage/editAssistant.tsx

@@ -0,0 +1,127 @@
+import { useMessageStore } from "@/components/bs-comp/chatComponent/messageStore";
+import { useToast } from "@/components/bs-ui/toast/use-toast";
+import { changeAssistantStatusApi, saveAssistanttApi } from "@/controllers/API/assistant";
+import { captureAndAlertRequestErrorHoc } from "@/controllers/request";
+import { useAssistantStore } from "@/store/assistantStore";
+import { useEffect, useState } from "react";
+import { useParams } from "react-router";
+import { useNavigate } from "react-router-dom";
+import Header from "./components/editAssistant/Header";
+import Prompt from "./components/editAssistant/Prompt";
+import Setting from "./components/editAssistant/Setting";
+import TestChat from "./components/editAssistant/TestChat";
+import { useTranslation } from "react-i18next";
+
+export default function editAssistant() {
+    const { t } = useTranslation()
+    const { id: assisId } = useParams()
+    const navigate = useNavigate()
+    // assistant data
+    const { assistantState, changed, loadAssistantState, saveAfter, destroy } = useAssistantStore()
+    const { startNewRound, insetSystemMsg, insetBsMsg, setShowGuideQuestion } = useMessageStore()
+
+    useEffect(() => {
+        loadAssistantState(assisId).then((res) => {
+            setShowGuideQuestion(true)
+            setGuideQuestion(res.guide_question?.filter((item) => item) || [])
+            res.guide_word && insetBsMsg(res.guide_word)
+        })
+    }, [])
+
+    // 展示的引导词独立存储
+    const [guideQuestion, setGuideQuestion] = useState([])
+    const handleStartChat = async (params) => {
+        if (!handleCheck()) return
+        await handleSave(true)
+        saveAfter()
+        startNewRound()
+        setGuideQuestion(assistantState.guide_question.filter((item) => item))
+        assistantState.guide_word && insetBsMsg(assistantState.guide_word)
+    }
+
+    const { message, toast } = useToast()
+    // 保存助手详细信息
+    const handleSave = async (showMessage = false) => {
+        if (!handleCheck()) return
+        await captureAndAlertRequestErrorHoc(saveAssistanttApi({
+            ...assistantState,
+            flow_list: assistantState.flow_list.map(item => item.id),
+            tool_list: assistantState.tool_list.map(item => item.id),
+            knowledge_list: assistantState.knowledge_list.map(item => item.id),
+            guide_question: assistantState.guide_question.filter((item) => item)
+        })).then(res => {
+            if (!res) return
+            showMessage && message({
+                title: t('prompt'),
+                variant: 'success',
+                description: t('skills.saveSuccessful')
+            })
+        })
+    }
+
+    // 上线助手
+    const handleOnline = async () => {
+        message({
+            title: t('prompt'),
+            variant: 'success',
+            description: t('skills.onlineSuccessful')
+        })
+        // if (!handleCheck()) return
+        // await handleSave()
+        // await captureAndAlertRequestErrorHoc(changeAssistantStatusApi(assistantState.id, 1)).then(res => {
+        //     if (res === false) return
+        //     message({
+        //         title: t('prompt'),
+        //         variant: 'success',
+        //         description: t('skills.onlineSuccessful')
+        //     })
+        // })
+        // setTimeout(() => {
+        //     navigate('/build')
+        // }, 1200);
+    }
+
+    // 校验助手数据
+    const handleCheck = () => {
+        const errors = []
+        if (assistantState.guide_question.some(que => que.length > 50)) {
+            errors.push(t('skills.guideQuestions50'))
+        }
+        if (assistantState.guide_word.length > 1000) {
+            errors.push(t('skills.promptWords1000'))
+        }
+
+        if (errors.length) {
+            message({
+                title: t('prompt'),
+                variant: 'error',
+                description: errors
+            })
+            return false
+        }
+        return true
+    }
+
+    // 销毁
+    useEffect(() => {
+        return destroy
+    }, [])
+
+    return <div className="bg-[#262626]">
+        <Header onSave={() => handleSave(true)} onLine={handleOnline}></Header>
+        <div className="flex h-[calc(100vh-70px)]">
+            <div className="w-[60%]">
+                <div className="text-md font-medium leading-none p-4 shadow-sm text-[#fff] bg-[#000]">{t('build.assistantConfiguration')}</div>
+                <div className="flex h-[calc(100vh-120px)]">
+                    <Prompt></Prompt>
+                    <Setting></Setting>
+                </div>
+            </div>
+            <div className="w-[40%] h-full bg-[#000000] relative">
+                <TestChat guideQuestion={guideQuestion} assisId={assisId}></TestChat>
+                {/* 变更触发保存的蒙版按钮 */}
+                {changed && <div className="absolute w-full bottom-0 h-60" onClick={handleStartChat}></div>}
+            </div>
+        </div>
+    </div>
+};

+ 18 - 9
src/pages/SkillPage/l2Edit.tsx

@@ -18,7 +18,10 @@ import { captureAndAlertRequestErrorHoc } from "../../controllers/request";
 import shangchuan from "../../assets/npc/shangchuan.png";
 import shou from "../../assets/npc/shou.png";
 import huifumoren from "../../assets/npc/huifumoren.png";
-import { uploadFileWithProgress } from "../../modals/UploadModal/upload";
+import nengliIcon from "../../assets/npc/nengliIcon.png";
+import robot from "../../assets/robot.png";
+import { uploadFileWithProgress, uploadNpcHeaderLibFileWithProgress } from "../../modals/UploadModal/upload";
+import { TitleIconBg, gradients } from "@/components/bs-comp/cardComponent";
 
 export default function l2Edit() {
     const { t } = useTranslation()
@@ -36,6 +39,7 @@ export default function l2Edit() {
     const descRef = useRef(null)
     const guideRef = useRef(null)
     const [logo, setLogo] = useState("")
+    const randomNum = Math.floor(Math.random()*(4-0+1)+0);
 
     useEffect(() => {
         // 无id不再请求
@@ -82,20 +86,23 @@ export default function l2Edit() {
         return !!errorlist.length;
     }
 
-
     const navigate = useNavigate()
     // 创建新技能
     const handleCreateNewSkill = async () => {
         const name = nameRef.current.value
         const guideWords = guideRef.current.value
         const description = descRef.current.value
+        const avatar_img = logo
+        const avatar_color = gradients[randomNum]
         if (isParamError(name, description, true)) return
         setLoading(true)
 
         await captureAndAlertRequestErrorHoc(createCustomFlowApi({
             name,
             description,
-            guide_word: guideWords
+            guide_word: guideWords,
+            avatar_img,
+            avatar_color
         }, user.user_name).then(newFlow => {
             setFlow('l2 create flow', newFlow)
             navigate("/flow/" + newFlow.id, { replace: true }); // l3
@@ -126,12 +133,14 @@ export default function l2Edit() {
         const name = nameRef.current.value
         const description = descRef.current.value
         const guideWords = guideRef.current.value
+        const avatar_img = logo
+        const avatar_color = gradients[randomNum]
         // const logo = flow.logo
         if (isParamError(name, description)) return
         setLoading(true)
         formRef.current?.save()
         console.log(flow,name,description,guideWords,logo)
-        await saveFlow({...flow, name, description, guide_word: guideWords, logo}, true)
+        await saveFlow({...flow, name, description, guide_word: guideWords, avatar_img, avatar_color}, true)
         setLoading(false)
         setSuccessData({ title: t('success') });
         setTimeout(() => /^\/skill\/[\w\d-]+/.test(location.pathname) && navigate(-1), 2000);
@@ -167,7 +176,7 @@ export default function l2Edit() {
           // Check if the file type is correct
           // if (file && checkFileType(file.name)) {
           // Upload the file
-          uploadFileWithProgress(file, (progress) => { }).then(res => {
+          uploadNpcHeaderLibFileWithProgress(file, (progress) => { }).then(res => {
         //   isSSO ? uploadFileWithProgress(file, (progress) => { }).then(res => {
             setLoading(false);
             if (typeof res === 'string') return setErrorData({ title: "Error", list: [res] })
@@ -235,18 +244,18 @@ export default function l2Edit() {
                 </div>
                 <div className="skillSettingsDiv">
                     <div className="pt-[20px] pr-[14px] pl-[14px]">
-                        <p>NPC头像</p>
+                        <p>能力头像</p>
                         <div className="flex items-center ml-[7px] mt-[10px]">
-                            {!logo ? <img src="http://npcall.ai:8402/tmp-dir/robot.png?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=admin%2F20240523%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20240523T074305Z&X-Amz-Expires=604800&X-Amz-SignedHeaders=host&X-Amz-Signature=33c17721784906036e8c55c1a2ed2ccdb8ead8311d3467588d6fdc92e3b2a88a" className="w-[34px] h-[34px]" onClick={handleButtonClick} alt="" /> : <img src={logo} className="w-[34px] h-[34px]" onClick={handleButtonClick} alt="" />}
+                            {!logo ? <TitleIconBg className="w-[41px] h-[41px] min-w-[41px]" id={randomNum} ><img onClick={handleButtonClick} src={nengliIcon} alt="" /></TitleIconBg> : <img src={logo} className="w-[41px] h-[41px]" onClick={handleButtonClick} alt="" />}
                             
-                            <div className="flex items-center justify-center ml-[27px] w-[95px] h-[27px] bg-[#333333] cursor-pointer" style={{borderRadius:"14px"}}>
+                            <div className="flex items-center justify-center ml-[20px] w-[95px] h-[27px] bg-[#333333] cursor-pointer" style={{borderRadius:"14px"}} onClick={() => setLogo(robot)}>
                                 <img src={huifumoren} className="w-[12px] h-[11px]" alt="" />
                                 <span className="ml-[5px] text-[#999999] text-[12px] mt-[1px]">恢复默认</span>
                             </div>
                         </div>
                     </div>
                     <div className="pt-[20px] pr-[14px] pl-[14px]">
-                        <p>NPC名称</p>
+                        <p>能力名称</p>
                         <Input ref={nameRef} placeholder={t('skills.skillName')} className={`mt-2 ${error.name && 'border-red-400'}`} />
                     </div>
                     <div className="pt-[20px] pr-[14px] pl-[14px]">

+ 144 - 12
src/pages/SkillPage/tabAssistant.tsx

@@ -12,19 +12,46 @@ import { captureAndAlertRequestErrorHoc } from "@/controllers/request";
 import { useToast } from "@/components/bs-ui/toast/use-toast";
 import { bsConfirm } from "@/components/bs-ui/alertDialog/useConfirm";
 import DialogForceUpdate from "@/components/bs-ui/dialog/DialogForceUpdate";
+import robot from "../../assets/robot.png";
+import robot2 from "../../assets/robot2.png";
+import robot3 from "../../assets/robot3.png";
+import bianji from "../../assets/npc/bianji.png";
+import create from "../../assets/npc/create.png";
+import share from "../../assets/npc/share.png";
+import moban from "../../assets/npc/moban.png";
+import sousuo from "../../assets/npc/sousuo.png";
+import del from "../../assets/npc/del.png";
+import zidingyi1 from "../../assets/npc/zidingyi1.png";
+import zidingyijia from "../../assets/npc/zidingyijia.png";
+import { useContext, useRef, useState } from "react";
+import { SpotlightCard } from "@lobehub/ui";
+import { Flexbox } from 'react-layout-kit';
+import { updataOnlineState } from "@/controllers/API/flow";
+import { Switch } from "@/components/ui/switch";
+import { userContext } from "@/contexts/userContext";
+import ShareLink from "./externalUse/shareLink";
 
 export default function Assistants() {
     const { t } = useTranslation()
+    const { user } = useContext(userContext);
     const navigate = useNavigate()
     const { message } = useToast()
-
+    const [open, setOpen] = useState(false);
+    const [isShareLink, setIsShareLink] = useState(false)
+    const [isShareLinkData, setIsShareLinkData] = useState("Any");
     const { page, pageSize, data: dataSource, total, loading, setPage, search, reload, refreshData } = useTable<AssistantItemDB>({ pageSize: 15 }, (param) =>
         getAssistantsApi(param.page, param.pageSize, param.keyword)
     )
+    
+    if(dataSource[0] && dataSource[0].type != 0){
+        dataSource.unshift({"type":0})
+    }
+
+    const inputRef = useRef(null);
 
     const handleDelete = (data) => {
         bsConfirm({
-            desc: t('deleteAssistant'),
+            desc: t('确认删除该助手?'),
             okTxt: t('delete'),
             onOk(next) {
                 deleteAssistantApi(data.id).then(() => reload())
@@ -42,10 +69,104 @@ export default function Assistants() {
         })
     }
 
+    const render = (item: any) => {
+        const [openSwitch, setOpenSwitch] = useState(item.status === 1);
+        const handleChange = (bln) => {
+            // captureAndAlertRequestErrorHoc(updataOnlineState(item.id, item, bln).then(res => {
+            //     setOpenSwitch(bln);
+            //     if (res) {
+            //         refreshData((item1) => item1.id === item.id, { status: bln ? 1 : 0 })
+            //     }
+            //     return res
+            // }))
+            return captureAndAlertRequestErrorHoc(changeAssistantStatusApi(item.id, bln ? 1 : 0)).then(res => {
+                if (res === null) {
+                    refreshData((item1) => item1.id === item.id, { status: bln ? 1 : 0 })
+                }
+                return res
+            })
+        };
+        return(
+            <Flexbox align={'flex-start'}>
+                {item.type == 0 ?
+                <div className="selectNpcFlexboxZiDing" onClick={() => setOpen(true)}>
+                    <div>
+                        <img src={zidingyijia} alt="" />
+                        <span>自定义NPC</span>
+                    </div>
+                    <p>通过描述角色和任务来创建你的NPC,NPC 可以调用多个能力和工具。</p>
+                    <div className="flex justify-end mb-[7px] mr-[14px] mt-0">
+                        <img src={zidingyi1} className="w-[68px]" alt="" />
+                    </div>
+                </div>
+                // <DialogForceUpdate
+                //     trigger={
+                //         <CardComponent<FlowType>
+                //             data={null}
+                //             type='skill'
+                //             title={t('build.createAssistant')}
+                //             description={(<>
+                //                 <p>{t('build.createDescription')}</p>
+                //                 <p>{t('build.nextDescription')}</p>
+                //             </>)}
+                //             onClick={() => console.log('新建')}
+                //         ></CardComponent>
+                //     }>
+                //     <CreateAssistant ></CreateAssistant>
+                // </DialogForceUpdate>
+                :
+                <div className={`selectNpcFlexbox`}>
+                    <div className="npcInfoItemBg">
+                        {openSwitch && <span>
+                            <span>
+                                <div>
+                                    {(item.id == "06b1d374-ba97-46e6-8782-c56dec8dcc17" || item.id == "ed8e21f6-9757-43d0-b076-8c6e81bb0580") && <img src={robot2} className="w-[160px]" alt=""/>}
+                                    {item.id == "ca214b41-2b73-4585-b172-bf1e546cf6ec" && <img src={robot3} className="w-[160px]" alt=""/>}
+                                    {(item.id != "06b1d374-ba97-46e6-8782-c56dec8dcc17" && item.id != "ed8e21f6-9757-43d0-b076-8c6e81bb0580" && item.id != "ca214b41-2b73-4585-b172-bf1e546cf6ec") && <img src={robot} className="w-[160px]" alt=""/>}
+                                </div>
+                            </span>
+                        </span>}
+                    </div>
+                    <div>
+                        {(item.id == "06b1d374-ba97-46e6-8782-c56dec8dcc17" || item.id == "ed8e21f6-9757-43d0-b076-8c6e81bb0580") && <img src={robot2} className="w-[40px]" alt=""/>}
+                        {item.id == "ca214b41-2b73-4585-b172-bf1e546cf6ec" && <img src={robot3} className="w-[40px]" alt=""/>}
+                        {(item.id != "06b1d374-ba97-46e6-8782-c56dec8dcc17" && item.id != "ed8e21f6-9757-43d0-b076-8c6e81bb0580" && item.id != "ca214b41-2b73-4585-b172-bf1e546cf6ec") && <img src={robot} className="w-[40px]" alt=""/>}
+                        {/* <img src={robot} className="w-[40px]" alt=""/> */}
+                        <p className="ml-[14px]">{item.name}</p>
+                    </div>
+                    <p>{item.desc}</p>
+                    <div>
+                        <div>
+                            {!openSwitch && <div className="w-[80px] h-[27px] bianji mr-[14px]" onClick={() => item.status !== 1 && navigate('/assistant/' + item.id)}>
+                                <img src={bianji} className="w-[14px] mr-[10px]" alt=""/>
+                                编辑
+                            </div>}
+                            
+
+                            <div className="w-[27px] h-[27px] create mr-[14px]" onClick={() => {setIsShareLink(!isShareLink);setIsShareLinkData("http://npcall.ai:3003/chat/" + item.id)}}>
+                                <img src={share} className="w-[14px]" alt=""/>
+                            </div>
+
+                            <div className="w-[27px] h-[27px] create" onClick={() => handleDelete(item)}>
+                                <img src={del} className="w-[14px]" alt=""/>
+                            </div>
+                        </div>
+                        <Switch checked={openSwitch} onCheckedChange={handleChange}  />
+                    </div>
+                </div>}
+            </Flexbox>
+        )
+    };
+
     return <div className="h-full relative">
-        <div className="px-10 py-10 h-full overflow-y-scroll scrollbar-hide relative top-[-60px]">
-            <div className="flex">
-                <SearchInput className="w-64" placeholder={t('build.searchAssistant')} onChange={(e) => search(e.target.value)}></SearchInput>
+        <div className="px-[27px] relative" style={{ height: 'calc(100% - 50px)' }}>
+            <p className="text-[11px]" style={{color:"#666666"}}>在此页面管理您的NPC,对NPC上下线、编辑等等</p>
+            <div className="flex gap-2 absolute top-[-47px] right-[27px] btn-r">
+                <div>
+
+                    <img ref={inputRef} src={sousuo} alt="" />
+                    <input type="text" placeholder="搜索" onChange={(e) => search(e.target.value)} />
+                </div>
             </div>
             {/* list */}
             {
@@ -53,9 +174,9 @@ export default function Assistants() {
                     ? <div className="absolute w-full h-full top-0 left-0 flex justify-center items-center z-10 bg-[rgba(255,255,255,0.6)] dark:bg-blur-shared">
                         <span className="loading loading-infinity loading-lg"></span>
                     </div>
-                    : <div className="mt-6 flex gap-2 flex-wrap pb-20 min-w-[980px]">
+                    : <div className="Skill_box overflow-y-scroll scrollbar-hide">
                         {/* 创建助手 */}
-                        <DialogForceUpdate
+                        {/* <DialogForceUpdate
                             trigger={
                                 <CardComponent<FlowType>
                                     data={null}
@@ -69,8 +190,8 @@ export default function Assistants() {
                                 ></CardComponent>
                             }>
                             <CreateAssistant ></CreateAssistant>
-                        </DialogForceUpdate>
-                        {
+                        </DialogForceUpdate> */}
+                        {/* {
                             dataSource.map((item, i) => (
                                 <CardComponent<AssistantItemDB>
                                     data={item}
@@ -88,14 +209,25 @@ export default function Assistants() {
                                     onCheckedChange={handleCheckedChange}
                                 ></CardComponent>
                             ))
-                        }
+                        } */}
+                        <SpotlightCard items={dataSource} renderItem={render} className="mt-[14px] selectNpcSpotlightCard"/>
                     </div>
             }
         </div>
         {/* footer */}
-        <div className="flex justify-between absolute bottom-0 left-0 w-full bg-background-main h-16 items-center px-10">
-            <p className="text-sm text-muted-foreground break-keep">{t('build.manageAssistant')}</p>
+        <div className="flex justify-end absolute bottom-0 left-0 w-full bg-background-main h-16 items-center px-10 bg-[#000000] z-[9]">
+            {/* <p className="text-sm text-muted-foreground break-keep">{t('build.manageAssistant')}</p> */}
             <AutoPagination className="m-0 w-auto justify-end" page={page} pageSize={pageSize} total={total} onChange={setPage}></AutoPagination>
         </div>
+
+        <Dialog open={open} onOpenChange={setOpen}>
+            {/* <DialogTrigger asChild>
+                {trigger}
+            </DialogTrigger>
+            {open ? children : null} */}
+            <CreateAssistant ></CreateAssistant>
+        </Dialog>
+        {/* 分享链接 */}
+        <ShareLink isShareLink={isShareLink} setIsShareLink={setIsShareLink} data={isShareLinkData}></ShareLink>
     </div>
 };

+ 108 - 61
src/pages/SkillPage/tabSkills.tsx

@@ -1,7 +1,7 @@
 import SkillTempSheet from "@/components/bs-comp/sheets/SkillTempSheet";
 import { bsConfirm } from "@/components/bs-ui/alertDialog/useConfirm";
 import { useToast } from "@/components/bs-ui/toast/use-toast";
-import { useContext, useRef, useState } from "react";
+import { useContext, useEffect, useRef, useState } from "react";
 import { useTranslation } from "react-i18next";
 import { useNavigate } from "react-router-dom";
 import CardComponent from "../../components/bs-comp/cardComponent";
@@ -28,9 +28,14 @@ import share from "../../assets/npc/share.png";
 import moban from "../../assets/npc/moban.png";
 import sousuo from "../../assets/npc/sousuo.png";
 import del from "../../assets/npc/del.png";
+import zidingyi1 from "../../assets/npc/zidingyi1.png";
+import zidingyijia from "../../assets/npc/zidingyijia.png";
 import ShareLink from "./externalUse/shareLink";
 import { Switch } from "@/components/ui/switch";
 import { Flexbox } from 'react-layout-kit';
+import Templates from "./temps";
+import { Size } from "recharts/types/util/types";
+import SkillTemps from "./components/SkillTemps";
 
 export default function Skills() {
     const { t } = useTranslation()
@@ -41,9 +46,19 @@ export default function Skills() {
     const { page, pageSize, data: dataSource, total, loading, setPage, search, reload, refreshData } = useTable<FlowType>({ pageSize: 14 }, (param) =>
         readFlowsFromDatabase(param.page, param.pageSize, param.keyword)
     )
+
+    if(dataSource[0] && dataSource[0].type != 0){
+        dataSource.unshift({"type":0})
+    }
+
+    const inputRef = useRef(null);
     const [open, setOpen] = useState(false)
+    const [isTempsPage, setIsTempPage] = useState(false)
+    const [temps, loadTemps] = useTemps()
 
     const { open: tempOpen, flowRef, toggleTempModal } = useCreateTemp()
+    const [isShareLink, setIsShareLink] = useState(false)
+    const [isShareLinkData, setIsShareLinkData] = useState("Any");
 
     // 上下线
     const handleCheckedChange = (checked, data) => {
@@ -75,7 +90,7 @@ export default function Skills() {
 
     // 选模板(创建技能)
     const handldSelectTemp = async (tempId) => {
-        const [flow] = await readTempsDatabase(tempId)
+        const [flow] = await readTempsDatabase(tempId.id)
 
         flow.name = `${flow.name}-${generateUUID(5)}`
         captureAndAlertRequestErrorHoc(saveFlowToDatabase({ ...flow, id: flow.flow_id }).then(res => {
@@ -86,77 +101,88 @@ export default function Skills() {
         }))
     }
 
+    const nvaigate = useNavigate()
+    const handleEdit = (id,pageNo) => {
+        // onBeforeEdit?.()
+        window.SearchSkillsPage = { no: pageNo, key: inputRef.current.value };
+        nvaigate("/skill/" + id)
+    }
+
+    // 模板管理
+     if (isTempsPage) return <Templates onBack={() => setIsTempPage(false)} onChange={loadTemps}></Templates>
+
     const render = (item: any) => {
         const [openSwitch, setOpenSwitch] = useState(item.status === 2);
         const handleChange = (bln) => {
             captureAndAlertRequestErrorHoc(updataOnlineState(item.id, item, bln).then(res => {
                 setOpenSwitch(bln);
-                loadPage(page.pageNo);
+                if (res) {
+                    refreshData((item1) => item1.id === item.id, { status: bln ? 2 : 1 })
+                }
+                return res
+                // loadPage(page.pageNo);
                 // itema.status = bln ? 2 : 1
             }))
         };
         return(
-            <Flexbox align={'flex-start'} className={`selectNpcFlexbox`}>
-              {/* <Avatar size={24} src={item.favicon} style={{ flex: 'none' }} /> */}
-              {/* <Flexbox>
-                <div style={{ fontSize: 15, fontWeight: 600 }}>{item.name}</div>
-                <div style={{ opacity: 0.6 }}>{item.name}</div>
-              </Flexbox> */}
-                {/* <Card key={item.id} className="w-[300px]  overflow-hidden cursor-pointer" onClick={() => onSelect(item)}>
-                    <CardHeader>
-                        <CardTitle className=" flex items-center gap-2">
-                            <div className={"rounded-full w-[30px] h-[30px] " + gradients[parseInt(item.id, 16) % gradients.length]}></div>
-                            <span>{item.name}</span>
-                        </CardTitle>
-                        <CardDescription className="">{item.description}</CardDescription>        
-                    </CardHeader>
-                </Card> */}
-                <div className="npcInfoItemBg">
-                    <span>
-                        <span>
-                            <div>
-                                {(item.id == "06b1d374-ba97-46e6-8782-c56dec8dcc17" || item.id == "ed8e21f6-9757-43d0-b076-8c6e81bb0580") && <img src={robot2} className="w-[160px]" alt=""/>}
-                                {item.id == "ca214b41-2b73-4585-b172-bf1e546cf6ec" && <img src={robot3} className="w-[160px]" alt=""/>}
-                                {(item.id != "06b1d374-ba97-46e6-8782-c56dec8dcc17" && item.id != "ed8e21f6-9757-43d0-b076-8c6e81bb0580" && item.id != "ca214b41-2b73-4585-b172-bf1e546cf6ec") && <img src={robot} className="w-[160px]" alt=""/>}
-                                {/* <img src={robot} className="w-[160px]" alt=""/> */}
-                            </div>
-                        </span>
-                    </span>
-                </div>
-                <div>
-                    {(item.id == "06b1d374-ba97-46e6-8782-c56dec8dcc17" || item.id == "ed8e21f6-9757-43d0-b076-8c6e81bb0580") && <img src={robot2} className="w-[40px]" alt=""/>}
-                    {item.id == "ca214b41-2b73-4585-b172-bf1e546cf6ec" && <img src={robot3} className="w-[40px]" alt=""/>}
-                    {(item.id != "06b1d374-ba97-46e6-8782-c56dec8dcc17" && item.id != "ed8e21f6-9757-43d0-b076-8c6e81bb0580" && item.id != "ca214b41-2b73-4585-b172-bf1e546cf6ec") && <img src={robot} className="w-[40px]" alt=""/>}
-                    {/* <img src={robot} className="w-[40px]" alt=""/> */}
-                    <p className="ml-[14px]">{item.name}</p>
-                </div>
-                <p>{item.description}</p>
-                <div>
+            <Flexbox align={'flex-start'}>
+                {item.type == 0 ?
+                <div className="selectNpcFlexboxZiDing" onClick={() => setOpen(true)}>
+                    <div>
+                        <img src={zidingyijia} alt="" />
+                        <span>自定义能力</span>
+                    </div>
+                    <p>能力通过可视化的流程编排,明确工作执行 步骤。我们提供场景模板供您使用和参考。</p>
+                    <img src={zidingyi1} alt="" />
+                </div>:
+                <div className={`selectNpcFlexbox`}>
+                    <div className="npcInfoItemBg">
+                        {openSwitch && <span>
+                            <span>
+                                <div>
+                                    {(item.id == "06b1d374-ba97-46e6-8782-c56dec8dcc17" || item.id == "ed8e21f6-9757-43d0-b076-8c6e81bb0580") && <img src={robot2} className="w-[160px]" alt=""/>}
+                                    {item.id == "ca214b41-2b73-4585-b172-bf1e546cf6ec" && <img src={robot3} className="w-[160px]" alt=""/>}
+                                    {(item.id != "06b1d374-ba97-46e6-8782-c56dec8dcc17" && item.id != "ed8e21f6-9757-43d0-b076-8c6e81bb0580" && item.id != "ca214b41-2b73-4585-b172-bf1e546cf6ec") && <img src={robot} className="w-[160px]" alt=""/>}
+                                </div>
+                            </span>
+                        </span>}
+                    </div>
                     <div>
-                        {!openSwitch && <div className="w-[80px] h-[27px] bianji mr-[14px]" onClick={() => handleEdit(item.id,page.pageNo)}>
-                            <img src={bianji} className="w-[14px] mr-[10px]" alt=""/>
-                            编辑
-                        </div>}
-                        
-                        {user.role === 'admin' && <div className="w-[27px] h-[27px] create mr-[14px]" onClick={() => toggleTempModal(item)}>
-                            <img src={create} className="w-[14px]" alt=""/>
-                        </div>}
-
-                        <div className="w-[27px] h-[27px] create" onClick={() => {setIsShareLink(!isShareLink);setIsShareLinkData("http://npcall.ai:3003/chat/" + item.id)}}>
-                            <img src={share} className="w-[14px]" alt=""/>
+                        {(item.id == "06b1d374-ba97-46e6-8782-c56dec8dcc17" || item.id == "ed8e21f6-9757-43d0-b076-8c6e81bb0580") && <img src={robot2} className="w-[40px]" alt=""/>}
+                        {item.id == "ca214b41-2b73-4585-b172-bf1e546cf6ec" && <img src={robot3} className="w-[40px]" alt=""/>}
+                        {(item.id != "06b1d374-ba97-46e6-8782-c56dec8dcc17" && item.id != "ed8e21f6-9757-43d0-b076-8c6e81bb0580" && item.id != "ca214b41-2b73-4585-b172-bf1e546cf6ec") && <img src={robot} className="w-[40px]" alt=""/>}
+                        {/* <img src={robot} className="w-[40px]" alt=""/> */}
+                        <p className="ml-[14px]">{item.name}</p>
+                    </div>
+                    <p>{item.description}</p>
+                    <div>
+                        <div>
+                            {!openSwitch && <div className="w-[80px] h-[27px] bianji mr-[14px]" onClick={() => handleEdit(item.id,page.pageNo)}>
+                                <img src={bianji} className="w-[14px] mr-[10px]" alt=""/>
+                                编辑
+                            </div>}
+                            
+                            {user.role === 'admin' && <div className="w-[27px] h-[27px] create mr-[14px]" onClick={() => toggleTempModal(item)}>
+                                <img src={create} className="w-[14px]" alt=""/>
+                            </div>}
+
+                            <div className="w-[27px] h-[27px] create mr-[14px]" onClick={() => {setIsShareLink(!isShareLink);setIsShareLinkData("http://npcall.ai:3003/chat/" + item.id)}}>
+                                <img src={share} className="w-[14px]" alt=""/>
+                            </div>
+
+                            <div className="w-[27px] h-[27px] create" onClick={() => handleDelete(item)}>
+                                <img src={del} className="w-[14px]" alt=""/>
+                            </div>
                         </div>
+                        <Switch checked={openSwitch} onCheckedChange={handleChange}  />
                     </div>
-                    <Switch checked={openSwitch} onCheckedChange={handleChange}  />
-                </div>
-                <div className="del" onClick={() => handleDelete(item.id)}>
-                    <img src={del} className="w-[14px]" alt=""/>
-                </div>
+                </div>}
             </Flexbox>
         )
     };
 
     return <div className="h-full relative">
-        <div className="px-[27px] h-full   relative">
+        <div className="px-[27px] relative" style={{ height: 'calc(100% - 50px)' }}>
             <p className="text-[11px]" style={{color:"#666666"}}>在此页面管理能力,对能力上下线、编辑等等</p>
             <div className="flex gap-2 absolute top-[-47px] right-[27px] btn-r">
                 {/* <SearchInput className="w-64" placeholder={t('skills.skillSearch')} onChange={(e) => search(e.target.value)}></SearchInput> */}
@@ -166,10 +192,12 @@ export default function Skills() {
                     onClick={() => navigate('/build/temps')}
                 ><MoveOneIcon />{t('skills.manageTemplate')}</Button>} */}
                 <div>
+
                     <img src={sousuo} alt="" />
-                    <input type="text" placeholder="搜索" onChange={(e) => search(e.target.value)} />
+                    <input ref={inputRef} type="text" placeholder="搜索" onChange={(e) => search(e.target.value)} />
                 </div>
-                {user.role === 'admin' && <div onClick={() => setIsTempPage(true)}><img src={moban} className="w-[14px] mr-[5px] btn-r-d" alt=""/><span>模版管理</span></div>}
+                {user.role === 'admin' && <div onClick={() => navigate('/build/temps')}><img src={moban} className="w-[14px] mr-[5px] btn-r-d" alt=""/><span>模版管理</span></div>}
+                {/* {user.role === 'admin' && <div onClick={() => setIsTempPage(true)}><img src={moban} className="w-[14px] mr-[5px] btn-r-d" alt=""/><span>模版管理</span></div>} */}
             </div>
             {/* list */}
             {
@@ -221,13 +249,17 @@ export default function Skills() {
                     </div>
             }
         </div>
+        {/* chose template */}
+        <SkillTemps flows={temps} isTemp open={open} setOpen={setOpen} onSelect={handldSelectTemp}></SkillTemps>
         {/* 添加模板 */}
         <CreateTemp flow={flowRef.current} open={tempOpen} setOpen={() => toggleTempModal()} onCreated={() => { }} ></CreateTemp>
         {/* footer */}
-        <div className="flex justify-between absolute bottom-0 left-0 w-full bg-background-main h-16 items-center px-10">
-            <p className="text-sm text-muted-foreground break-keep">{t('skills.manageProjects')}</p>
+        <div className="flex justify-end absolute bottom-0 left-0 w-full bg-background-main h-16 items-center px-10 bg-[#000000] z-[9]">
+            {/* <p className="text-sm text-muted-foreground break-keep">{t('skills.manageProjects')}</p> */}
             <AutoPagination className="m-0 w-auto justify-end" page={page} pageSize={pageSize} total={total} onChange={setPage}></AutoPagination>
         </div>
+        {/* 分享链接 */}
+        <ShareLink isShareLink={isShareLink} setIsShareLink={setIsShareLink} data={isShareLinkData}></ShareLink>
     </div>
 };
 
@@ -244,4 +276,19 @@ const useCreateTemp = () => {
             setOpen(!open)
         }
     }
-}
+}
+
+// 获取模板数据
+const useTemps = () => {
+    const [temps, setTemps] = useState([]);
+
+    const loadTemps = () => {
+        readTempsDatabase().then(setTemps);
+    };
+
+    useEffect(() => {
+        loadTemps();
+    }, []);
+
+    return [temps, loadTemps];
+};

+ 37 - 19
src/pages/SkillPage/tabTools.tsx

@@ -7,6 +7,10 @@ import { useEffect, useMemo, useRef, useState } from "react";
 import { useTranslation } from "react-i18next";
 import EditTool from "./components/EditTool";
 import ToolItem from "./components/ToolItem";
+import sousuo from "../../assets/npc/sousuo.png";
+import gongjuAdd from "../../assets/npc/gongjuAdd.png";
+import gongjuIcon from "../../assets/npc/gongjuIcon.png";
+import gongjuIcon1 from "../../assets/npc/gongjuIcon1.png";
 
 export default function tabTools({ select = null, onSelect }) {
   const [keyword, setKeyword] = useState(" ");
@@ -35,40 +39,54 @@ export default function tabTools({ select = null, onSelect }) {
 
   return (
     <div className="flex h-full relative" onClick={(e) => e.stopPropagation()}>
-      <div className="w-full flex h-full overflow-y-scroll scrollbar-hide relative top-[-60px]">
-        <div className="w-fit p-6">
-          <h1>{t("tools.addTool")}</h1>
-          <SearchInput
-            placeholder={t("tools.search")}
-            className="mt-6"
+      <p className="absolute text-[11px] px-[27px]" style={{color:"#666666"}}>在此页面管理您的自定义工具,对自定义工具创建、编辑等等</p>
+      <div className="mt-[24px] w-full flex overflow-y-scroll scrollbar-hide relative bg-[#121212] ml-[14px]" style={{ height: 'calc(100vh - 110px)' }}>
+        <div className="w-[260px]">
+          {/* <h1>{t("tools.addTool")}</h1> */}
+          {/* <SearchInput
+            placeholder="搜索"
+            className="w-[237px] h-[34px] bg-[#1A1A1A] ml-[14px] mt-[14px] text-[#666666]"
             onChange={(e) => setKeyword(e.target.value)}
-          />
-          <Button
+          /> */}
+          <div className="relative ml-[14px] mt-[14px]">
+            <img src={sousuo} className="absolute w-[14px] left-[14px] top-[10px]" alt="" />
+            <input placeholder="搜索"
+            className="w-[237px] h-[34px] bg-[#1A1A1A]  text-[#fff] pl-[40px]"
+            style={{borderRadius:"17px",outline:"none"}}
+            onChange={(e) => setKeyword(e.target.value)} type="text" />
+          </div>
+          {/* <Button
             className="mt-4 w-full"
             onClick={() => editRef.current.open()}
           >
             {t('create')}{t("tools.createCustomTool")}
-          </Button>
-          <div className="mt-4">
+          </Button> */}
+          <div className="w-[237px] h-[27px] bg-[#FFD025] m-[14px] flex justify-center items-center border-radius-14 cursor-pointer" onClick={() => editRef.current.open()}>
+            <img src={gongjuAdd} className="w-[14px]" alt="" />
+            <span className="text-[#333333] ml-[12px]">自定义工具</span>
+          </div>
+          <div className="mt-[14px]">
             <div
-              className={`flex cursor-pointer items-center gap-2 rounded-md px-4 py-2 transition-all duration-200 hover:bg-muted-foreground/10 ${type === "" && "bg-muted-foreground/10"
+              className={`flex cursor-pointer items-center w-[237px] h-[40px] ml-[14px] border-radius-7 pl-[14px] ${type === "" && "bg-[#2A271D] text-[#FFD54C]"
                 }`}
               onClick={() => setType("")}
             >
-              <PersonIcon />
-              <span>{t("tools.builtinTools")}</span>
+              {type === "" ? <img src={gongjuIcon1} className="w-[14px]" alt="" /> : <img src={gongjuIcon} className="w-[14px]" alt="" />}
+              {/* <PersonIcon /> */}
+              <span className="ml-[8px] text-[#999999]">内置工具</span>
             </div>
             <div
-              className={`mt-1 flex cursor-pointer items-center gap-2 rounded-md px-4 py-2 transition-all duration-200 hover:bg-muted-foreground/10 ${type === "edit" && "bg-muted-foreground/10"
+               className={`flex cursor-pointer items-center w-[237px] h-[40px] ml-[14px] border-radius-7 pl-[14px] ${type === "edit" && "bg-[#2A271D] text-[#FFD54C]"
                 }`}
               onClick={() => setType("edit")}
             >
-              <StarFilledIcon />
-              <span>{t("tools.customTools")}</span>
+              {type === "edit" ? <img src={gongjuIcon1} className="w-[14px]" alt="" /> : <img src={gongjuIcon} className="w-[14px]" alt="" />}
+              {/* <StarFilledIcon /> */}
+              <span className="ml-[8px] text-[#999999]">自定义工具</span>
             </div>
           </div>
         </div>
-        <div className="h-full w-full flex-1 overflow-auto bg-[#fff] p-5 pt-12 scrollbar-hide">
+        <div className="h-full w-full flex-1 overflow-auto p-5 pt-12 scrollbar-hide">
           <Accordion type="single" collapsible className="w-full">
             {options.length ? (
               options.map((el) => (
@@ -90,11 +108,11 @@ export default function tabTools({ select = null, onSelect }) {
         </div>
       </div>
       {/* footer */}
-      <div className="absolute bottom-0 left-0 flex h-16 w-full items-center justify-between bg-[#F4F5F8] px-10">
+      {/* <div className="absolute bottom-0 left-0 flex h-16 w-full items-center justify-between bg-[#F4F5F8] px-10">
         <p className="break-keep text-sm text-muted-foreground">
           {t("tools.manageCustomTools")}
         </p>
-      </div>
+      </div> */}
 
       <EditTool onReload={() => {
         // 切换自定义工具 并 刷新

+ 6 - 2
src/pages/SkillPage/temps.tsx

@@ -17,8 +17,12 @@ import { SpotlightCard } from "@lobehub/ui";
 import { Flexbox } from 'react-layout-kit';
 import robot from "../../assets/robot.png";
 import del from "../../assets/npc/del.png";
+import { useNavigate } from "react-router-dom";
 
-export default function Templates({ onBack, onChange }) {
+export default function Templates() {
+    const navigate = useNavigate()
+
+    const onChange = () => { }
     const { t } = useTranslation()
 
     const [temps, setTemps] = useState([])
@@ -107,7 +111,7 @@ export default function Templates({ onBack, onChange }) {
         </Flexbox>
       );
     return <div className="p-6 h-screen overflow-y-auto temps">
-        <div className="fanhui"  onClick={onBack}>
+        <div className="fanhui"  onClick={() => navigate('/build/skills')}>
             返 回
         </div>
         {/* <p className="text-gray-500">{t('skills.skillTemplateManagement')}</p> */}

+ 11 - 0
src/routes.tsx

@@ -11,6 +11,7 @@ import Report from "./pages/Report";
 import SkillChatPage from "./pages/ChatAppPage";
 import ChatShare from "./pages/ChatAppPage/chatShare";
 import SkillAssisPage from "./pages/SkillPage/tabAssistant";
+import EditAssistantPage from "./pages/SkillPage/editAssistant";
 import SkillsPage from "./pages/SkillPage/tabSkills";
 import SkillToolsPage from "./pages/SkillPage/tabTools";
 import SkillPage from "./pages/SkillPage";
@@ -18,6 +19,7 @@ import SkillPage from "./pages/SkillPage";
 import L2Edit from "./pages/SkillPage/l2Edit";
 import SystemPage from "./pages/SystemPage";
 import BuildLayout from "./layout/BuildLayout";
+import Templates from "./pages/SkillPage/temps";
 
 // react 与 react router dom版本不匹配
 // const FileLibPage = lazy(() => import(/* webpackChunkName: "FileLibPage" */ "./pages/FileLibPage"));
@@ -47,6 +49,9 @@ const router = createBrowserRouter([
           { path: "", element: <Navigate to="assist" replace /> },
         ]
       },
+      { path: "build/skill", element: <L2Edit /> },
+      { path: "build/skill/:id/:vid", element: <L2Edit /> },
+      { path: "build/temps", element: <Templates /> },
       { path: "model", element: <ModelPage /> },
       { path: "sys", element: <SystemPage /> },
     ],
@@ -58,6 +63,12 @@ const router = createBrowserRouter([
       { path: "", element: <FlowPage /> }
     ]
   },
+  {
+    path: "/assistant/:id/",
+    children: [
+      { path: "", element: <EditAssistantPage /> }
+    ]
+  },
   // 独立会话页
   { path: "/chat", element: <SkillChatPage /> },
   { path: "/chat/:id/", element: <ChatShare /> },

+ 77 - 0
src/store/assistantStore.tsx

@@ -0,0 +1,77 @@
+import { AssistantDetail } from '@/types/assistant'
+import { create } from 'zustand'
+import { getAssistantDetailApi } from '../controllers/API/assistant'
+
+/**
+ * 助手编辑管理
+ */
+
+type State = {
+  changed: boolean,
+  assistantState: AssistantDetail
+}
+
+type Actions = {
+  dispatchAssistant: (action: Action, assistantState: Partial<AssistantDetail>) => void,
+  loadAssistantState: (id: string) => Promise<any>
+  saveAfter: () => void
+  destroy: () => void
+}
+
+type Action = 'setBaseInfo' | 'setting' | 'setPrompt' | 'setGuideword' | 'setTools' | 'setFlows' | 'setQuestion'
+
+const assistantReducer = (state: State, action: Action, data: Partial<AssistantDetail>) => {
+  console.log('action :>> ', action, data);
+  return { changed: true, assistantState: { ...state.assistantState, ...data } }
+  // switch (action) {
+  //   case 'setBaseInfo':
+  //     return { assistantState: { ...state.assistantState, ...data } }
+  //   default:
+  //     return state
+  // }
+}
+
+
+const assistantTemp = {
+  id: 3,
+  name: "",
+  desc: "",
+  logo: "",
+  prompt: "",
+  guide_word: "",
+  guide_question: [],
+  model_name: "",
+  temperature: 0.5,
+  status: 0,
+  user_id: 1,
+  create_time: "",
+  update_time: "",
+  tool_list: [],
+  flow_list: [],
+  knowledge_list: [],
+}
+
+export const useAssistantStore = create<State & Actions>((set) => ({
+  changed: false,
+  assistantState: { ...assistantTemp },
+  dispatchAssistant: (action: Action, data: Partial<AssistantDetail>) => set((state) => assistantReducer(state, action, data)),
+  // 加载助手状态
+  loadAssistantState: (id) => {
+    return getAssistantDetailApi(id).then(data => {
+      set({
+        assistantState: {
+          ...data,
+          // 补一个空行
+          guide_question: data.guide_question ? [...data.guide_question, ''] : ['']
+        }
+      })
+      return data
+    })
+  },
+  saveAfter() {
+    set({ changed: false })
+  },
+  destroy: () => {
+    set({ assistantState: { ...assistantTemp } })
+  }
+}))

+ 267 - 0
src/store/diffFlowStore.tsx

@@ -0,0 +1,267 @@
+/**
+ * 技能组件版本效果对比
+ */
+import { generateUUID } from "@/components/bs-ui/utils"
+import { getVersionDetails, runTestCase } from "@/controllers/API/flow"
+import { captureAndAlertRequestErrorHoc } from "@/controllers/request"
+import { create } from "zustand"
+
+const enum RunningType {
+    /** 全量对比 */
+    All = 'all',
+    /** 列对比 */
+    Col = 'col',
+    /** 行对比 */
+    Row = 'row',
+    /** 无对比 */
+    None = ''
+}
+
+type State = {
+    mulitVersionFlow: any[],
+    /** 用例问题列表 */
+    questions: { id: string, q: string, ready: boolean }[],
+    /** 运行过一次 */
+    running: boolean,
+    /** 测试运行按钮状态 */
+    runningType: RunningType,
+    /** 版本运行按钮状态 */
+    readyVersions: { [verstion in string]: boolean }
+    /** 单元格ref */
+    cellRefs: { [key in string]: any }
+}
+
+type Actions = {
+    /** 初始化版本技能 */
+    initFristVersionFlow(versionId: string): void,
+    /** 添加空的对比版本 */
+    addEmptyVersionFlow(): void,
+    /** 添加对比版本技能 */
+    addVersionFlow(versionId: string, index: number): void,
+    /** 移除对比版本技能 */
+    removeVersionFlow(index: number): void,
+    /** 更新版本运行状态 */
+    // updateReadyVersions(version: string): void,
+    /** 上传覆盖问题列表 */
+    overQuestions(list: string[]): void,
+    /** 添加问题 */
+    addQuestion(q: string): void
+}
+
+export const useDiffFlowStore = create<State & Actions>((set, get) => ({
+    mulitVersionFlow: [],
+    questions: [],
+    readyVersions: {},
+    running: false,
+    runningType: RunningType.None,
+    cellRefs: {},
+    initFristVersionFlow(versionId) {
+        getVersionDetails(versionId).then(version => {
+            set({
+                mulitVersionFlow: [version, null],
+                questions: [],
+                readyVersions: {},
+                running: false,
+                runningType: RunningType.None,
+                cellRefs: {},
+            })
+        })
+    },
+    addEmptyVersionFlow() {
+        set((state) => ({ mulitVersionFlow: [...state.mulitVersionFlow, null] }))
+    },
+    addVersionFlow(versionId, index) {
+        const { running, readyVersions } = get()
+        // 标记可运行状态
+        if (running) {
+            set({ readyVersions: { ...readyVersions, [versionId]: true } })
+        }
+
+        getVersionDetails(versionId).then(version => {
+            set((state) => {
+                // 填充flow
+                state.mulitVersionFlow[index] = version
+                return { mulitVersionFlow: [...state.mulitVersionFlow] }
+            })
+        })
+    },
+    removeVersionFlow(index) {
+        set((state) => ({
+            mulitVersionFlow:
+                state.mulitVersionFlow.filter((_, i) => i !== index)
+        }))
+    },
+    // updateReadyVersions(version) {
+    //     if (get().running) {
+    //         set((state) => ({
+    //             readyVersions: {
+    //                 ...state.readyVersions,
+    //                 [version]: true
+    //             }
+    //         }))
+    //     }
+    // },
+    overQuestions(list) {
+        set(() => ({
+            questions: list.splice(0, 20).map(q => ({
+                id: generateUUID(5),
+                q,
+                ready: get().running
+            }))
+        }))
+    },
+    addQuestion(q) {
+        set((state) => ({
+            questions: [...state.questions, { q, id: generateUUID(5), ready: get().running }]
+        }))
+    },
+    updateQuestion(q, index) {
+        set((state) => ({
+            questions: state.questions.map((el, i) => i === index ? { ...el, q, ready: get().running } : el)
+        }))
+    },
+    removeQuestion(index) {
+        set((state) => ({
+            questions: state.questions.filter((_, i) => i !== index)
+        }))
+    },
+    addCellRef: (key, ref) => {
+        set(state => {
+            return { cellRefs: { ...state.cellRefs, [key]: ref } };
+        })
+    },
+    removeCellRef: (key) => set(state => {
+        const newCellRefs = { ...state.cellRefs }
+        delete newCellRefs[key]
+        return { cellRefs: newCellRefs };
+    }),
+    async allRunStart(nodeId, inputs) {
+        set((state) => ({
+            readyVersions: {},
+            questions: state.questions.map(el => ({ ...el, ready: false })),
+            runningType: RunningType.All,
+            running: true
+        }))
+        const questions = get().questions
+        const versions = get().mulitVersionFlow
+
+        await runTest({
+            questions,
+            questionIndexs: questions.map((_, index) => index),
+            versionIds: versions.filter(el => el).map(version => version?.id),
+            nodeId,
+            inputs,
+            refs: get().cellRefs
+        })
+        set({ runningType: RunningType.None })
+    },
+    async rowRunStart(qIndex, nodeId, inputs) {
+        set((state) => ({
+            questions: state.questions.map((el, i) => qIndex === i ? { ...el, ready: false } : el),
+            runningType: RunningType.Row
+        }))
+        const questions = get().questions
+        const versions = get().mulitVersionFlow
+
+        await runTest({
+            questions,
+            questionIndexs: [qIndex],
+            versionIds: versions.filter(el => el).map(version => version?.id),
+            nodeId,
+            inputs,
+            refs: get().cellRefs
+        })
+        set({ runningType: RunningType.None })
+    },
+    async colRunStart(versionId, nodeId, inputs) {
+        set((state) => ({
+            readyVersions: { ...state.readyVersions, [versionId]: false },
+            runningType: RunningType.Col
+        }))
+        const questions = get().questions
+
+        await runTest({
+            questions,
+            questionIndexs: questions.map((_, index) => index),
+            versionIds: [versionId],
+            nodeId,
+            inputs,
+            refs: get().cellRefs
+        })
+        set({ runningType: RunningType.None })
+    }
+}))
+
+
+/**
+ * 运行测试用例
+ * @param questions 所有问题列表
+ * @param questionIndexs 问题索引
+ * @param nodeId 节点id
+ * @param versionIds 版本id
+ * @param refs 单元格ref
+ */
+const runTest = ({ questions, questionIndexs, nodeId, versionIds, inputs, refs }) => {
+    // loading
+    // console.log(refs, 222);
+    const runIds = []
+    questionIndexs.forEach(qIndex => {
+        versionIds.forEach(versionId => {
+            refs[`${qIndex}-${versionId}`].current.loading()
+            runIds.push(`${qIndex}-${versionId}`)
+        })
+    });
+    // 运行
+    const data = JSON.stringify({
+        question_list: questionIndexs.map(qIndex => questions[qIndex].q),
+        version_list: versionIds,
+        inputs,
+        node_id: nodeId
+    })
+
+    return new Promise((resolve, reject) => {
+        const apiUrl = `/api/v1/flows/compare/stream?data=${encodeURIComponent(data)}`;
+        const eventSource = new EventSource(apiUrl);
+
+        eventSource.onmessage = (event) => {
+            if (!event.data) {
+                return;
+            }
+            const parsedData = JSON.parse(event.data);
+            const { type, question_index, version_id, answer } = parsedData;
+            if (!type) {
+                refs[`${questionIndexs[question_index]}-${version_id}`].current.setData(answer)
+            } else if (type === 'end') {
+                resolve('')
+            }
+        }
+
+        eventSource.onerror = (error: any) => {
+            console.error('event :>> ', error);
+            eventSource.close();
+            runIds.forEach(id => {
+                refs[id].current.loaded()
+            })
+
+            reject(error);
+        }
+    })
+    // runTestCaseStream()
+    // return captureAndAlertRequestErrorHoc(runTestCase({
+    //     question_list: questionIndexs.map(qIndex => questions[qIndex].q),
+    //     version_list: versionIds,
+    //     inputs,
+    //     node_id: nodeId
+    // }).then(data => {
+    //     data.forEach((row, rowIndex) => {
+    //         Object.keys(row).forEach(vId => {
+    //             refs[`${questionIndexs[rowIndex]}-${vId}`].current.setData(row[vId])
+    //         })
+    //     })
+    // }), () => {
+    //     // error callback
+    //     runIds.forEach(id => {
+    //         refs[id].current.loaded()
+    //     })
+    // })
+}

+ 293 - 7
src/style/zk.scss

@@ -90,7 +90,7 @@
     width: 100%;
     z-index: 1;
     .xinDuiHua-btn{
-      width: 204px;
+      width: 260px;
       height: 35px;
       background: #0D0D0D;
       border-radius: 18px;
@@ -2439,7 +2439,9 @@
   .border-radius-14{
     border-radius: 14px;
   }
-
+  .border-radius-7{
+    border-radius: 7px;
+  }
   .skillSettings{
     width: 542px;
     margin: 0 auto;
@@ -2575,6 +2577,8 @@
     }
   }
   .Skill_box{
+    height: 100%;
+    padding-bottom: 30px;
     .css-15l7r2q{
       gap: 36px!important;
     }
@@ -2597,10 +2601,44 @@
         background: #111111;
       }
     }
+    .selectNpcFlexboxZiDing{
+      height: 176px;
+      >div{
+        margin-left: 14px;
+        margin-top: 27px;
+        display: flex;
+        align-items: center;
+        >img{
+          width: 15px;
+        }
+        >span{
+          font-family: PingFang SC;
+          font-weight: 400;
+          font-size: 15px;
+          color: #FFFFFF;
+          margin-left: 8px;
+        }
+      }
+      >p{
+        margin-top: 20px;
+        margin-left: 14px;
+        font-family: PingFang SC;
+        font-weight: 300;
+        font-size: 12px;
+        color: #CCCCCC;
+      }
+      >img{
+        width: 68px;
+        margin: 7px 0;
+        margin-left: 180px;
+      }
+    }
     .selectNpcFlexbox{
       width: 100%;
-      height: 100%;
-      display: block;
+      height: 176px;
+      // display: flex;
+      // flex-direction: column;
+      // justify-content:space-between;
       /* padding: 14px; */
       position: relative;
       padding-bottom: 55px;
@@ -2664,7 +2702,8 @@
         }
       }
       .npcInfoItemBg{
-        position: relative;
+        width: 100%;
+        position: absolute;
         overflow: hidden;
         height: 64px;
         margin-bottom: -56px;
@@ -3314,7 +3353,8 @@
       }
     }
     .selectNpcFlexbox{
-      height: 127px;
+      // height: 127px;
+      padding-bottom: 14px;
       display: block;
       /* padding: 14px; */
       >div:nth-of-type(2){
@@ -3804,4 +3844,250 @@
         }
       }
     }
-  }
+  }
+
+  .npcInput{
+    // margin-left: 14px;
+    // width: 89%!important;
+    // height: 34px!important;
+    background: #1A1A1A!important;
+    border-radius: 7px!important;
+    outline: none!important;
+    --tw-ring-color:none!important;
+    border: none!important;
+    --tw-ring-offset-color:none!important;
+    color: #fff!important;
+    
+    &::-webkit-input-placeholder{
+      color: #666!important;
+    }
+    // outline: #997e1f;
+    &:focus{
+      // border: none!important;
+      outline: 1px solid #997e1f!important;
+    }
+  }
+  .npcInput1{
+    background: #262626!important;
+    border-radius: 7px!important;
+    outline: none!important;
+    --tw-ring-color:none!important;
+    border: none!important;
+    --tw-ring-offset-color:none!important;
+    color: #fff!important;
+    
+    &::-webkit-input-placeholder{
+      color: #666!important;
+    }
+    // outline: #997e1f;
+    &:focus{
+      // border: none!important;
+      outline: 1px solid #997e1f!important;
+    }
+  }
+  .npcInput2{
+    background: #1a1a1a!important;
+    border-radius: 7px!important;
+    outline: none!important;
+    --tw-ring-color:none!important;
+    border: none!important;
+    --tw-ring-offset-color:none!important;
+    color: #fff!important;
+    
+    &::-webkit-input-placeholder{
+      color: #666!important;
+    }
+    // outline: #997e1f;
+    &:focus{
+      // border: none!important;
+      outline: 1px solid #997e1f!important;
+    }
+  }
+  .npcInput3{
+    background: #0F0F0F!important;
+    border-radius: 17px!important;
+    outline: none!important;
+    --tw-ring-color:none!important;
+    border: none!important;
+    --tw-ring-offset-color:none!important;
+    color: #fff!important;
+    
+    &::-webkit-input-placeholder{
+      color: #666!important;
+    }
+    // outline: #997e1f;
+    &:focus{
+      // border: none!important;
+      outline: 1px solid #997e1f!important;
+    }
+  }
+  .bs-chat-bg{
+    .questionTextarea{
+      width: 95%!important;
+      margin-left: 2.5%;
+      height: 40px!important;
+      background: #1A1A1A!important;
+      box-shadow: 0px 2px 7px 0px rgba(0,1,51,0.15)!important;
+      border-radius: 20px!important;
+      padding: 10px 0;
+      padding-left: 14px;
+      font-family: PingFang SC;
+      font-weight: 400;
+      font-size: 11px;
+      color: #FFFFFF;
+    }
+  }
+
+  .skillSheet{
+    height: 100%;
+    padding: 0 27px 30px 27px;
+    padding-bottom: 30px;
+    .css-15l7r2q{
+      gap: 27px!important;
+    }
+    .acss-134lb3j{
+      --rows: 10!important;
+      --max-item-width: 298px!important;
+    }
+    .skillSheetSpotlightCard{
+      .acss-1iddemf{
+        /* background: rgba(45, 45, 45, 0.75); */
+        background: #121212;
+      }
+      .hover-card::before{
+        background: radial-gradient(800px circle at var(--mouse-x) var(--mouse-y), rgba(255, 255, 255, 0.06), transparent 40%);
+      }
+      .hover-card::after{
+        background: radial-gradient(600px circle at var(--mouse-x) var(--mouse-y), rgba(255, 255, 255, 0.4), transparent 40%);
+      }
+      .acss-13wwyd1{
+        background: #111111;
+      }
+    }
+    .selectNpcFlexbox{
+      min-height: 127px;
+      display: block;
+      /* padding: 14px; */
+      >div:nth-of-type(2){
+        display: flex;
+        align-items: self-start;
+        padding: 14px 14px 0 14px;
+        >div{
+          margin-left: 15px;
+          >p{
+            font-family: PingFang SC;
+            font-weight: 400;
+            font-size: 14px;
+            color: #FFFFFF;
+          }
+          >div{
+            display: flex;
+            >div{
+              margin-top: 4px;
+              padding: 0 6px;
+              height: 17px;
+              background: rgba(255, 213, 76, 0.15);
+              border-radius: 8px;
+              display: flex;
+              justify-content: center;
+              align-items: center;
+              margin-right: 7px;
+              font-family: PingFang SC;
+              font-weight: 400;
+              font-size: 10px;
+              color: #CCCCCC;
+            }
+          }
+        }
+      }
+      >p{
+        padding: 0 14px;
+        line-height: 21px;
+        font-family: PingFang SC;
+        font-weight: 300;
+        font-size: 12px;
+        color: #CCCCCC;
+      }
+      .npcInfoItemBg{
+        position: relative;
+        overflow: hidden;
+        height: 64px;
+        margin-bottom: -56px;
+        background: rgba(0, 0, 0, 0.06);
+        -webkit-mask-image: linear-gradient(to bottom, #fff, transparent);
+        mask-image: linear-gradient(to bottom, #fff, transparent);
+        display: -webkit-box;
+        display: -webkit-flex;
+        display: -ms-flexbox;
+        display: flex;
+        -webkit-flex-direction: column;
+        -ms-flex-direction: column;
+        flex-direction: column;
+        -webkit-box-pack: center;
+        -ms-flex-pack: center;
+        -webkit-justify-content: center;
+        justify-content: center;
+        -webkit-align-items: center;
+        -webkit-box-align: center;
+        -ms-flex-align: center;
+        align-items: center;
+        >span{
+          width: 180px;
+          height: 180px;
+          font-size: 18px;
+          cursor: default;
+          cursor: pointer;
+          display: -webkit-box;
+          display: -webkit-flex;
+          display: -ms-flexbox;
+          display: flex;
+          -webkit-align-items: center;
+          -webkit-box-align: center;
+          -ms-flex-align: center;
+          align-items: center;
+          -webkit-box-pack: center;
+          -ms-flex-pack: center;
+          -webkit-justify-content: center;
+          justify-content: center;
+          background: rgba(0, 0, 0, 0);
+          border: 1px solid transparent;
+          position: absolute;
+          top: -50%;
+          -webkit-filter: blur(50px) saturate(2);
+          filter: blur(50px) saturate(2);
+          >span{
+            transform: scale(1);
+            font-size: 140px;
+            font-weight: 700;
+            line-height: 1 !important;
+            color: #fff;
+            >div{
+              height: 130px;
+              width: 130px;
+              position: relative;
+              display: -webkit-box;
+              display: -webkit-flex;
+              display: -ms-flexbox;
+              display: flex;
+              -webkit-align-items: center;
+              -webkit-box-align: center;
+              -ms-flex-align: center;
+              align-items: center;
+              -webkit-box-pack: center;
+              -ms-flex-pack: center;
+              -webkit-justify-content: center;
+              justify-content: center;
+              line-height: 1;
+              text-align: center;
+            }
+          }
+        }
+      }
+      .del{
+        position: absolute;
+        right: 14px;
+        top: 14px;
+      }
+    }
+  }
+  

+ 2 - 0
src/types/flow/index.ts

@@ -16,6 +16,8 @@ export type FlowType = {
   date_created?: string;
   updated_at?: string;
   last_tested_version?: string;
+  avatar_img?: string;
+  avatar_color?: string;
 };
 export type NodeType = {
   id: string;

+ 1 - 0
src/util/hook.ts

@@ -76,6 +76,7 @@ export function useTable<T extends object>(param, apiFun) {
         apiFun({ ...page, ...paramRef.current }).then(res => {
             if (requestId !== requestIdRef.current) return
             if (!("total" in res)) return console.error('该接口不支持分页,无法正常使用 useTable')
+            // res.data.unshift({type:0})
             setData(res.data);
             setTotal(res.total);
             setLoading(false);

Some files were not shown because too many files changed in this diff