Browse Source

Merge branch 'master' of http://101.126.133.7:9001/www/helper-ai-web into dev-rmd

Rmengdi 2 weeks ago
parent
commit
d32a899ad2
58 changed files with 2000 additions and 855 deletions
  1. 2 2
      README.md
  2. 8 0
      SPONSOR.md
  3. 55 0
      docker-compose/gpts-mj-file/docker-compose.yml
  4. 27 0
      docker-compose/gpts-mj-file/nginx/nginx.conf
  5. 10 0
      docker-compose/gpts-mj-file/readme.md
  6. 6 0
      docker-compose/gpts-mj-file/start.sh
  7. 5 0
      docker-compose/gpts-mj-file/start_h.sh
  8. BIN
      docs/alipay.jpg
  9. BIN
      docs/c1-2.8.0.png
  10. BIN
      docs/c1-2.9.0.png
  11. BIN
      docs/c1.png
  12. BIN
      docs/c2-2.8.0.png
  13. BIN
      docs/c2-2.9.0.png
  14. BIN
      docs/c2.png
  15. BIN
      docs/check_error.jpg
  16. BIN
      docs/docker.png
  17. BIN
      docs/gptbase.jpg
  18. BIN
      docs/gpts.jpg
  19. BIN
      docs/gpts1.jpg
  20. BIN
      docs/mj1.jpg
  21. BIN
      docs/mj2.jpg
  22. BIN
      docs/mj2a1.jpg
  23. BIN
      docs/mj2a2.jpg
  24. BIN
      docs/mj2a3.jpg
  25. BIN
      docs/mj3a2.jpg
  26. BIN
      docs/mj4a1.png
  27. BIN
      docs/mjs1.jpg
  28. BIN
      docs/mjs2.jpg
  29. BIN
      docs/mjs3.jpg
  30. BIN
      docs/tts-whisper.png
  31. BIN
      docs/tts.jpg
  32. BIN
      docs/wxpay.jpg
  33. 35 0
      nginx.conf
  34. 642 72
      package-lock.json
  35. 1 0
      package.json
  36. 298 184
      pnpm-lock.yaml
  37. 31 0
      public/gpts.json
  38. 2 0
      src/api/mjapi.ts
  39. 4 1
      src/api/model.ts
  40. 6 2
      src/api/openapi.ts
  41. BIN
      src/assets/01.png
  42. 5 4
      src/components/common/PromptStore/index.vue
  43. 3 1
      src/router/index.ts
  44. 1 1
      src/store/modules/app/helper.ts
  45. 1 1
      src/utils/request/req.ts
  46. 31 11
      src/views/chat/components/Message/Text.vue
  47. 19 19
      src/views/chat/components/Message/index.vue
  48. 4 1
      src/views/chat/components/Message/style.less
  49. 474 332
      src/views/chat/index.vue
  50. 1 1
      src/views/fanyi/components/textComponent.vue
  51. 16 5
      src/views/knowledge/index.vue
  52. 2 2
      src/views/login/index.vue
  53. 11 5
      src/views/mj/aiGpt.vue
  54. 36 9
      src/views/mj/aiGptInput.vue
  55. 197 197
      src/views/mj/aiModel.vue
  56. 62 0
      src/views/wxbot/layout.vue
  57. 1 1
      vercel.json
  58. 4 4
      vite.config.ts

+ 2 - 2
README.md

@@ -1,2 +1,2 @@
-# ruoyi-web-pro
-基于ruoyi-plus实现AI聊天和绘画功能
+# ruoyi-web
+ruoyi-ai 用户端页面

+ 8 - 0
SPONSOR.md

@@ -0,0 +1,8 @@
+# Sponsor My Open Source Works
+
+如果我的开源项目对你有帮助,请考虑通过以下任意一种方式赞助:
+
+## 微信赞助
+![微信](./docs/wxpay.jpg)
+## 支付宝赞助
+![支付宝](./docs/alipay.jpg)

+ 55 - 0
docker-compose/gpts-mj-file/docker-compose.yml

@@ -0,0 +1,55 @@
+version: '3'
+
+services:
+  gptweb:
+    container_name: chatgpt-web-midjourney-proxy
+    image: ydlhero/chatgpt-web-midjourney-proxy # 总是使用latest,更新时重新pull该tag镜像即可
+    ports:
+      - 6050:3002
+    environment:
+      TZ: Asia/Shanghai # 指定时区
+      # 必选
+      OPENAI_API_KEY: sk-xxxx
+      # API接口地址,可选,设置 OPENAI_API_KEY 时可用
+      OPENAI_API_BASE_URL:
+      # 访问权限密钥,可选 注意修改
+      AUTH_SECRET_KEY: mygod
+      # midjourney 服务器地址,可选 可用下面的 http://midjourney-proxy:8080
+      MJ_SERVER: http://midjourney-proxy:8080
+      # midjourney API密钥,可选
+      MJ_API_SECRET: mygod
+      #API_UPLOADER 是否可以上传 1 可以其他都不可以,可选
+      API_UPLOADER: 1
+      #FILE_SERVER 文件服务器,可选 可以用下面的 http://fileserver:3012
+      FILE_SERVER: http://fileserver:3012
+      #爆破:验证次数 注意: vercel 不支持 nginx 请设置  proxy_set_header   X-Forwarded-For  $remote_addr;
+      AUTH_SECRET_ERROR_COUNT: 3
+      #爆破:验证停留时间 单位分钟 注意: vercel 不支持
+      AUTH_SECRET_ERROR_TIME: 10
+
+  # midjourney服务 可选
+  midjourney-proxy:
+    image: novicezk/midjourney-proxy:2.5.5
+    restart: always
+    # ports:
+    #   - 6013:8080 #映射端口
+    environment:
+      TZ: Asia/Shanghai # 指定时区
+      mj.discord.guild-id: xxx # xxx 如何获取
+      mj.discord.channel-id: xxx
+      mj.discord.user-token: xxx
+      mj.api-secret: mygod # MJ_API_SECRET
+  
+  #文件服务 可选
+  fileserver:
+    image: ydlhero/file-server:latest
+    restart: always
+    environment:
+      TZ: Asia/Shanghai # 指定时区
+      #对外显示的域名
+      SERVER_NAME: http://myip:3102
+    ports:
+      - "3102:3102"
+    volumes:
+      - /data/uploads:/app/uploads
+

+ 27 - 0
docker-compose/gpts-mj-file/nginx/nginx.conf

@@ -0,0 +1,27 @@
+server {
+	listen 80;
+	server_name  localhost;
+	charset utf-8;
+	error_page   500 502 503 504  /50x.html;
+	
+	# 防止爬虫抓取
+	if ($http_user_agent ~* "360Spider|JikeSpider|Spider|spider|bot|Bot|2345Explorer|curl|wget|webZIP|qihoobot|Baiduspider|Googlebot|Googlebot-Mobile|Googlebot-Image|Mediapartners-Google|Adsbot-Google|Feedfetcher-Google|Yahoo! Slurp|Yahoo! Slurp China|YoudaoBot|Sosospider|Sogou spider|Sogou web spider|MSNBot|ia_archiver|Tomato Bot|NSPlayer|bingbot")
+	{
+		return 403;
+	}
+	
+	location / {
+			root /usr/share/nginx/html;
+   		try_files $uri /index.html;
+	}
+
+	location /api {
+			proxy_set_header   X-Real-IP $remote_addr; #转发用户IP
+			proxy_pass http://app:3002;
+	}
+
+	proxy_set_header Host $host;
+	proxy_set_header X-Real-IP $remote_addr;
+	proxy_set_header REMOTE-HOST $remote_addr;
+	proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+}

+ 10 - 0
docker-compose/gpts-mj-file/readme.md

@@ -0,0 +1,10 @@
+### docker-compose 部署教程 
+
+```shell
+    git clone https://github.com/Dooy/chatgpt-web-midjourney-proxy.git
+    cd chatgpt-web-midjourney-proxy/docker-compose/gpts-mj-file
+    #修改 docker-compose.yml 文件配置问文件
+    
+    #如果执行有问题 请执行 start_h.sh
+    start.sh 
+  ``` 

+ 6 - 0
docker-compose/gpts-mj-file/start.sh

@@ -0,0 +1,6 @@
+#!/bin/bash
+set -e
+# docker compose pull
+# docker compose up -d --remove-orphans
+docker-compose pull
+docker-compose up -d --remove-orphans

+ 5 - 0
docker-compose/gpts-mj-file/start_h.sh

@@ -0,0 +1,5 @@
+#!/bin/bash
+set -e
+docker compose pull
+docker compose up -d --remove-orphans
+ 

BIN
docs/alipay.jpg


BIN
docs/c1-2.8.0.png


BIN
docs/c1-2.9.0.png


BIN
docs/c1.png


BIN
docs/c2-2.8.0.png


BIN
docs/c2-2.9.0.png


BIN
docs/c2.png


BIN
docs/check_error.jpg


BIN
docs/docker.png


BIN
docs/gptbase.jpg


BIN
docs/gpts.jpg


BIN
docs/gpts1.jpg


BIN
docs/mj1.jpg


BIN
docs/mj2.jpg


BIN
docs/mj2a1.jpg


BIN
docs/mj2a2.jpg


BIN
docs/mj2a3.jpg


BIN
docs/mj3a2.jpg


BIN
docs/mj4a1.png


BIN
docs/mjs1.jpg


BIN
docs/mjs2.jpg


BIN
docs/mjs3.jpg


BIN
docs/tts-whisper.png


BIN
docs/tts.jpg


BIN
docs/wxpay.jpg


+ 35 - 0
nginx.conf

@@ -0,0 +1,35 @@
+worker_processes 1;
+
+events {
+    worker_connections 1024;
+}
+
+http {
+    include       mime.types;
+    default_type  application/octet-stream;
+
+    sendfile        on;
+    keepalive_timeout  65;
+
+    server {
+        listen       8081;
+        server_name  localhost;
+
+        location / {
+            root   /usr/share/nginx/html/web;
+            index  index.html index.htm;
+            try_files $uri $uri/ /index.html;
+        }
+
+        location /api/{
+            proxy_pass http://ruoyi-server:6039/; 
+            # 避免出现反代https域名出现502错误
+            proxy_ssl_server_name on;
+        }
+
+        error_page   500 502 503 504  /50x.html;
+        location = /50x.html {
+            root   /usr/share/nginx/html;
+        }
+    }
+}

+ 642 - 72
package-lock.json

@@ -8,6 +8,7 @@
       "name": "ruoyi-web",
       "version": "2.0.0",
       "dependencies": {
+        "@tinyflow-ai/vue": "^0.0.9",
         "@traptitech/markdown-it-katex": "^3.6.0",
         "@vicons/ionicons5": "^0.13.0",
         "@vue-office/pdf": "^2.0.10",
@@ -317,13 +318,13 @@
       }
     },
     "node_modules/@ampproject/remapping": {
-      "version": "2.2.1",
-      "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.1.tgz",
-      "integrity": "sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg==",
-      "dev": true,
+      "version": "2.3.0",
+      "resolved": "https://registry.npmmirror.com/@ampproject/remapping/-/remapping-2.3.0.tgz",
+      "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==",
+      "license": "Apache-2.0",
       "dependencies": {
-        "@jridgewell/gen-mapping": "^0.3.0",
-        "@jridgewell/trace-mapping": "^0.3.9"
+        "@jridgewell/gen-mapping": "^0.3.5",
+        "@jridgewell/trace-mapping": "^0.3.24"
       },
       "engines": {
         "node": ">=6.0.0"
@@ -3123,14 +3124,14 @@
       }
     },
     "node_modules/@jridgewell/gen-mapping": {
-      "version": "0.3.3",
-      "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz",
-      "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==",
-      "dev": true,
+      "version": "0.3.8",
+      "resolved": "https://registry.npmmirror.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz",
+      "integrity": "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==",
+      "license": "MIT",
       "dependencies": {
-        "@jridgewell/set-array": "^1.0.1",
+        "@jridgewell/set-array": "^1.2.1",
         "@jridgewell/sourcemap-codec": "^1.4.10",
-        "@jridgewell/trace-mapping": "^0.3.9"
+        "@jridgewell/trace-mapping": "^0.3.24"
       },
       "engines": {
         "node": ">=6.0.0"
@@ -3140,16 +3141,15 @@
       "version": "3.1.0",
       "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz",
       "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==",
-      "dev": true,
       "engines": {
         "node": ">=6.0.0"
       }
     },
     "node_modules/@jridgewell/set-array": {
-      "version": "1.1.2",
-      "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz",
-      "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==",
-      "dev": true,
+      "version": "1.2.1",
+      "resolved": "https://registry.npmmirror.com/@jridgewell/set-array/-/set-array-1.2.1.tgz",
+      "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==",
+      "license": "MIT",
       "engines": {
         "node": ">=6.0.0"
       }
@@ -3171,21 +3171,15 @@
       "license": "MIT"
     },
     "node_modules/@jridgewell/trace-mapping": {
-      "version": "0.3.18",
-      "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.18.tgz",
-      "integrity": "sha512-w+niJYzMHdd7USdiH2U6869nqhD2nbfZXND5Yp93qIbEmnDNk7PD48o+YchRVpzMU7M6jVCbenTR7PA1FLQ9pA==",
-      "dev": true,
+      "version": "0.3.25",
+      "resolved": "https://registry.npmmirror.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz",
+      "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==",
+      "license": "MIT",
       "dependencies": {
-        "@jridgewell/resolve-uri": "3.1.0",
-        "@jridgewell/sourcemap-codec": "1.4.14"
+        "@jridgewell/resolve-uri": "^3.1.0",
+        "@jridgewell/sourcemap-codec": "^1.4.14"
       }
     },
-    "node_modules/@jridgewell/trace-mapping/node_modules/@jridgewell/sourcemap-codec": {
-      "version": "1.4.14",
-      "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz",
-      "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==",
-      "dev": true
-    },
     "node_modules/@juggle/resize-observer": {
       "version": "3.4.0",
       "resolved": "https://registry.npmmirror.com/@juggle/resize-observer/-/resize-observer-3.4.0.tgz",
@@ -3660,6 +3654,47 @@
         "sourcemap-codec": "^1.4.8"
       }
     },
+    "node_modules/@svelte-put/shortcut": {
+      "version": "3.1.1",
+      "resolved": "https://registry.npmmirror.com/@svelte-put/shortcut/-/shortcut-3.1.1.tgz",
+      "integrity": "sha512-2L5EYTZXiaKvbEelVkg5znxqvfZGZai3m97+cAiUBhLZwXnGtviTDpHxOoZBsqz41szlfRMcamW/8o0+fbW3ZQ==",
+      "license": "MIT",
+      "peerDependencies": {
+        "svelte": "^3.55.0 || ^4.0.0 || ^5.0.0"
+      }
+    },
+    "node_modules/@sveltejs/acorn-typescript": {
+      "version": "1.0.5",
+      "resolved": "https://registry.npmmirror.com/@sveltejs/acorn-typescript/-/acorn-typescript-1.0.5.tgz",
+      "integrity": "sha512-IwQk4yfwLdibDlrXVE04jTZYlLnwsTT2PIOQQGNLWfjavGifnk1JD1LcZjZaBTRcxZu2FfPfNLOE04DSu9lqtQ==",
+      "license": "MIT",
+      "peer": true,
+      "peerDependencies": {
+        "acorn": "^8.9.0"
+      }
+    },
+    "node_modules/@tinyflow-ai/ui": {
+      "version": "0.0.9",
+      "resolved": "https://registry.npmmirror.com/@tinyflow-ai/ui/-/ui-0.0.9.tgz",
+      "integrity": "sha512-dEm3/LGsqA8YOiQYyGu9XdzVvskmNsjrHhRGsT4CEA/jCzAN6ef82qH5AGrSUWljTvBghZOT3OLzjwycVYKaYQ==",
+      "license": "LGPL-3.0-or-later",
+      "dependencies": {
+        "@floating-ui/dom": "^1.6.13",
+        "@xyflow/svelte": "^0.1.31"
+      }
+    },
+    "node_modules/@tinyflow-ai/vue": {
+      "version": "0.0.9",
+      "resolved": "https://registry.npmmirror.com/@tinyflow-ai/vue/-/vue-0.0.9.tgz",
+      "integrity": "sha512-+/N8zpKUV85b5yLPprn0Ux7qMgzGWChHrHqR23+mMa6l9xznpL2B+tUP6UyXjF47snf7h4oypJSkyeMDl7fVFg==",
+      "license": "LGPL-3.0-or-later",
+      "dependencies": {
+        "@tinyflow-ai/ui": "0.0.9"
+      },
+      "peerDependencies": {
+        "vue": "^3.5.13"
+      }
+    },
     "node_modules/@traptitech/markdown-it-katex": {
       "version": "3.6.0",
       "resolved": "https://registry.npmjs.org/@traptitech/markdown-it-katex/-/markdown-it-katex-3.6.0.tgz",
@@ -3707,11 +3742,59 @@
       "integrity": "sha512-BG7fQKZ689HIoc5h+6D2Dgq1fABRa0RbBWKBd9SP/MVRVXROflpm5fhwyATX5duFmbStzyzyycPB8qUYKDH3NA==",
       "dev": true
     },
+    "node_modules/@types/d3-color": {
+      "version": "3.1.3",
+      "resolved": "https://registry.npmmirror.com/@types/d3-color/-/d3-color-3.1.3.tgz",
+      "integrity": "sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A==",
+      "license": "MIT"
+    },
+    "node_modules/@types/d3-drag": {
+      "version": "3.0.7",
+      "resolved": "https://registry.npmmirror.com/@types/d3-drag/-/d3-drag-3.0.7.tgz",
+      "integrity": "sha512-HE3jVKlzU9AaMazNufooRJ5ZpWmLIoc90A37WU2JMmeq28w1FQqCZswHZ3xR+SuxYftzHq6WU6KJHvqxKzTxxQ==",
+      "license": "MIT",
+      "dependencies": {
+        "@types/d3-selection": "*"
+      }
+    },
+    "node_modules/@types/d3-interpolate": {
+      "version": "3.0.4",
+      "resolved": "https://registry.npmmirror.com/@types/d3-interpolate/-/d3-interpolate-3.0.4.tgz",
+      "integrity": "sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA==",
+      "license": "MIT",
+      "dependencies": {
+        "@types/d3-color": "*"
+      }
+    },
+    "node_modules/@types/d3-selection": {
+      "version": "3.0.11",
+      "resolved": "https://registry.npmmirror.com/@types/d3-selection/-/d3-selection-3.0.11.tgz",
+      "integrity": "sha512-bhAXu23DJWsrI45xafYpkQ4NtcKMwWnAC/vKrd2l+nxMFuvOT3XMYTIj2opv8vq8AO5Yh7Qac/nSeP/3zjTK0w==",
+      "license": "MIT"
+    },
+    "node_modules/@types/d3-transition": {
+      "version": "3.0.9",
+      "resolved": "https://registry.npmmirror.com/@types/d3-transition/-/d3-transition-3.0.9.tgz",
+      "integrity": "sha512-uZS5shfxzO3rGlu0cC3bjmMFKsXv+SmZZcgp0KD22ts4uGXp5EVYGzu/0YdwZeKmddhcAccYtREJKkPfXkZuCg==",
+      "license": "MIT",
+      "dependencies": {
+        "@types/d3-selection": "*"
+      }
+    },
+    "node_modules/@types/d3-zoom": {
+      "version": "3.0.8",
+      "resolved": "https://registry.npmmirror.com/@types/d3-zoom/-/d3-zoom-3.0.8.tgz",
+      "integrity": "sha512-iqMC4/YlFCSlO8+2Ii1GGGliCAY4XdeG748w5vQUbevlbDu0zSjH/+jojorQVBK/se0j6DUFNPBGSqD3YWYnDw==",
+      "license": "MIT",
+      "dependencies": {
+        "@types/d3-interpolate": "*",
+        "@types/d3-selection": "*"
+      }
+    },
     "node_modules/@types/estree": {
       "version": "1.0.6",
       "resolved": "https://registry.npmmirror.com/@types/estree/-/estree-1.0.6.tgz",
       "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==",
-      "dev": true,
       "license": "MIT"
     },
     "node_modules/@types/hast": {
@@ -4478,11 +4561,40 @@
         "url": "https://github.com/sponsors/antfu"
       }
     },
+    "node_modules/@xyflow/svelte": {
+      "version": "0.1.32",
+      "resolved": "https://registry.npmmirror.com/@xyflow/svelte/-/svelte-0.1.32.tgz",
+      "integrity": "sha512-IS4aXbLC4SYdPuolMBzD2cMpyYOnK+S6i8ot+gQXdPpZJhrRpDiTEr8gFJgeLK2dHnW0n4p8uOALhtePyOPULw==",
+      "license": "MIT",
+      "dependencies": {
+        "@svelte-put/shortcut": "3.1.1",
+        "@xyflow/system": "0.0.53",
+        "classcat": "^5.0.4"
+      },
+      "peerDependencies": {
+        "svelte": "^3.0.0 || ^4.0.0 || ^5.0.0"
+      }
+    },
+    "node_modules/@xyflow/system": {
+      "version": "0.0.53",
+      "resolved": "https://registry.npmmirror.com/@xyflow/system/-/system-0.0.53.tgz",
+      "integrity": "sha512-QTWieiTtvNYyQAz1fxpzgtUGXNpnhfh6vvZa7dFWpWS2KOz6bEHODo/DTK3s07lDu0Bq0Db5lx/5M5mNjb9VDQ==",
+      "license": "MIT",
+      "dependencies": {
+        "@types/d3-drag": "^3.0.7",
+        "@types/d3-selection": "^3.0.10",
+        "@types/d3-transition": "^3.0.8",
+        "@types/d3-zoom": "^3.0.8",
+        "d3-drag": "^3.0.0",
+        "d3-selection": "^3.0.0",
+        "d3-zoom": "^3.0.0"
+      }
+    },
     "node_modules/acorn": {
-      "version": "8.11.3",
-      "resolved": "https://registry.npmmirror.com/acorn/-/acorn-8.11.3.tgz",
-      "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==",
-      "dev": true,
+      "version": "8.14.1",
+      "resolved": "https://registry.npmmirror.com/acorn/-/acorn-8.14.1.tgz",
+      "integrity": "sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg==",
+      "license": "MIT",
       "bin": {
         "acorn": "bin/acorn"
       },
@@ -4648,6 +4760,16 @@
       "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
       "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="
     },
+    "node_modules/aria-query": {
+      "version": "5.3.2",
+      "resolved": "https://registry.npmmirror.com/aria-query/-/aria-query-5.3.2.tgz",
+      "integrity": "sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==",
+      "license": "Apache-2.0",
+      "peer": true,
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
     "node_modules/arr-diff": {
       "version": "4.0.0",
       "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz",
@@ -4923,6 +5045,16 @@
         "proxy-from-env": "^1.1.0"
       }
     },
+    "node_modules/axobject-query": {
+      "version": "4.1.0",
+      "resolved": "https://registry.npmmirror.com/axobject-query/-/axobject-query-4.1.0.tgz",
+      "integrity": "sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==",
+      "license": "Apache-2.0",
+      "peer": true,
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
     "node_modules/babel-plugin-polyfill-corejs2": {
       "version": "0.3.3",
       "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.3.3.tgz",
@@ -5412,6 +5544,12 @@
         "node": ">=0.10.0"
       }
     },
+    "node_modules/classcat": {
+      "version": "5.0.5",
+      "resolved": "https://registry.npmmirror.com/classcat/-/classcat-5.0.5.tgz",
+      "integrity": "sha512-JhZUT7JFcQy/EzW605k/ktHtncoo9vnyW/2GspNYwFlN1C/WmjuV/xtS04e9SOkL2sTdw0VAZ2UGCcQ9lR6p6w==",
+      "license": "MIT"
+    },
     "node_modules/clean-regexp": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/clean-regexp/-/clean-regexp-1.0.0.tgz",
@@ -5522,6 +5660,16 @@
         "node": ">=0.8"
       }
     },
+    "node_modules/clsx": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmmirror.com/clsx/-/clsx-2.1.1.tgz",
+      "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==",
+      "license": "MIT",
+      "peer": true,
+      "engines": {
+        "node": ">=6"
+      }
+    },
     "node_modules/collection-visit": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz",
@@ -5900,6 +6048,111 @@
       "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.11.tgz",
       "integrity": "sha512-sa6P2wJ+CAbgyy4KFssIb/JNMLxFvKF1pCYCSXS8ZMuqZnMsrxqI2E5sPyoTpxoPU/gVZMzr2zjOfg8GIZOMsw=="
     },
+    "node_modules/d3-color": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmmirror.com/d3-color/-/d3-color-3.1.0.tgz",
+      "integrity": "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==",
+      "license": "ISC",
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/d3-dispatch": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmmirror.com/d3-dispatch/-/d3-dispatch-3.0.1.tgz",
+      "integrity": "sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg==",
+      "license": "ISC",
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/d3-drag": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmmirror.com/d3-drag/-/d3-drag-3.0.0.tgz",
+      "integrity": "sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg==",
+      "license": "ISC",
+      "dependencies": {
+        "d3-dispatch": "1 - 3",
+        "d3-selection": "3"
+      },
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/d3-ease": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmmirror.com/d3-ease/-/d3-ease-3.0.1.tgz",
+      "integrity": "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==",
+      "license": "BSD-3-Clause",
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/d3-interpolate": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmmirror.com/d3-interpolate/-/d3-interpolate-3.0.1.tgz",
+      "integrity": "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==",
+      "license": "ISC",
+      "dependencies": {
+        "d3-color": "1 - 3"
+      },
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/d3-selection": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmmirror.com/d3-selection/-/d3-selection-3.0.0.tgz",
+      "integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==",
+      "license": "ISC",
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/d3-timer": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmmirror.com/d3-timer/-/d3-timer-3.0.1.tgz",
+      "integrity": "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==",
+      "license": "ISC",
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/d3-transition": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmmirror.com/d3-transition/-/d3-transition-3.0.1.tgz",
+      "integrity": "sha512-ApKvfjsSR6tg06xrL434C0WydLr7JewBB3V+/39RMHsaXTOG0zmt/OAXeng5M5LBm0ojmxJrpomQVZ1aPvBL4w==",
+      "license": "ISC",
+      "dependencies": {
+        "d3-color": "1 - 3",
+        "d3-dispatch": "1 - 3",
+        "d3-ease": "1 - 3",
+        "d3-interpolate": "1 - 3",
+        "d3-timer": "1 - 3"
+      },
+      "engines": {
+        "node": ">=12"
+      },
+      "peerDependencies": {
+        "d3-selection": "2 - 3"
+      }
+    },
+    "node_modules/d3-zoom": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmmirror.com/d3-zoom/-/d3-zoom-3.0.0.tgz",
+      "integrity": "sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw==",
+      "license": "ISC",
+      "dependencies": {
+        "d3-dispatch": "1 - 3",
+        "d3-drag": "2 - 3",
+        "d3-interpolate": "1 - 3",
+        "d3-selection": "2 - 3",
+        "d3-transition": "2 - 3"
+      },
+      "engines": {
+        "node": ">=12"
+      }
+    },
     "node_modules/dargs": {
       "version": "7.0.0",
       "resolved": "https://registry.npmjs.org/dargs/-/dargs-7.0.0.tgz",
@@ -7130,6 +7383,13 @@
       "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
       "dev": true
     },
+    "node_modules/esm-env": {
+      "version": "1.2.2",
+      "resolved": "https://registry.npmmirror.com/esm-env/-/esm-env-1.2.2.tgz",
+      "integrity": "sha512-Epxrv+Nr/CaL4ZcFGPJIYLWFom+YeV1DqMLHJoEd9SYRxNbaFruBwfEX/kkHUJf55j2+TUbmDcmuilbP1TmXHA==",
+      "license": "MIT",
+      "peer": true
+    },
     "node_modules/espree": {
       "version": "9.5.1",
       "resolved": "https://registry.npmjs.org/espree/-/espree-9.5.1.tgz",
@@ -7168,6 +7428,16 @@
         "node": ">=4.0"
       }
     },
+    "node_modules/esrap": {
+      "version": "1.4.5",
+      "resolved": "https://registry.npmmirror.com/esrap/-/esrap-1.4.5.tgz",
+      "integrity": "sha512-CjNMjkBWWZeHn+VX+gS8YvFwJ5+NDhg8aWZBSFJPR8qQduDNjbJodA2WcwCm7uQa5Rjqj+nZvVmceg1RbHFB9g==",
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "@jridgewell/sourcemap-codec": "^1.4.15"
+      }
+    },
     "node_modules/esrecurse": {
       "version": "4.3.0",
       "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz",
@@ -8793,6 +9063,16 @@
         "node": ">=0.10.0"
       }
     },
+    "node_modules/is-reference": {
+      "version": "3.0.3",
+      "resolved": "https://registry.npmmirror.com/is-reference/-/is-reference-3.0.3.tgz",
+      "integrity": "sha512-ixkJoqQvAP88E6wLydLGGqCJsrFUnqoH6HnaczB8XmDH1oaWU+xxdptvikTgaEhtZ53Ky6YXiBuUI2WXLMCwjw==",
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "@types/estree": "^1.0.6"
+      }
+    },
     "node_modules/is-regex": {
       "version": "1.1.4",
       "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz",
@@ -9572,6 +9852,13 @@
         "lie": "3.1.1"
       }
     },
+    "node_modules/locate-character": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmmirror.com/locate-character/-/locate-character-3.0.0.tgz",
+      "integrity": "sha512-SW13ws7BjaeJ6p7Q6CO2nchbYEc3X3J6WrmTTDto7yMPqVSZTUyY5Tjbid+Ab8gLnATtygYtiDIJGQRRn2ZOiA==",
+      "license": "MIT",
+      "peer": true
+    },
     "node_modules/locate-path": {
       "version": "6.0.0",
       "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz",
@@ -13252,6 +13539,42 @@
         "url": "https://github.com/sponsors/ljharb"
       }
     },
+    "node_modules/svelte": {
+      "version": "5.25.3",
+      "resolved": "https://registry.npmmirror.com/svelte/-/svelte-5.25.3.tgz",
+      "integrity": "sha512-J9rcZ/xVJonAoESqVGHHZhrNdVbrCfkdB41BP6eiwHMoFShD9it3yZXApVYMHdGfCshBsZCKsajwJeBbS/M1zg==",
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "@ampproject/remapping": "^2.3.0",
+        "@jridgewell/sourcemap-codec": "^1.5.0",
+        "@sveltejs/acorn-typescript": "^1.0.5",
+        "@types/estree": "^1.0.5",
+        "acorn": "^8.12.1",
+        "aria-query": "^5.3.1",
+        "axobject-query": "^4.1.0",
+        "clsx": "^2.1.1",
+        "esm-env": "^1.2.1",
+        "esrap": "^1.4.3",
+        "is-reference": "^3.0.3",
+        "locate-character": "^3.0.0",
+        "magic-string": "^0.30.11",
+        "zimmerframe": "^1.1.2"
+      },
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/svelte/node_modules/magic-string": {
+      "version": "0.30.17",
+      "resolved": "https://registry.npmmirror.com/magic-string/-/magic-string-0.30.17.tgz",
+      "integrity": "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==",
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "@jridgewell/sourcemap-codec": "^1.5.0"
+      }
+    },
     "node_modules/svg-baker": {
       "version": "1.7.0",
       "resolved": "https://registry.npmjs.org/svg-baker/-/svg-baker-1.7.0.tgz",
@@ -16377,6 +16700,13 @@
         "url": "https://github.com/sponsors/sindresorhus"
       }
     },
+    "node_modules/zimmerframe": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmmirror.com/zimmerframe/-/zimmerframe-1.1.2.tgz",
+      "integrity": "sha512-rAbqEGa8ovJy4pyBxZM70hg4pE6gDgaQ0Sl9M3enG3I0d6H4XSAM3GeNGLKnsBpuijUow064sf7ww1nutC5/3w==",
+      "license": "MIT",
+      "peer": true
+    },
     "node_modules/zwitch": {
       "version": "2.0.4",
       "resolved": "https://registry.npmmirror.com/zwitch/-/zwitch-2.0.4.tgz",
@@ -16573,13 +16903,12 @@
       "dev": true
     },
     "@ampproject/remapping": {
-      "version": "2.2.1",
-      "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.1.tgz",
-      "integrity": "sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg==",
-      "dev": true,
+      "version": "2.3.0",
+      "resolved": "https://registry.npmmirror.com/@ampproject/remapping/-/remapping-2.3.0.tgz",
+      "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==",
       "requires": {
-        "@jridgewell/gen-mapping": "^0.3.0",
-        "@jridgewell/trace-mapping": "^0.3.9"
+        "@jridgewell/gen-mapping": "^0.3.5",
+        "@jridgewell/trace-mapping": "^0.3.24"
       }
     },
     "@antfu/eslint-config": {
@@ -18489,27 +18818,24 @@
       }
     },
     "@jridgewell/gen-mapping": {
-      "version": "0.3.3",
-      "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz",
-      "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==",
-      "dev": true,
+      "version": "0.3.8",
+      "resolved": "https://registry.npmmirror.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz",
+      "integrity": "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==",
       "requires": {
-        "@jridgewell/set-array": "^1.0.1",
+        "@jridgewell/set-array": "^1.2.1",
         "@jridgewell/sourcemap-codec": "^1.4.10",
-        "@jridgewell/trace-mapping": "^0.3.9"
+        "@jridgewell/trace-mapping": "^0.3.24"
       }
     },
     "@jridgewell/resolve-uri": {
       "version": "3.1.0",
       "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz",
-      "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==",
-      "dev": true
+      "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w=="
     },
     "@jridgewell/set-array": {
-      "version": "1.1.2",
-      "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz",
-      "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==",
-      "dev": true
+      "version": "1.2.1",
+      "resolved": "https://registry.npmmirror.com/@jridgewell/set-array/-/set-array-1.2.1.tgz",
+      "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A=="
     },
     "@jridgewell/source-map": {
       "version": "0.3.3",
@@ -18527,21 +18853,12 @@
       "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ=="
     },
     "@jridgewell/trace-mapping": {
-      "version": "0.3.18",
-      "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.18.tgz",
-      "integrity": "sha512-w+niJYzMHdd7USdiH2U6869nqhD2nbfZXND5Yp93qIbEmnDNk7PD48o+YchRVpzMU7M6jVCbenTR7PA1FLQ9pA==",
-      "dev": true,
+      "version": "0.3.25",
+      "resolved": "https://registry.npmmirror.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz",
+      "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==",
       "requires": {
-        "@jridgewell/resolve-uri": "3.1.0",
-        "@jridgewell/sourcemap-codec": "1.4.14"
-      },
-      "dependencies": {
-        "@jridgewell/sourcemap-codec": {
-          "version": "1.4.14",
-          "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz",
-          "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==",
-          "dev": true
-        }
+        "@jridgewell/resolve-uri": "^3.1.0",
+        "@jridgewell/sourcemap-codec": "^1.4.14"
       }
     },
     "@juggle/resize-observer": {
@@ -18842,6 +19159,36 @@
         }
       }
     },
+    "@svelte-put/shortcut": {
+      "version": "3.1.1",
+      "resolved": "https://registry.npmmirror.com/@svelte-put/shortcut/-/shortcut-3.1.1.tgz",
+      "integrity": "sha512-2L5EYTZXiaKvbEelVkg5znxqvfZGZai3m97+cAiUBhLZwXnGtviTDpHxOoZBsqz41szlfRMcamW/8o0+fbW3ZQ==",
+      "requires": {}
+    },
+    "@sveltejs/acorn-typescript": {
+      "version": "1.0.5",
+      "resolved": "https://registry.npmmirror.com/@sveltejs/acorn-typescript/-/acorn-typescript-1.0.5.tgz",
+      "integrity": "sha512-IwQk4yfwLdibDlrXVE04jTZYlLnwsTT2PIOQQGNLWfjavGifnk1JD1LcZjZaBTRcxZu2FfPfNLOE04DSu9lqtQ==",
+      "peer": true,
+      "requires": {}
+    },
+    "@tinyflow-ai/ui": {
+      "version": "0.0.9",
+      "resolved": "https://registry.npmmirror.com/@tinyflow-ai/ui/-/ui-0.0.9.tgz",
+      "integrity": "sha512-dEm3/LGsqA8YOiQYyGu9XdzVvskmNsjrHhRGsT4CEA/jCzAN6ef82qH5AGrSUWljTvBghZOT3OLzjwycVYKaYQ==",
+      "requires": {
+        "@floating-ui/dom": "^1.6.13",
+        "@xyflow/svelte": "^0.1.31"
+      }
+    },
+    "@tinyflow-ai/vue": {
+      "version": "0.0.9",
+      "resolved": "https://registry.npmmirror.com/@tinyflow-ai/vue/-/vue-0.0.9.tgz",
+      "integrity": "sha512-+/N8zpKUV85b5yLPprn0Ux7qMgzGWChHrHqR23+mMa6l9xznpL2B+tUP6UyXjF47snf7h4oypJSkyeMDl7fVFg==",
+      "requires": {
+        "@tinyflow-ai/ui": "0.0.9"
+      }
+    },
     "@traptitech/markdown-it-katex": {
       "version": "3.6.0",
       "resolved": "https://registry.npmjs.org/@traptitech/markdown-it-katex/-/markdown-it-katex-3.6.0.tgz",
@@ -18886,11 +19233,53 @@
       "integrity": "sha512-BG7fQKZ689HIoc5h+6D2Dgq1fABRa0RbBWKBd9SP/MVRVXROflpm5fhwyATX5duFmbStzyzyycPB8qUYKDH3NA==",
       "dev": true
     },
+    "@types/d3-color": {
+      "version": "3.1.3",
+      "resolved": "https://registry.npmmirror.com/@types/d3-color/-/d3-color-3.1.3.tgz",
+      "integrity": "sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A=="
+    },
+    "@types/d3-drag": {
+      "version": "3.0.7",
+      "resolved": "https://registry.npmmirror.com/@types/d3-drag/-/d3-drag-3.0.7.tgz",
+      "integrity": "sha512-HE3jVKlzU9AaMazNufooRJ5ZpWmLIoc90A37WU2JMmeq28w1FQqCZswHZ3xR+SuxYftzHq6WU6KJHvqxKzTxxQ==",
+      "requires": {
+        "@types/d3-selection": "*"
+      }
+    },
+    "@types/d3-interpolate": {
+      "version": "3.0.4",
+      "resolved": "https://registry.npmmirror.com/@types/d3-interpolate/-/d3-interpolate-3.0.4.tgz",
+      "integrity": "sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA==",
+      "requires": {
+        "@types/d3-color": "*"
+      }
+    },
+    "@types/d3-selection": {
+      "version": "3.0.11",
+      "resolved": "https://registry.npmmirror.com/@types/d3-selection/-/d3-selection-3.0.11.tgz",
+      "integrity": "sha512-bhAXu23DJWsrI45xafYpkQ4NtcKMwWnAC/vKrd2l+nxMFuvOT3XMYTIj2opv8vq8AO5Yh7Qac/nSeP/3zjTK0w=="
+    },
+    "@types/d3-transition": {
+      "version": "3.0.9",
+      "resolved": "https://registry.npmmirror.com/@types/d3-transition/-/d3-transition-3.0.9.tgz",
+      "integrity": "sha512-uZS5shfxzO3rGlu0cC3bjmMFKsXv+SmZZcgp0KD22ts4uGXp5EVYGzu/0YdwZeKmddhcAccYtREJKkPfXkZuCg==",
+      "requires": {
+        "@types/d3-selection": "*"
+      }
+    },
+    "@types/d3-zoom": {
+      "version": "3.0.8",
+      "resolved": "https://registry.npmmirror.com/@types/d3-zoom/-/d3-zoom-3.0.8.tgz",
+      "integrity": "sha512-iqMC4/YlFCSlO8+2Ii1GGGliCAY4XdeG748w5vQUbevlbDu0zSjH/+jojorQVBK/se0j6DUFNPBGSqD3YWYnDw==",
+      "requires": {
+        "@types/d3-interpolate": "*",
+        "@types/d3-selection": "*"
+      }
+    },
     "@types/estree": {
       "version": "1.0.6",
       "resolved": "https://registry.npmmirror.com/@types/estree/-/estree-1.0.6.tgz",
-      "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==",
-      "dev": true
+      "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw=="
     },
     "@types/hast": {
       "version": "3.0.4",
@@ -19448,11 +19837,34 @@
         "vue-demi": "*"
       }
     },
+    "@xyflow/svelte": {
+      "version": "0.1.32",
+      "resolved": "https://registry.npmmirror.com/@xyflow/svelte/-/svelte-0.1.32.tgz",
+      "integrity": "sha512-IS4aXbLC4SYdPuolMBzD2cMpyYOnK+S6i8ot+gQXdPpZJhrRpDiTEr8gFJgeLK2dHnW0n4p8uOALhtePyOPULw==",
+      "requires": {
+        "@svelte-put/shortcut": "3.1.1",
+        "@xyflow/system": "0.0.53",
+        "classcat": "^5.0.4"
+      }
+    },
+    "@xyflow/system": {
+      "version": "0.0.53",
+      "resolved": "https://registry.npmmirror.com/@xyflow/system/-/system-0.0.53.tgz",
+      "integrity": "sha512-QTWieiTtvNYyQAz1fxpzgtUGXNpnhfh6vvZa7dFWpWS2KOz6bEHODo/DTK3s07lDu0Bq0Db5lx/5M5mNjb9VDQ==",
+      "requires": {
+        "@types/d3-drag": "^3.0.7",
+        "@types/d3-selection": "^3.0.10",
+        "@types/d3-transition": "^3.0.8",
+        "@types/d3-zoom": "^3.0.8",
+        "d3-drag": "^3.0.0",
+        "d3-selection": "^3.0.0",
+        "d3-zoom": "^3.0.0"
+      }
+    },
     "acorn": {
-      "version": "8.11.3",
-      "resolved": "https://registry.npmmirror.com/acorn/-/acorn-8.11.3.tgz",
-      "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==",
-      "dev": true
+      "version": "8.14.1",
+      "resolved": "https://registry.npmmirror.com/acorn/-/acorn-8.14.1.tgz",
+      "integrity": "sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg=="
     },
     "acorn-jsx": {
       "version": "5.3.2",
@@ -19574,6 +19986,12 @@
       "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
       "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="
     },
+    "aria-query": {
+      "version": "5.3.2",
+      "resolved": "https://registry.npmmirror.com/aria-query/-/aria-query-5.3.2.tgz",
+      "integrity": "sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==",
+      "peer": true
+    },
     "arr-diff": {
       "version": "4.0.0",
       "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz",
@@ -19758,6 +20176,12 @@
         "proxy-from-env": "^1.1.0"
       }
     },
+    "axobject-query": {
+      "version": "4.1.0",
+      "resolved": "https://registry.npmmirror.com/axobject-query/-/axobject-query-4.1.0.tgz",
+      "integrity": "sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==",
+      "peer": true
+    },
     "babel-plugin-polyfill-corejs2": {
       "version": "0.3.3",
       "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.3.3.tgz",
@@ -20097,6 +20521,11 @@
         }
       }
     },
+    "classcat": {
+      "version": "5.0.5",
+      "resolved": "https://registry.npmmirror.com/classcat/-/classcat-5.0.5.tgz",
+      "integrity": "sha512-JhZUT7JFcQy/EzW605k/ktHtncoo9vnyW/2GspNYwFlN1C/WmjuV/xtS04e9SOkL2sTdw0VAZ2UGCcQ9lR6p6w=="
+    },
     "clean-regexp": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/clean-regexp/-/clean-regexp-1.0.0.tgz",
@@ -20181,6 +20610,12 @@
       "integrity": "sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w==",
       "dev": true
     },
+    "clsx": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmmirror.com/clsx/-/clsx-2.1.1.tgz",
+      "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==",
+      "peer": true
+    },
     "collection-visit": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz",
@@ -20466,6 +20901,72 @@
       "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.11.tgz",
       "integrity": "sha512-sa6P2wJ+CAbgyy4KFssIb/JNMLxFvKF1pCYCSXS8ZMuqZnMsrxqI2E5sPyoTpxoPU/gVZMzr2zjOfg8GIZOMsw=="
     },
+    "d3-color": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmmirror.com/d3-color/-/d3-color-3.1.0.tgz",
+      "integrity": "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA=="
+    },
+    "d3-dispatch": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmmirror.com/d3-dispatch/-/d3-dispatch-3.0.1.tgz",
+      "integrity": "sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg=="
+    },
+    "d3-drag": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmmirror.com/d3-drag/-/d3-drag-3.0.0.tgz",
+      "integrity": "sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg==",
+      "requires": {
+        "d3-dispatch": "1 - 3",
+        "d3-selection": "3"
+      }
+    },
+    "d3-ease": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmmirror.com/d3-ease/-/d3-ease-3.0.1.tgz",
+      "integrity": "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w=="
+    },
+    "d3-interpolate": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmmirror.com/d3-interpolate/-/d3-interpolate-3.0.1.tgz",
+      "integrity": "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==",
+      "requires": {
+        "d3-color": "1 - 3"
+      }
+    },
+    "d3-selection": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmmirror.com/d3-selection/-/d3-selection-3.0.0.tgz",
+      "integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ=="
+    },
+    "d3-timer": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmmirror.com/d3-timer/-/d3-timer-3.0.1.tgz",
+      "integrity": "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA=="
+    },
+    "d3-transition": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmmirror.com/d3-transition/-/d3-transition-3.0.1.tgz",
+      "integrity": "sha512-ApKvfjsSR6tg06xrL434C0WydLr7JewBB3V+/39RMHsaXTOG0zmt/OAXeng5M5LBm0ojmxJrpomQVZ1aPvBL4w==",
+      "requires": {
+        "d3-color": "1 - 3",
+        "d3-dispatch": "1 - 3",
+        "d3-ease": "1 - 3",
+        "d3-interpolate": "1 - 3",
+        "d3-timer": "1 - 3"
+      }
+    },
+    "d3-zoom": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmmirror.com/d3-zoom/-/d3-zoom-3.0.0.tgz",
+      "integrity": "sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw==",
+      "requires": {
+        "d3-dispatch": "1 - 3",
+        "d3-drag": "2 - 3",
+        "d3-interpolate": "1 - 3",
+        "d3-selection": "2 - 3",
+        "d3-transition": "2 - 3"
+      }
+    },
     "dargs": {
       "version": "7.0.0",
       "resolved": "https://registry.npmjs.org/dargs/-/dargs-7.0.0.tgz",
@@ -21356,6 +21857,12 @@
       "integrity": "sha512-HPpKPUBQcAsZOsHAFwTtIKcYlCje62XB7SEAcxjtmW6TD1WVpkS6i6/hOVtTZIl4zGj/mBqpFVGvaDneik+VoQ==",
       "dev": true
     },
+    "esm-env": {
+      "version": "1.2.2",
+      "resolved": "https://registry.npmmirror.com/esm-env/-/esm-env-1.2.2.tgz",
+      "integrity": "sha512-Epxrv+Nr/CaL4ZcFGPJIYLWFom+YeV1DqMLHJoEd9SYRxNbaFruBwfEX/kkHUJf55j2+TUbmDcmuilbP1TmXHA==",
+      "peer": true
+    },
     "espree": {
       "version": "9.5.1",
       "resolved": "https://registry.npmjs.org/espree/-/espree-9.5.1.tgz",
@@ -21384,6 +21891,15 @@
         }
       }
     },
+    "esrap": {
+      "version": "1.4.5",
+      "resolved": "https://registry.npmmirror.com/esrap/-/esrap-1.4.5.tgz",
+      "integrity": "sha512-CjNMjkBWWZeHn+VX+gS8YvFwJ5+NDhg8aWZBSFJPR8qQduDNjbJodA2WcwCm7uQa5Rjqj+nZvVmceg1RbHFB9g==",
+      "peer": true,
+      "requires": {
+        "@jridgewell/sourcemap-codec": "^1.4.15"
+      }
+    },
     "esrecurse": {
       "version": "4.3.0",
       "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz",
@@ -22572,6 +23088,15 @@
         }
       }
     },
+    "is-reference": {
+      "version": "3.0.3",
+      "resolved": "https://registry.npmmirror.com/is-reference/-/is-reference-3.0.3.tgz",
+      "integrity": "sha512-ixkJoqQvAP88E6wLydLGGqCJsrFUnqoH6HnaczB8XmDH1oaWU+xxdptvikTgaEhtZ53Ky6YXiBuUI2WXLMCwjw==",
+      "peer": true,
+      "requires": {
+        "@types/estree": "^1.0.6"
+      }
+    },
     "is-regex": {
       "version": "1.1.4",
       "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz",
@@ -23124,6 +23649,12 @@
         "lie": "3.1.1"
       }
     },
+    "locate-character": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmmirror.com/locate-character/-/locate-character-3.0.0.tgz",
+      "integrity": "sha512-SW13ws7BjaeJ6p7Q6CO2nchbYEc3X3J6WrmTTDto7yMPqVSZTUyY5Tjbid+Ab8gLnATtygYtiDIJGQRRn2ZOiA==",
+      "peer": true
+    },
     "locate-path": {
       "version": "6.0.0",
       "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz",
@@ -25859,6 +26390,39 @@
       "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==",
       "dev": true
     },
+    "svelte": {
+      "version": "5.25.3",
+      "resolved": "https://registry.npmmirror.com/svelte/-/svelte-5.25.3.tgz",
+      "integrity": "sha512-J9rcZ/xVJonAoESqVGHHZhrNdVbrCfkdB41BP6eiwHMoFShD9it3yZXApVYMHdGfCshBsZCKsajwJeBbS/M1zg==",
+      "peer": true,
+      "requires": {
+        "@ampproject/remapping": "^2.3.0",
+        "@jridgewell/sourcemap-codec": "^1.5.0",
+        "@sveltejs/acorn-typescript": "^1.0.5",
+        "@types/estree": "^1.0.5",
+        "acorn": "^8.12.1",
+        "aria-query": "^5.3.1",
+        "axobject-query": "^4.1.0",
+        "clsx": "^2.1.1",
+        "esm-env": "^1.2.1",
+        "esrap": "^1.4.3",
+        "is-reference": "^3.0.3",
+        "locate-character": "^3.0.0",
+        "magic-string": "^0.30.11",
+        "zimmerframe": "^1.1.2"
+      },
+      "dependencies": {
+        "magic-string": {
+          "version": "0.30.17",
+          "resolved": "https://registry.npmmirror.com/magic-string/-/magic-string-0.30.17.tgz",
+          "integrity": "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==",
+          "peer": true,
+          "requires": {
+            "@jridgewell/sourcemap-codec": "^1.5.0"
+          }
+        }
+      }
+    },
     "svg-baker": {
       "version": "1.7.0",
       "resolved": "https://registry.npmjs.org/svg-baker/-/svg-baker-1.7.0.tgz",
@@ -28046,6 +28610,12 @@
       "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==",
       "dev": true
     },
+    "zimmerframe": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmmirror.com/zimmerframe/-/zimmerframe-1.1.2.tgz",
+      "integrity": "sha512-rAbqEGa8ovJy4pyBxZM70hg4pE6gDgaQ0Sl9M3enG3I0d6H4XSAM3GeNGLKnsBpuijUow064sf7ww1nutC5/3w==",
+      "peer": true
+    },
     "zwitch": {
       "version": "2.0.4",
       "resolved": "https://registry.npmmirror.com/zwitch/-/zwitch-2.0.4.tgz",

+ 1 - 0
package.json

@@ -32,6 +32,7 @@
     "docs:preview": "vitepress preview docs"
   },
   "dependencies": {
+    "@tinyflow-ai/vue": "^0.0.9",
     "@traptitech/markdown-it-katex": "^3.6.0",
     "@vicons/ionicons5": "^0.13.0",
     "@vue-office/pdf": "^2.0.10",

File diff suppressed because it is too large
+ 298 - 184
pnpm-lock.yaml


+ 31 - 0
public/gpts.json

@@ -0,0 +1,31 @@
+{
+    "tag": [
+        "图像",
+        "论文",
+        "文件",
+        "识别",
+        "PDF",
+        "新闻",
+        "医生",
+        "老师",
+        "Logo"
+    ],
+    "gpts": [
+        {
+            "gid": "gpt-4-all",
+            "name": "gpt-4-all",
+            "logo": "https://cos.aitutu.cc/gpts/gpt4all.jpg",
+            "info": "集合官方GPT-4、联网,多模态(gpt-4v),绘图功能(dall-e3),限制不支持function等",
+            "use_cnt": "8624",
+            "bad": "0"
+        },
+        {
+            "gid": "gpt-4-gizmo-g-2fkFE8rbu",
+            "name": "DALL·E",
+            "logo": "https://files.oaiusercontent.com/file-SxYQO0Fq1ZkPagkFtg67DRVb?se=2123-10-12T23%3A57%3A32Z&sp=r&sv=2021-08-06&sr=b&rscc=max-age%3D31536000%2C%20immutable&rscd=attachment%3B%20filename%3Dagent_3.webp&sig=pLlQh8oUktqQzhM09SDDxn5aakqFuM2FAPptuA0mbqc%3D",
+            "info": "让我将你的想象变成图像 – 基于 ChatGPT",
+            "use_cnt": "3087",
+            "bad": "1"
+        }
+    ]
+}

+ 2 - 0
src/api/mjapi.ts

@@ -11,7 +11,9 @@ export interface gptsType{
     logo:string
     info:string
     use_cnt?:string
+    id:string
     bad?:string|number
+    modelName:string
 }
  //const { addChat, updateChat, updateChatSome, getChatByUuidAndIndex } = useChat()
 export function upImg(file:any   ):Promise<any>

+ 4 - 1
src/api/model.ts

@@ -4,10 +4,13 @@ import request from '@/utils/request/req';
  * 查询未隐藏模型
  * @returns 
  */
-export function modelList() {
+export function modelList(category: string) {
 	return request({
 		url: '/system/model/modelList',
 		method: 'get',
+		params: {
+            category: category
+        }
 	})
 }
 

+ 6 - 2
src/api/openapi.ts

@@ -192,6 +192,8 @@ interface subModelType{
     signal?:AbortSignal
     model?:string
     uuid?:string|number
+    chatType: number
+    appId: string
 }
 function getHeaderAuthorization(){
     // if(!gptServerStore.myData.OPENAI_API_KEY){
@@ -236,7 +238,6 @@ export const subModel= async (opt: subModelType)=>{
         frequency_penalty = gStore.frequency_penalty??frequency_penalty;
         max_tokens= gStore.max_tokens;
     }
-   
     let body ={
             max_tokens ,
             model ,
@@ -246,6 +247,8 @@ export const subModel= async (opt: subModelType)=>{
             "messages": opt.message
            ,stream:true
            ,kid:gptConfigStore.myData.kid
+           ,chat_type: opt.chatType
+           ,appId: opt.appId
         }
 
         let headers=   {'Content-Type': 'application/json;charset=UTF-8',
@@ -267,8 +270,9 @@ export const subModel= async (opt: subModelType)=>{
                  if(data=='[DONE]') opt.onMessage({text:'',isFinish:true})
                  else {
                     try{
+                        // TODO 思考处理,DeepSeek  API 字段reasoning_content ,本地部署标签<think> 
                         const obj= JSON.parse(data );
-                        opt.onMessage({text:obj.choices[0].delta?.content??'' ,isFinish:obj.choices[0].finish_reason!=null })
+                        opt.onMessage({text:obj.choices[0].delta?.content??obj.choices[0].delta?.reasoning_content??'' ,isFinish:obj.choices[0].finish_reason!=null })
                     }catch{
                         opt.onMessage({
                             text: data,

BIN
src/assets/01.png


+ 5 - 4
src/components/common/PromptStore/index.vue

@@ -70,6 +70,8 @@ let intervalId: string | number | NodeJS.Timer | undefined;
 
 // 获取支付二维码
 async function getPayUrl(money: string, name: string) {
+	message.success('本系统仅用于演示,暂不支持此功能!')
+	return;
 	showMeVisible.value = true;
 	const response = await payUrl({ money: money, name: name });
 	imageUrl.value = response.data.url;
@@ -81,9 +83,8 @@ async function getPayUrl(money: string, name: string) {
 
 // 跳转到支付地址
 async function getPayUrl1(money: string, name: string) {
-	if(loading.value) {
-		return
-	}
+	message.success('本系统仅用于演示,暂不支持此功能!')
+	return;
 	loading.value = true
 	const [err, result] = await to(getSPayUrl({ money: money, name: name }));
 	if (err) {
@@ -212,7 +213,7 @@ const tableData = ref([]);
 const fetchData1 = async () => {
 	try {
 		// 发起一个请求
-		const [err, result] = await to(modelList());
+		const [err, result] = await to(modelList('chat'));
 
 		if (err) {
 			message.error(err.message)

+ 3 - 1
src/router/index.ts

@@ -10,6 +10,7 @@ import fanyilayout from '@/views/fanyi/layout.vue'
 import pptlayout from '@/views/ppt/layout.vue'
 import musiclayout from '@/views/suno/layout.vue'
 import knowledgelayout from '@/views/knowledge/layout.vue'
+import wxlayout from '@/views/wxbot/layout.vue'
 
 
 const routes: RouteRecordRaw[] = [
@@ -55,6 +56,7 @@ const routes: RouteRecordRaw[] = [
   },
 
   {
+    
     path: '/draw',
     name: 'Rootdraw',
     component: mjlayout,
@@ -141,7 +143,7 @@ const routes: RouteRecordRaw[] = [
   {
     path: '/wxbot',
     name: 'Wxbot',
-    component: mjlayout,
+    component: wxlayout,
     redirect: '/wxbot/t',
     children: [
       {

+ 1 - 1
src/store/modules/app/helper.ts

@@ -14,7 +14,7 @@ export interface AppState {
 }
 
 export function defaultSetting(): AppState {
-  return { siderCollapsed: false,isChat:true, theme: 'dark', language: 'zh-CN' }
+  return { siderCollapsed: false,isChat:true, theme: 'light', language: 'zh-CN' }
 }
 
 export function getLocalSetting(): AppState {

+ 1 - 1
src/utils/request/req.ts

@@ -90,7 +90,7 @@ service.interceptors.response.use(
       // 退出登录
       message.error('无效的会话,或者会话已过期,请重新登录。')
       useUserStore().logout().then(() => {
-        location.href = '/login';
+          location.href = '#/login';
       });
     } else if (code === HttpStatus.SERVER_ERROR) {
       // console.log(msg);

+ 31 - 11
src/views/chat/components/Message/Text.vue

@@ -15,7 +15,7 @@ import whisperText from '@/views/mj/whisperText.vue'
 import MjTextAttr from '@/views/mj/mjTextAttr.vue'
 import aiTextSetting from '@/views/mj/aiTextSetting.vue'
 import aiSetAuth from '@/views/mj/aiSetAuth.vue'
-import { isApikeyError, isAuthSessionError, isTTS } from '@/api'
+import { isApikeyError, isAuthSessionError, isTTS, mlog } from '@/api'
 
 interface Props {
   inversion?: boolean
@@ -51,7 +51,7 @@ mdi.use(mdKatex, { blockClass: 'katexmath-block rounded-md p-[10px]', errorColor
 const wrapClass = computed(() => {
   return [
     'text-wrap',
-    'min-w-[20px]',
+    'min-w-[20px]','max-w-[810px]',
     'rounded-md',
     isMobile.value ? 'p-2' : 'px-3 py-2',
     props.inversion ? 'bg-[#d2f9d1]' : 'bg-[#f4f6f8]',
@@ -62,11 +62,30 @@ const wrapClass = computed(() => {
 })
 
 const text = computed(() => {
-  const value = props.text ?? ''
-  // if (!props.asRawText)
-  //   return mdi.render(value)
-  // return value
-  return mdi.render(value)
+  let value = props.text ?? ''
+  if (!props.asRawText){
+    value = value.replace(/\\\( *(.*?) *\\\)/g, '$$$1$$');
+    //value = value.replace(/\\\((.*?)\\\)/g, '$$$1$$');
+    value = value.replace(/\\\[ *(.*?) *\\\]/g, '$$$$$1$$$$');
+    //
+    value= value.replaceAll('\\[',"$$$$")
+    value= value.replaceAll('\\]',"$$$$")   
+
+    //思考过程处理
+    //value= value.replace(/<think>([\s\S]*?)<\/think>/g, (match: string, content: string) => { 
+    value= value.replace(/<think>([\s\S]*?)(?=<\/think>|$)/g, (match: string, content: string) => { 
+      const processedContent: string = content
+        .split('\n')
+        .map(line => line.trim() ? '>' + line : line)  
+        .join('\n').replace(/(\r?\n)+/g, '\n>\n');
+       
+      return ">Thinking..."+(processedContent) ;
+    });
+    value= value.replaceAll('</think>','')
+    //mlog('replace', value)
+    return mdi.render(value) 
+  }
+  return value
 })
 
 function highlightBlock(str: string, lang?: string) {
@@ -121,15 +140,16 @@ onUnmounted(() => {
         <aiTextSetting v-if="!inversion && isApikeyError(text)"/>
         <aiSetAuth v-if="!inversion && isAuthSessionError(text)" />
           
-        <dallText :chat="chat" v-if="chat.model=='dall-e-3' || chat.model=='dall-e-2'" class="whitespace-pre-wrap" />
+        <dallText :chat="chat" v-if=" chat.model && chat.model?.indexOf('chat') == -1" class="whitespace-pre-wrap" />
         <mjText v-if="chat.mjID" class="whitespace-pre-wrap" :chat="chat" :mdi="mdi"></mjText>
         <ttsText v-else-if="chat.model && isTTS(chat.model) && chat.text=='ok'" :chat="chat"/>
         <template v-else>
-          <div v-if="!asRawText" class="markdown-body " :class="{ 'markdown-body-generate': loading }" v-html="text" />
-          <div style="font-size: 17px; font-family: 'Karla';" v-else class="whitespace-pre-wrap" v-text="text" />
+          <div v-if="!asRawText" class="markdown-body" :class="{ 'markdown-body-generate': loading }" v-html="text" />
+          <div v-else class="whitespace-pre-wrap" v-text="text" />
         </template>
       </div>
       <whisperText v-else-if="text=='whisper' && chat.opt?.lkey "  :chat="chat" />
+      <div v-else-if="asRawText" class="whitespace-pre-wrap" v-text="text" />
       <div v-else class="markdown-body "  style="--color-fg-default:#24292f"  v-html="text" />
       <!-- <div v-else class="whitespace-pre-wrap" v-text="text" /> -->
       <MjTextAttr :image="chat.opt?.images[0]" v-if="chat.opt?.images"></MjTextAttr>
@@ -144,4 +164,4 @@ onUnmounted(() => {
 
 <style lang="less">
 @import url(./style.less);
-</style>
+</style>

+ 19 - 19
src/views/chat/components/Message/index.vue

@@ -9,7 +9,7 @@ import { t } from '@/locales'
 import { useBasicLayout } from '@/hooks/useBasicLayout'
 import { copyToClip } from '@/utils/copy'
 import { homeStore } from '@/store'
-import { getSeed, mlog ,mjImgUrl} from '@/api'
+import { getSeed, mlog} from '@/api' 
 
 interface Props {
   dateTime?: string
@@ -24,7 +24,7 @@ interface Props {
 interface Emit {
   (ev: 'regenerate'): void
   (ev: 'delete'): void
-	(ev: 'edit'): void
+  (ev: 'edit'): void
 }
 
 const props = defineProps<Props>()
@@ -39,7 +39,7 @@ const message = useMessage()
 
 const textRef = ref<HTMLElement>()
 
-const asRawText = ref(props.inversion)
+const asRawText = ref(props.inversion && homeStore.myData.session.isCloseMdPreview)
 
 const messageRef = ref<HTMLElement>()
 
@@ -55,7 +55,7 @@ const options = computed(() => {
       key: 'delete',
       icon: iconRender({ icon: 'ri:delete-bin-line' }),
     },
-		{
+    {
       label: t('common.edit'),
       key: 'edit',
       icon: iconRender({ icon: 'ri:edit-2-line' }),
@@ -78,9 +78,9 @@ const options = computed(() => {
   return common
 })
 
-function handleSelect(key: 'copyText' | 'delete' | 'edit' | 'toggleRenderType' |'tts') {
+function handleSelect(key: 'copyText' | 'delete' | 'edit' | 'toggleRenderType' | 'tts') {
   switch (key) {
-    case 'tts':
+    case 'tts': 
       homeStore.setMyData({act:'gpt.ttsv2', actData:{ index:props.index , uuid:props.chat.uuid, text:props.text } });
       return;
     case 'copyText':
@@ -91,8 +91,8 @@ function handleSelect(key: 'copyText' | 'delete' | 'edit' | 'toggleRenderType' |
       return
     case 'delete':
       emit('delete')
-			return
-		case 'edit':
+      return
+    case 'edit':
       emit('edit')
   }
 }
@@ -123,7 +123,7 @@ function handleRegenerate2() {
   mlog('重新发送!');
   homeStore.setMyData({act:'gpt.resubmit', actData:{ index:props.index , uuid:props.chat.uuid } });
 }
-
+ 
 </script>
 
 <template>
@@ -140,23 +140,23 @@ function handleRegenerate2() {
     </div>
     <div class="overflow-hidden text-sm " :class="[inversion ? 'items-end' : 'items-start']">
       <p class="text-xs group  text-[#b4bbc4] flex  items-center space-x-2 " :class="[inversion ? 'justify-end' : 'justify-start']">
-        <span style="font-size: 14px; font-family: 'Karla';">{{ dateTime }}</span>
+        <span>{{ dateTime }}</span>
         <span v-if="chat.model"  class="text-[#b4bbc4]/50">{{ chat.model }}</span>
         <!-- <span>{{ chat.opt?.progress }}</span> -->
         <template  v-if="chat.opt?.status=='SUCCESS'">
           <SvgIcon icon="ri:restart-line" @click="sendReload"  class="cursor-pointer text-neutral-300 hover:text-neutral-800 dark:hover:text-neutral-300 " ></SvgIcon>
-
+          
           <div @click="getSeed(chat, message )" class="cursor-pointer">
             <span v-if="chat.opt?.seed">Seed:{{ chat.opt?.seed }}</span>
             <span v-else>Seed</span>
           </div>
-          <a :href=" mjImgUrl(chat.opt?.imageUrl)" class="hidden group-hover:block active  cursor-pointer underline " target="_blank">{{ $t('mj.ulink') }}</a>
+          <!-- <a :href=" mjImgUrl(chat.opt?.imageUrl)" class="hidden group-hover:block active  cursor-pointer underline " target="_blank">{{ $t('mj.ulink') }}</a> -->
         </template>
       </p>
-
+      
       <div  class="flex items-end gap-1 mt-2"
-        :class="[inversion ? 'flex-row-reverse' : 'flex-row']" >
-        <TextComponent
+        :class="[inversion ? 'flex-row-reverse' : 'flex-row']" > 
+        <TextComponent 
           ref="textRef"
           :inversion="inversion"
           :error="error"
@@ -165,8 +165,8 @@ function handleRegenerate2() {
           :as-raw-text="asRawText"
           :chat="chat"
         />
-
-        <div class="flex flex-col" v-if="!chat.mjID && chat.model!='dall-e-3' && chat.model!='dall-e-2' ">
+        <!-- <div class="flex flex-col" v-if="!chat.mjID && chat.model!='dall-e-3' && chat.model!='dall-e-2' "> -->
+        <div class="flex flex-col" v-if="!chat.mjID">
           <!-- <button
             v-if="!inversion "
             class="mb-2 transition text-neutral-300 hover:text-neutral-800 dark:hover:text-neutral-300"
@@ -185,7 +185,7 @@ function handleRegenerate2() {
             :trigger="isMobile ? 'click' : 'hover'"
             :placement="!inversion ? 'right' : 'left'"
             :options="options"
-            @select="handleSelect"
+            @select="handleSelect" 
           >
             <button class="transition text-neutral-300 hover:text-neutral-800 dark:hover:text-neutral-200">
               <SvgIcon icon="ri:more-2-fill" />
@@ -195,4 +195,4 @@ function handleRegenerate2() {
       </div>
     </div>
   </div>
-</template>
+</template>

+ 4 - 1
src/views/chat/components/Message/style.less

@@ -13,6 +13,9 @@
 	ul {
 		list-style-type: disc;
 	}
+	img{
+		max-width: 400px;
+	}
 
 	pre code,
 	pre tt {
@@ -132,4 +135,4 @@ html.dark {
 			padding: 24px 16px 16px 16px;
 		}
 	}
-}
+}

File diff suppressed because it is too large
+ 474 - 332
src/views/chat/index.vue


+ 1 - 1
src/views/fanyi/components/textComponent.vue

@@ -149,7 +149,7 @@ onMounted(() => {
 
 async function getModelList() {
 	try {
-		const res = await modelList();
+		const res = await modelList('chat');
 		modelListData.value = res.data;
 		model.value = modelListData.value[0]?.modelDescribe || "";
 	} catch (error) {

+ 16 - 5
src/views/knowledge/index.vue

@@ -26,9 +26,10 @@ import {
 import to from "await-to-js";
 import { useRouter } from "vue-router";
 import { t } from "@/locales";
+import { modelList } from "@/api/model";
 
 onMounted(() => {
-	fetchData();
+	fetchData(),getModelList();
 });
 
 const router = useRouter();
@@ -102,12 +103,20 @@ const placement = ref<DrawerPlacement>("right");
 
 const getVector = reactive([
 	{ label: "weaviate", value: "weaviate" },
-	{ label: "milvus", value: "milvus" },
+	{ label: "milvus", value: "milvus" }, 
 ]);
 
-const getVectorModel = reactive([
-	{ label: "text-embedding-3-small", value: "text-embedding-3-small" },
-]);
+const getVectorModel = ref([]);
+
+async function getModelList() {
+	try {
+		const res = await modelList('vector');
+		getVectorModel.value = res.data;
+	} catch (error) {
+		console.error("获取模型列表失败:", error);
+		message.error("获取模型列表失败");
+	}
+}
 
 const createColumns = () => {
 	return [
@@ -317,6 +326,8 @@ const columns = ref(createColumns());
 								<n-select
 									:options="getVectorModel"
 									v-model:value="formValue.vectorModel"
+									value-field="modelDescribe"
+									label-field="modelName"
 									placeholder="请选择向量模型"
 									clearable
 								></n-select>

+ 2 - 2
src/views/login/index.vue

@@ -90,7 +90,7 @@ let intervalId: string | number | NodeJS.Timer | undefined;
 const POLLING_INTERVAL = 3000;
 
 async function handleWxLogin() {
-
+	
 	showModal.value = true
 	//获取二维码信息
 	const [err1, res1] = await to(getMpQrCode());
@@ -101,7 +101,7 @@ async function handleWxLogin() {
 		ticket.value = res1.data.ticket;
 		intervalId = setInterval(slectLoginType, POLLING_INTERVAL);
 	}
-
+	
 }
 
 // 1. 定时查询是否登录成功

+ 11 - 5
src/views/mj/aiGpt.vue

@@ -12,7 +12,7 @@ import { t } from "@/locales";
 const emit = defineEmits(['finished']);
 const { addChat , updateChatSome } = useChat()
 const chatStore = useChatStore()
-const st=ref({uuid:'1002', index:-1 });
+const st=ref({uuid:'1002', index:-1, chatType:0, appId:'' });
 const controller = ref<AbortController>( );;// new AbortController();
 const dataSources = computed(() => chatStore.getChatByUuid(+st.value.uuid))
 const ms= useMessage();
@@ -52,8 +52,10 @@ watch(()=>homeStore.myData.act, async (n)=>{
 
         let  uuid2 =  dd.uuid?? uuid;
         st.value.uuid =  uuid2 ;
+        st.value.chatType = dd.chatType;
+        st.value.appId = dd.appId??'';
         const chatSet = new chatSetting(   +st.value.uuid  );
-        const nGptStore =   chatSet.getGptConfig()  ;
+        const nGptStore =   chatSet.getGptConfig();
          mlog('gpt.submit', dd , dd.uuid,  nGptStore ) ;
         let model = nGptStore.model ;//gptConfigStore.myData.model
 
@@ -145,8 +147,10 @@ watch(()=>homeStore.myData.act, async (n)=>{
         let historyMesg=  await getMessage();
         mlog('historyMesg', historyMesg );
         //return ;
-        let message= [ {  "role": "system", "content": getSystemMessage(  +uuid2) },
-                ...historyMesg ];
+        // let message= [ {  "role": "system", "content": getSystemMessage(  +uuid2) },
+        //         ...historyMesg ];
+        let message= [...historyMesg ];
+                
         if( dd.fileBase64 && dd.fileBase64.length>0 ){
             if(isCanBase64Model(model)){ 
                 let obj={
@@ -307,7 +311,9 @@ const submit= (model:string, message:any[],opt?:any)=>{
                     goFinish()
                 },
                 signal: controller.value.signal,
-                kid: ''
+                kid: '',
+                chatType: st.value.chatType,
+                appId: st.value.appId
             }).then(()=>goFinish() ).catch(e=>{
                 if(e.message!='canceled')  textRz.value.push("\n"+t('mj.fail')+":\n```\n"+(e.reason??JSON.stringify(e,null,2)) +"\n```\n")
                 goFinish();

+ 36 - 9
src/views/mj/aiGptInput.vue

@@ -39,7 +39,12 @@ const { iconRender } = useIconRender();
 //import FormData from 'form-data'
 const route = useRoute();
 const chatStore = useChatStore();
-const emit = defineEmits(["update:modelValue", "export", "handleClear"]);
+const emit = defineEmits([
+	"update:modelValue",
+	"update:chatType",
+	"export",
+	"handleClear",
+]);
 const props = defineProps<{
 	modelValue: string;
 	disabled?: boolean;
@@ -53,12 +58,14 @@ const st = ref<{
 	isShow: boolean;
 	showMic: boolean;
 	micStart: boolean;
+	chatType: boolean;
 }>({
 	fileBase64: [],
 	isLoad: 0,
 	isShow: false,
 	showMic: false,
 	micStart: false,
+	chatType: false,
 });
 const { isMobile } = useBasicLayout();
 const placeholder = computed(() => {
@@ -94,6 +101,8 @@ const handleSubmit = () => {
 	let obj = {
 		prompt: mvalue.value,
 		fileBase64: st.value.fileBase64,
+		chatType: st.value.chatType ? 1 : 0,
+		appId: gptConfigStore.myData.gpts ? gptConfigStore.myData.gpts.id : "",
 	};
 	homeStore.setMyData({ act: "gpt.submit", actData: obj });
 	mvalue.value = "";
@@ -109,6 +118,11 @@ const mvalue = computed({
 		emit("update:modelValue", value);
 	},
 });
+const chatTypeBn = computed({
+	get() {
+		return st.value.chatType ? "info" : "";
+	},
+});
 function selectFile(input: any) {
 	const file = input.target.files[0];
 	upFile(file);
@@ -215,7 +229,6 @@ const paste = (e: ClipboardEvent) => {
 	let rz = getFileFromClipboard(e);
 	if (rz.length > 0) upFile(rz[0]);
 };
-
 const sendMic = (e: any) => {
 	mlog("sendMic", e);
 	st.value.showMic = false;
@@ -279,6 +292,20 @@ const handleSelectASR = (key: string | number) => {
 	if (key == "asr") goASR();
 	if (key == "whisper") st.value.showMic = true;
 };
+/**
+ * 校验字符串的大小
+ * @param inputStr 输入的字符
+ * @param maxLength 字符串长度
+ */
+const truncateText = (inputStr: any, maxLength = 20) => {
+	// 处理空值情况
+	if (!inputStr) return "";
+	// 类型安全校验
+	const str = String(inputStr);
+	// 判断并截断
+	return str.length > maxLength ? `${str.slice(0, maxLength)}...` : str;
+};
+
 const show = ref(false);
 function handleExport() {
 	emit("export");
@@ -335,14 +362,14 @@ function handleClear() {
 						<template v-if="nGptStore.gpts">
 							<SvgIcon icon="ri:apps-fill" />
 							<span class="line-clamp-1 overflow-hidden">{{
-								nGptStore.gpts.name
-							}}</span>
+									nGptStore.gpts.name
+								}}</span>
 						</template>
 						<template v-else>
 							<SvgIcon icon="heroicons:sparkles" />
-							<span>{{
-								nGptStore.modelLabel ? nGptStore.modelLabel : "gpt-4o-mini"
-							}}</span>
+							<span>模型:{{
+									nGptStore.modelLabel ? truncateText(nGptStore.modelLabel,20) : "deepseek-r1:1.5b"
+								}} {{nGptStore.kid?'知识库:'+truncateText(nGptStore.kName,10):''}}</span>
 						</template>
 						<SvgIcon icon="icon-park-outline:right" />
 					</div> -->
@@ -552,8 +579,8 @@ function handleClear() {
 						<p class="py-1" v-text="$t('mj.tokenInfo2')"></p>
 						<p class="text-right">
 							<NButton @click="st.isShow = true" type="info" size="small">{{
-								$t("setting.setting")
-							}}</NButton>
+									$t("setting.setting")
+								}}</NButton>
 						</p>
 					</div>
 				</NPopover> -->

+ 197 - 197
src/views/mj/aiModel.vue

@@ -22,228 +22,228 @@ onMounted(() => { fetchData(),fetchDataGetKnowledge() });
 
 const config = ref([])
 const fetchData = async () => {
-    try {
-       // 发起一个请求
-      const [err, result] = await to(modelList());
-
-      if (err) {
-        message.error(err.message)
-        config.value = []; // 设置为空数组,避免迭代错误
-      } else {
-         config.value = result.data;
-      }
-    } catch (error) {
-      console.error('Error fetching data:', error);
-    }
+	try {
+		// 发起一个请求
+		const [err, result] = await to(modelList('chat'));
+
+		if (err) {
+			message.error(err.message)
+			config.value = []; // 设置为空数组,避免迭代错误
+		} else {
+			config.value = result.data;
+		}
+	} catch (error) {
+		console.error('Error fetching data:', error);
+	}
 };
 
 const fetchDataGetKnowledge = async () => {
-    if(getToken()){
-        try {
-       // 发起一个请求
-      const [err, result] = await to(getKnowledge());
-      console.log("result===", result.rows)
-      if (err) {
-        ms.error(err.message)
-      } else {
-        options.value = result.rows.map((item: any) => ({
-            label: item.kname, // 假设后台返回的数据有 'name' 字段
-            value: item.id     // 假设每个数据项都有一个唯一的 'id' 字段
-        }));
-
-        // 请求成功
-        options.value.push({ label: 'please select', value: '' });
-      }
-    } catch (error) {
-      console.error('Error fetching data:', error);
-    }
-    }
-  };
+	if(getToken()){
+		try {
+			// 发起一个请求
+			const [err, result] = await to(getKnowledge());
+			console.log("result===", result.rows)
+			if (err) {
+				ms.error(err.message)
+			} else {
+				options.value = result.rows.map((item: any) => ({
+					label: item.kname, // 假设后台返回的数据有 'name' 字段
+					value: item.id     // 假设每个数据项都有一个唯一的 'id' 字段
+				}));
+
+				// 请求成功
+				options.value.push({ label: '暂不配置', value: '' });
+			}
+		} catch (error) {
+			console.error('Error fetching data:', error);
+		}
+	}
+};
 
 const st= ref({openMore:false });
 const voiceList= computed(()=>{
-    let rz=[];
-    for(let o of "alloy,echo,fable,onyx,nova,shimmer".split(/[ ,]+/ig))rz.push({label:o,value:o})
-    return rz;
+	let rz=[];
+	for(let o of "alloy,echo,fable,onyx,nova,shimmer".split(/[ ,]+/ig))rz.push({label:o,value:o})
+	return rz;
 });
 const modellist = computed(() => { //
-    let rz =[ ];
-    for(let o of config.value){
-        rz.push({label:o.modelDescribe,value:o.modelName})
-    }
-
-    if(gptConfigStore.myData.userModel){
-        let arr = gptConfigStore.myData.userModel.split(/[ ,]+/ig);
-        for(let o of arr ){
-             rz.push({label:o,value:o})
-        }
-    }
-    //服务端的 CUSTOM_MODELS 设置
-    if( homeStore.myData.session.cmodels ){
-        let delModel:string[] = [];
-        let addModel:string[]=[];
-        homeStore.myData.session.cmodels.split(/[ ,]+/ig).map( (v:string)=>{
-            if(v.indexOf('-')==0){
-                delModel.push(v.substring(1))
-            }else{
-                addModel.push(v);
-            }
-        });
-        mlog('cmodels',delModel,addModel);
-        rz= rz.filter(v=> delModel.indexOf(v.value)==-1 );
-        addModel.map(o=>rz.push({label:o,value:o}) )
-    }
-
-    let uniqueArray: { label: string, value: string }[] = Array.from(
-        new Map(rz.map(item => [JSON.stringify(item), item]))
-        .values()
-    );
-    return uniqueArray ;
+	let rz =[ ];
+	for(let o of config.value){
+		rz.push({label:o.modelDescribe,value:o.modelName})
+	}
+
+	if(gptConfigStore.myData.userModel){
+		let arr = gptConfigStore.myData.userModel.split(/[ ,]+/ig);
+		for(let o of arr ){
+			rz.push({label:o,value:o})
+		}
+	}
+	//服务端的 CUSTOM_MODELS 设置
+	if( homeStore.myData.session.cmodels ){
+		let delModel:string[] = [];
+		let addModel:string[]=[];
+		homeStore.myData.session.cmodels.split(/[ ,]+/ig).map( (v:string)=>{
+			if(v.indexOf('-')==0){
+				delModel.push(v.substring(1))
+			}else{
+				addModel.push(v);
+			}
+		});
+		mlog('cmodels',delModel,addModel);
+		rz= rz.filter(v=> delModel.indexOf(v.value)==-1 );
+		addModel.map(o=>rz.push({label:o,value:o}) )
+	}
+
+	let uniqueArray: { label: string, value: string }[] = Array.from(
+		new Map(rz.map(item => [JSON.stringify(item), item]))
+			.values()
+	);
+	return uniqueArray ;
 });
 const ms= useMessage();
 
 const saveChat=(type:string)=>{
-     chatSet.save(  nGptStore.value );
-     gptConfigStore.setMyData( nGptStore.value );
-     homeStore.setMyData({act:'saveChat'});
-     if(type!='hide')ms.success( t('common.saveSuccess'));
-     emit('close');
+	chatSet.save(  nGptStore.value );
+	gptConfigStore.setMyData( nGptStore.value );
+	homeStore.setMyData({act:'saveChat'});
+	if(type!='hide')ms.success( t('common.saveSuccess'));
+	emit('close');
 }
 
 // 添加一个空选项
 const options = ref([]);
 
-  const onSelectChange = (newValue: any) => {
-    const option = options.value.find(optionValue => optionValue.value === newValue);
-    nGptStore.value.kName = option.label;
-  };
+const onSelectChange = (newValue: any) => {
+	const option = options.value.find(optionValue => optionValue.value === newValue);
+	nGptStore.value.kName = option.label;
+};
 
-  const onSelectChange1 = (newValue: any) => {
-    const option = modellist.value.find(optionValue => optionValue.value === newValue);
-    nGptStore.value.modelLabel = option.label;
-  };
+const onSelectChange1 = (newValue: any) => {
+	const option = modellist.value.find(optionValue => optionValue.value === newValue);
+	nGptStore.value.modelLabel = option.label;
+};
 
 watch(()=>nGptStore.value.model,(n)=>{
-    nGptStore.value.gpts=undefined;
-    let max=4096;
-    if( n.indexOf('vision')>-1){
-        max=4096;
-    }else if( n.indexOf('gpt-4')>-1 ||  n.indexOf('16k')>-1 ){ //['16k','8k','32k','gpt-4'].indexOf(n)>-1
-        max=4096*2;
-    }else if( n.toLowerCase().includes('claude-3') ){
-         max=4096*2;
-    }
-    config.value.maxToken=max/2;
-    if(nGptStore.value.max_tokens> config.value.maxToken ) nGptStore.value.max_tokens= config.value.maxToken;
+	nGptStore.value.gpts=undefined;
+	let max=4096;
+	if( n.indexOf('vision')>-1){
+		max=4096;
+	}else if( n.indexOf('gpt-4')>-1 ||  n.indexOf('16k')>-1 ){ //['16k','8k','32k','gpt-4'].indexOf(n)>-1
+		max=4096*2;
+	}else if( n.toLowerCase().includes('claude-3') ){
+		max=4096*2;
+	}
+	config.value.maxToken=max/2;
+	if(nGptStore.value.max_tokens> config.value.maxToken ) nGptStore.value.max_tokens= config.value.maxToken;
 })
 
 const reSet=()=>{
-    gptConfigStore.setInit();
-    nGptStore.value= gptConfigStore.myData;
+	gptConfigStore.setInit();
+	nGptStore.value= gptConfigStore.myData;
 }
 
 </script>
 <template>
-<section class="mb-5 justify-between items-center"  >
-     <div style="margin-bottom: 8px;"><span class="text-red-500">*</span>  {{ $t('mjset.model') }}</div>
-    <n-select class="change-select" v-model:value="nGptStore.model" :options="modellist" @update:value="onSelectChange1" size="small"   />
-</section>
-
-<section class="mb-5 flex justify-between items-center"  >
-    <n-input  class="change-select"  :placeholder="$t('mjchat.modlePlaceholder')" v-model:value="nGptStore.userModel">
-      <template #prefix>
-        {{ $t('mjchat.myModle') }}
-      </template>
-    </n-input>
- </section>
-
- <section class="mb-5 justify-between items-center"  >
-     <div  style="margin-bottom: 8px;">{{ $t('mjchat.knowledgeBase') }} </div>
-    <n-select class="change-select" v-model:value="nGptStore.kid" :options="options" @update:value="onSelectChange" size="small"   />
-</section>
-
- <section class=" flex justify-between items-center"  >
-     <div style="margin-bottom: 8px;"> {{ $t('mjchat.historyCnt') }}
-     </div>
-     <div class=" flex justify-end items-center w-[80%] max-w-[240px]">
-        <div class=" w-[200px]"><n-slider class="change-slider" v-model:value="nGptStore.talkCount" :step="1" :max="50" /></div>
-        <div  class="w-[40px] text-right">{{ nGptStore.talkCount }}</div>
-    </div>
-</section>
-<div class="mb-5 text-[12px] text-gray-300 dark:text-gray-300/20">{{ $t('mjchat.historyToken') }}</div>
-
- <section class=" flex justify-between items-center"  >
-     <div> {{ $t('mjchat.historyTCnt') }}
-     </div>
-     <div class=" flex justify-end items-center w-[80%] max-w-[240px]">
-        <div class=" w-[200px]"><n-slider class="change-slider" v-model:value="nGptStore.max_tokens" :step="1" :max="1280000" :min="1" /></div>
-        <div  class="w-[100px] text-right">{{ nGptStore.max_tokens }}</div>
-    </div>
-</section>
-<div class="mb-5 text-[16px] text-gray-300 dark:text-gray-300/20">{{ $t('mjchat.historyTCntInfo') }}  </div>
-
- <section class="mb-5 change-select"  >
-    <div style="margin-bottom: 8px;">{{ $t('mjchat.role') }}</div>
-    <div>
-     <n-input  type="textarea"  :placeholder=" $t('mjchat.rolePlaceholder') "   v-model:value="nGptStore.systemMessage" :autosize="{ minRows: 3 }"
-    />
-    </div>
- </section>
-
-<template v-if="st.openMore">
-    <section class=" flex justify-between items-center "  >
-        <div>{{ $t('mj.temperature') }}</div>
-        <div class=" flex justify-end items-center w-[80%] max-w-[240px]">
-            <div class=" w-[200px]"><n-slider class="change-slider" v-model:value="nGptStore.temperature" :step="0.01" :max="1" /></div>
-            <div  class="w-[40px] text-right">{{ nGptStore.temperature }}</div>
-        </div>
-    </section>
-    <div class="mb-5 text-[12px] text-gray-300 dark:text-gray-300/20"> {{ $t('mj.temperatureInfo') }}</div>
-
-
-    <section class=" flex justify-between items-center "  >
-        <div> {{ $t('mj.top_p') }}</div>
-        <div class=" flex justify-end items-center w-[80%] max-w-[240px]">
-            <div class=" w-[200px]"><n-slider class="change-slider" v-model:value="nGptStore.top_p" :step="0.01" :max="1" /></div>
-            <div  class="w-[40px] text-right">{{ nGptStore.top_p }}</div>
-        </div>
-    </section>
-    <div class="mb-5 text-[12px] text-gray-300 dark:text-gray-300/20">{{ $t('mj.top_pInfo') }}</div>
-
-    <section class=" flex justify-between items-center "  >
-        <div> {{ $t('mj.presence_penalty') }}</div>
-        <div class=" flex justify-end items-center w-[80%] max-w-[240px]">
-            <div class=" w-[200px]"><n-slider class="change-slider" v-model:value="nGptStore.presence_penalty" :step="0.01" :max="1" /></div>
-            <div  class="w-[40px] text-right">{{ nGptStore.presence_penalty }}</div>
-        </div>
-    </section>
-    <div class="mb-5 text-[12px] text-gray-300 dark:text-gray-300/20">{{ $t('mj.presence_penaltyInfo') }} </div>
-
-
-    <section class=" flex justify-between items-center "  >
-        <div>{{ $t('mj.frequency_penalty') }}</div>
-        <div class=" flex justify-end items-center w-[80%] max-w-[240px]">
-            <div class=" w-[200px]"><n-slider class="change-slider" v-model:value="nGptStore.frequency_penalty" :step="0.01" :max="1" /></div>
-            <div  class="w-[40px] text-right">{{ nGptStore.frequency_penalty }}</div>
-        </div>
-    </section>
-    <div class="mb-5 text-[12px] text-gray-300 dark:text-gray-300/20">{{ $t('mj.frequency_penaltyInfo') }}</div>
-
-    <section class="mb-4 justify-between items-center change-select"  >
-        <div style="margin-bottom: 8px;">{{ $t('mj.tts_voice') }}</div>
-        <n-select v-model:value="nGptStore.tts_voice" :options="voiceList" size="small"   />
-    </section>
-
-
-</template>
-<div v-else class="text-right cursor-pointer mb-4" @click="st.openMore=true">
-    <NTag  type="primary" round size="small" :bordered="false" class="!cursor-pointer">More...</NTag>
-</div>
-
-<section class=" text-right flex justify-end space-x-2 model-button"  >
-    <NButton :bordered="false"  @click="reSet()">{{ $t('mj.setBtBack') }}</NButton>
-    <!-- <NButton type="primary" @click="saveChat">{{ $t('mj.setBtSaveChat') }}</NButton>
-    <NButton type="primary" @click="save">{{ $t('mj.setBtSaveSys') }}</NButton> -->
-    <NButton :bordered="false" @click="saveChat('no')">{{ $t('common.save') }}</NButton>
- </section>
+	<section class="mb-5 justify-between items-center"  >
+		<div style="margin-bottom: 8px;"><span class="text-red-500">*</span>  {{ $t('mjset.model') }}</div>
+		<n-select class="change-select" v-model:value="nGptStore.model" :options="modellist" @update:value="onSelectChange1" size="small"   />
+	</section>
+
+	<section class="mb-5 flex justify-between items-center"  >
+		<n-input  class="change-select"  :placeholder="$t('mjchat.modlePlaceholder')" v-model:value="nGptStore.userModel">
+			<template #prefix>
+				{{ $t('mjchat.myModle') }}
+			</template>
+		</n-input>
+	</section>
+
+	<section class="mb-5 justify-between items-center"  >
+		<div  style="margin-bottom: 8px;">{{ $t('mjchat.knowledgeBase') }} </div>
+		<n-select class="change-select" v-model:value="nGptStore.kid" :options="options" @update:value="onSelectChange" size="small"   />
+	</section>
+
+	<section class=" flex justify-between items-center"  >
+		<div style="margin-bottom: 8px;"> {{ $t('mjchat.historyCnt') }}
+		</div>
+		<div class=" flex justify-end items-center w-[80%] max-w-[240px]">
+			<div class=" w-[200px]"><n-slider class="change-slider" v-model:value="nGptStore.talkCount" :step="1" :max="50" /></div>
+			<div  class="w-[40px] text-right">{{ nGptStore.talkCount }}</div>
+		</div>
+	</section>
+	<div class="mb-5 text-[12px] text-gray-300 dark:text-gray-300/20">{{ $t('mjchat.historyToken') }}</div>
+
+	<section class=" flex justify-between items-center"  >
+		<div> {{ $t('mjchat.historyTCnt') }}
+		</div>
+		<div class=" flex justify-end items-center w-[80%] max-w-[240px]">
+			<div class=" w-[200px]"><n-slider class="change-slider" v-model:value="nGptStore.max_tokens" :step="1" :max="1280000" :min="1" /></div>
+			<div  class="w-[100px] text-right">{{ nGptStore.max_tokens }}</div>
+		</div>
+	</section>
+	<div class="mb-5 text-[16px] text-gray-300 dark:text-gray-300/20">{{ $t('mjchat.historyTCntInfo') }}  </div>
+
+	<section class="mb-5 change-select"  >
+		<div style="margin-bottom: 8px;">{{ $t('mjchat.role') }}</div>
+		<div>
+			<n-input  type="textarea"  :placeholder=" $t('mjchat.rolePlaceholder') "   v-model:value="nGptStore.systemMessage" :autosize="{ minRows: 3 }"
+			/>
+		</div>
+	</section>
+
+	<template v-if="st.openMore">
+		<section class=" flex justify-between items-center "  >
+			<div>{{ $t('mj.temperature') }}</div>
+			<div class=" flex justify-end items-center w-[80%] max-w-[240px]">
+				<div class=" w-[200px]"><n-slider class="change-slider" v-model:value="nGptStore.temperature" :step="0.01" :max="1" /></div>
+				<div  class="w-[40px] text-right">{{ nGptStore.temperature }}</div>
+			</div>
+		</section>
+		<div class="mb-5 text-[12px] text-gray-300 dark:text-gray-300/20"> {{ $t('mj.temperatureInfo') }}</div>
+
+
+		<section class=" flex justify-between items-center "  >
+			<div> {{ $t('mj.top_p') }}</div>
+			<div class=" flex justify-end items-center w-[80%] max-w-[240px]">
+				<div class=" w-[200px]"><n-slider class="change-slider" v-model:value="nGptStore.top_p" :step="0.01" :max="1" /></div>
+				<div  class="w-[40px] text-right">{{ nGptStore.top_p }}</div>
+			</div>
+		</section>
+		<div class="mb-5 text-[12px] text-gray-300 dark:text-gray-300/20">{{ $t('mj.top_pInfo') }}</div>
+
+		<section class=" flex justify-between items-center "  >
+			<div> {{ $t('mj.presence_penalty') }}</div>
+			<div class=" flex justify-end items-center w-[80%] max-w-[240px]">
+				<div class=" w-[200px]"><n-slider class="change-slider" v-model:value="nGptStore.presence_penalty" :step="0.01" :max="1" /></div>
+				<div  class="w-[40px] text-right">{{ nGptStore.presence_penalty }}</div>
+			</div>
+		</section>
+		<div class="mb-5 text-[12px] text-gray-300 dark:text-gray-300/20">{{ $t('mj.presence_penaltyInfo') }} </div>
+
+
+		<section class=" flex justify-between items-center "  >
+			<div>{{ $t('mj.frequency_penalty') }}</div>
+			<div class=" flex justify-end items-center w-[80%] max-w-[240px]">
+				<div class=" w-[200px]"><n-slider class="change-slider" v-model:value="nGptStore.frequency_penalty" :step="0.01" :max="1" /></div>
+				<div  class="w-[40px] text-right">{{ nGptStore.frequency_penalty }}</div>
+			</div>
+		</section>
+		<div class="mb-5 text-[12px] text-gray-300 dark:text-gray-300/20">{{ $t('mj.frequency_penaltyInfo') }}</div>
+
+		<section class="mb-4 justify-between items-center change-select"  >
+			<div style="margin-bottom: 8px;">{{ $t('mj.tts_voice') }}</div>
+			<n-select v-model:value="nGptStore.tts_voice" :options="voiceList" size="small"   />
+		</section>
+
+
+	</template>
+	<div v-else class="text-right cursor-pointer mb-4" @click="st.openMore=true">
+		<NTag  type="primary" round size="small" :bordered="false" class="!cursor-pointer">More...</NTag>
+	</div>
+
+	<section class=" text-right flex justify-end space-x-2 model-button"  >
+		<NButton :bordered="false"  @click="reSet()">{{ $t('mj.setBtBack') }}</NButton>
+		<!-- <NButton type="primary" @click="saveChat">{{ $t('mj.setBtSaveChat') }}</NButton>
+		<NButton type="primary" @click="save">{{ $t('mj.setBtSaveSys') }}</NButton> -->
+		<NButton :bordered="false" @click="saveChat('no')">{{ $t('common.save') }}</NButton>
+	</section>
 </template>

+ 62 - 0
src/views/wxbot/layout.vue

@@ -0,0 +1,62 @@
+<script setup lang='ts'>
+import { computed } from 'vue'
+import { NLayout, NLayoutContent } from 'naive-ui'
+import { useRouter } from 'vue-router'
+import Permission from '../chat/layout/Permission.vue'
+import { useBasicLayout } from '@/hooks/useBasicLayout'
+import { homeStore, useAppStore, useAuthStore, useChatStore } from '@/store'
+import { aiSider ,aiFooter} from '@/views/mj'
+import aiMobileMenu from '@/views/mj/aiMobileMenu.vue'; 
+
+const router = useRouter()
+const appStore = useAppStore()
+const chatStore = useChatStore()
+const authStore = useAuthStore()
+
+router.replace({ name: 'Wxbot', params: { uuid: chatStore.active } })
+homeStore.setMyData({local:'wxbot'});
+const { isMobile } = useBasicLayout()
+
+const collapsed = computed(() => appStore.siderCollapsed)
+
+const needPermission = computed(() => !!authStore.session?.auth && !authStore.token)
+
+const getMobileClass = computed(() => {
+  if (isMobile.value)
+    return ['rounded-none', 'shadow-none' ]
+  return [ 'shadow-md', 'dark:border-neutral-800' ] //'border', 'rounded-md',
+})
+
+const getContainerClass = computed(() => {
+  return [
+    'h-full',
+    { 'abc': !isMobile.value && !collapsed.value },
+  ]
+}) 
+</script>
+
+<template>
+  <div class="dark:bg-[#24272e] transition-all p-0" :class="[isMobile ? 'h55' : 'h-full' ]">
+    <div class="h-full overflow-hidden" :class="getMobileClass">
+      <NLayout class="z-40 transition" :class="getContainerClass" has-sider  :sider-placement="isMobile?'left': 'right'">
+        <aiSider v-if="!isMobile"/>
+       
+        <NLayoutContent class="h-full">
+          <RouterView v-slot="{ Component, route }">
+            <component :is="Component" :key="route.fullPath" />
+          </RouterView>
+        </NLayoutContent>
+         <!-- <Sider /> -->
+      </NLayout>
+    </div>
+    <Permission :visible="needPermission" />
+  </div>
+   <aiMobileMenu v-if="isMobile"   /> 
+  <aiFooter/>
+  <player/>
+</template>
+<style  >
+.h55{
+  height: calc(100% - 55px);
+}
+</style>

+ 1 - 1
vercel.json

@@ -9,4 +9,4 @@
       "destination": "/api/proxy"
     }
   ]
-}
+}

+ 4 - 4
vite.config.ts

@@ -4,7 +4,7 @@ import vue from '@vitejs/plugin-vue'
 import { VitePWA } from 'vite-plugin-pwa'
 import { createSvgIconsPlugin } from 'vite-plugin-svg-icons'
 
-function setupPlugins(env: Record<string, string>) {
+function setupPlugins(env) {
   return [
     vue(),
     env.VITE_GLOB_APP_PWA === 'true' && VitePWA({
@@ -24,7 +24,7 @@ function setupPlugins(env: Record<string, string>) {
       // 指定 symbolId 格式
       symbolId: 'icon-[name]',
     }),
-  ].filter(Boolean) // 过滤掉 falsy 值
+  ].filter(Boolean); // 过滤掉 falsy 值
 }
 
 export default defineConfig(({ mode }) => {
@@ -50,12 +50,12 @@ export default defineConfig(({ mode }) => {
         '/uploads': {
           target: viteEnv.VITE_APP_API_BASE_URL,
           changeOrigin: true, // 允许跨域
-          // rewrite: path => path.replace('/api/', '/'),
+          //rewrite: path => path.replace('/api/', '/'),
         },
         '/openapi': {
           target: viteEnv.VITE_APP_API_BASE_URL,
           changeOrigin: true, // 允许跨域
-          // rewrite: path => path.replace('/api/', '/'),
+          //rewrite: path => path.replace('/api/', '/'),
         },
       },
     },

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