commit
d9928ecc5d
@ -28,3 +28,4 @@ software/**
|
|||||||
.github
|
.github
|
||||||
webpack
|
webpack
|
||||||
.openmcp
|
.openmcp
|
||||||
|
.vscode
|
@ -1,5 +1,13 @@
|
|||||||
# Change Log
|
# Change Log
|
||||||
|
|
||||||
|
## [main] 0.1.9
|
||||||
|
- 增加 mook 功能:可以利用随机种子或者AI生成来自动化填充测试 tool 的表单数据
|
||||||
|
- 增加工具自检功能:openmcp 的 tool 下可以点击「工具模块」 右侧的 「工具自检」进入自检模式,该模式下,用户可以自己定义工具执行的拓扑顺序,然后一次性进行自动检测。
|
||||||
|
- 修复 issue #44: 完成链接跳转的平台适配
|
||||||
|
- 修复 issue #36: 完成非文件夹打开下的成功启动
|
||||||
|
- 修复 issue #45: 数组类型参数不支持
|
||||||
|
- 修复多行对话粘贴进入对话框样式异常的问题
|
||||||
|
|
||||||
## [main] 0.1.8
|
## [main] 0.1.8
|
||||||
- 增加 STDIO 下的热更新,现在用户修改 mcp 代码,openmcp 会自动完成一切相关功能的热更新,无需用户手动重启。
|
- 增加 STDIO 下的热更新,现在用户修改 mcp 代码,openmcp 会自动完成一切相关功能的热更新,无需用户手动重启。
|
||||||
- 完成 mcpconfig.json 的导出功能,导出的 配置文件 可以通过 openmcp-sdk 框架完成低代码 agent 部署;也可以直接载入 Claude Desktop 等等 MCP 客户端中,实现 MCP 的快速部署和使用。
|
- 完成 mcpconfig.json 的导出功能,导出的 配置文件 可以通过 openmcp-sdk 框架完成低代码 agent 部署;也可以直接载入 Claude Desktop 等等 MCP 客户端中,实现 MCP 的快速部署和使用。
|
||||||
|
744
package-lock.json
generated
744
package-lock.json
generated
@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "openmcp",
|
"name": "openmcp",
|
||||||
"version": "0.1.8",
|
"version": "0.1.9",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "openmcp",
|
"name": "openmcp",
|
||||||
"version": "0.1.8",
|
"version": "0.1.9",
|
||||||
"workspaces": [
|
"workspaces": [
|
||||||
"service",
|
"service",
|
||||||
"renderer"
|
"renderer"
|
||||||
@ -2998,6 +2998,290 @@
|
|||||||
"@types/node": "*"
|
"@types/node": "*"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@types/d3": {
|
||||||
|
"version": "7.4.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/d3/-/d3-7.4.3.tgz",
|
||||||
|
"integrity": "sha512-lZXZ9ckh5R8uiFVt8ogUNf+pIrK4EsWrx2Np75WvF/eTpJ0FMHNhjXk8CKEx/+gpHbNQyJWehbFaTvqmHWB3ww==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@types/d3-array": "*",
|
||||||
|
"@types/d3-axis": "*",
|
||||||
|
"@types/d3-brush": "*",
|
||||||
|
"@types/d3-chord": "*",
|
||||||
|
"@types/d3-color": "*",
|
||||||
|
"@types/d3-contour": "*",
|
||||||
|
"@types/d3-delaunay": "*",
|
||||||
|
"@types/d3-dispatch": "*",
|
||||||
|
"@types/d3-drag": "*",
|
||||||
|
"@types/d3-dsv": "*",
|
||||||
|
"@types/d3-ease": "*",
|
||||||
|
"@types/d3-fetch": "*",
|
||||||
|
"@types/d3-force": "*",
|
||||||
|
"@types/d3-format": "*",
|
||||||
|
"@types/d3-geo": "*",
|
||||||
|
"@types/d3-hierarchy": "*",
|
||||||
|
"@types/d3-interpolate": "*",
|
||||||
|
"@types/d3-path": "*",
|
||||||
|
"@types/d3-polygon": "*",
|
||||||
|
"@types/d3-quadtree": "*",
|
||||||
|
"@types/d3-random": "*",
|
||||||
|
"@types/d3-scale": "*",
|
||||||
|
"@types/d3-scale-chromatic": "*",
|
||||||
|
"@types/d3-selection": "*",
|
||||||
|
"@types/d3-shape": "*",
|
||||||
|
"@types/d3-time": "*",
|
||||||
|
"@types/d3-time-format": "*",
|
||||||
|
"@types/d3-timer": "*",
|
||||||
|
"@types/d3-transition": "*",
|
||||||
|
"@types/d3-zoom": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@types/d3-array": {
|
||||||
|
"version": "3.2.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/d3-array/-/d3-array-3.2.1.tgz",
|
||||||
|
"integrity": "sha512-Y2Jn2idRrLzUfAKV2LyRImR+y4oa2AntrgID95SHJxuMUrkNXmanDSed71sRNZysveJVt1hLLemQZIady0FpEg==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/@types/d3-axis": {
|
||||||
|
"version": "3.0.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/d3-axis/-/d3-axis-3.0.6.tgz",
|
||||||
|
"integrity": "sha512-pYeijfZuBd87T0hGn0FO1vQ/cgLk6E1ALJjfkC0oJ8cbwkZl3TpgS8bVBLZN+2jjGgg38epgxb2zmoGtSfvgMw==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@types/d3-selection": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@types/d3-brush": {
|
||||||
|
"version": "3.0.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/d3-brush/-/d3-brush-3.0.6.tgz",
|
||||||
|
"integrity": "sha512-nH60IZNNxEcrh6L1ZSMNA28rj27ut/2ZmI3r96Zd+1jrZD++zD3LsMIjWlvg4AYrHn/Pqz4CF3veCxGjtbqt7A==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@types/d3-selection": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@types/d3-chord": {
|
||||||
|
"version": "3.0.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/d3-chord/-/d3-chord-3.0.6.tgz",
|
||||||
|
"integrity": "sha512-LFYWWd8nwfwEmTZG9PfQxd17HbNPksHBiJHaKuY1XeqscXacsS2tyoo6OdRsjf+NQYeB6XrNL3a25E3gH69lcg==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/@types/d3-color": {
|
||||||
|
"version": "3.1.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/d3-color/-/d3-color-3.1.3.tgz",
|
||||||
|
"integrity": "sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/@types/d3-contour": {
|
||||||
|
"version": "3.0.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/d3-contour/-/d3-contour-3.0.6.tgz",
|
||||||
|
"integrity": "sha512-BjzLgXGnCWjUSYGfH1cpdo41/hgdWETu4YxpezoztawmqsvCeep+8QGfiY6YbDvfgHz/DkjeIkkZVJavB4a3rg==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@types/d3-array": "*",
|
||||||
|
"@types/geojson": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@types/d3-delaunay": {
|
||||||
|
"version": "6.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/d3-delaunay/-/d3-delaunay-6.0.4.tgz",
|
||||||
|
"integrity": "sha512-ZMaSKu4THYCU6sV64Lhg6qjf1orxBthaC161plr5KuPHo3CNm8DTHiLw/5Eq2b6TsNP0W0iJrUOFscY6Q450Hw==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/@types/d3-dispatch": {
|
||||||
|
"version": "3.0.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/d3-dispatch/-/d3-dispatch-3.0.6.tgz",
|
||||||
|
"integrity": "sha512-4fvZhzMeeuBJYZXRXrRIQnvUYfyXwYmLsdiN7XXmVNQKKw1cM8a5WdID0g1hVFZDqT9ZqZEY5pD44p24VS7iZQ==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/@types/d3-drag": {
|
||||||
|
"version": "3.0.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/d3-drag/-/d3-drag-3.0.7.tgz",
|
||||||
|
"integrity": "sha512-HE3jVKlzU9AaMazNufooRJ5ZpWmLIoc90A37WU2JMmeq28w1FQqCZswHZ3xR+SuxYftzHq6WU6KJHvqxKzTxxQ==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@types/d3-selection": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@types/d3-dsv": {
|
||||||
|
"version": "3.0.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/d3-dsv/-/d3-dsv-3.0.7.tgz",
|
||||||
|
"integrity": "sha512-n6QBF9/+XASqcKK6waudgL0pf/S5XHPPI8APyMLLUHd8NqouBGLsU8MgtO7NINGtPBtk9Kko/W4ea0oAspwh9g==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/@types/d3-ease": {
|
||||||
|
"version": "3.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/d3-ease/-/d3-ease-3.0.2.tgz",
|
||||||
|
"integrity": "sha512-NcV1JjO5oDzoK26oMzbILE6HW7uVXOHLQvHshBUW4UMdZGfiY6v5BeQwh9a9tCzv+CeefZQHJt5SRgK154RtiA==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/@types/d3-fetch": {
|
||||||
|
"version": "3.0.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/d3-fetch/-/d3-fetch-3.0.7.tgz",
|
||||||
|
"integrity": "sha512-fTAfNmxSb9SOWNB9IoG5c8Hg6R+AzUHDRlsXsDZsNp6sxAEOP0tkP3gKkNSO/qmHPoBFTxNrjDprVHDQDvo5aA==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@types/d3-dsv": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@types/d3-force": {
|
||||||
|
"version": "3.0.10",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/d3-force/-/d3-force-3.0.10.tgz",
|
||||||
|
"integrity": "sha512-ZYeSaCF3p73RdOKcjj+swRlZfnYpK1EbaDiYICEEp5Q6sUiqFaFQ9qgoshp5CzIyyb/yD09kD9o2zEltCexlgw==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/@types/d3-format": {
|
||||||
|
"version": "3.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/d3-format/-/d3-format-3.0.4.tgz",
|
||||||
|
"integrity": "sha512-fALi2aI6shfg7vM5KiR1wNJnZ7r6UuggVqtDA+xiEdPZQwy/trcQaHnwShLuLdta2rTymCNpxYTiMZX/e09F4g==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/@types/d3-geo": {
|
||||||
|
"version": "3.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/d3-geo/-/d3-geo-3.1.0.tgz",
|
||||||
|
"integrity": "sha512-856sckF0oP/diXtS4jNsiQw/UuK5fQG8l/a9VVLeSouf1/PPbBE1i1W852zVwKwYCBkFJJB7nCFTbk6UMEXBOQ==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@types/geojson": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@types/d3-hierarchy": {
|
||||||
|
"version": "3.1.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/d3-hierarchy/-/d3-hierarchy-3.1.7.tgz",
|
||||||
|
"integrity": "sha512-tJFtNoYBtRtkNysX1Xq4sxtjK8YgoWUNpIiUee0/jHGRwqvzYxkq0hGVbbOGSz+JgFxxRu4K8nb3YpG3CMARtg==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/@types/d3-interpolate": {
|
||||||
|
"version": "3.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/d3-interpolate/-/d3-interpolate-3.0.4.tgz",
|
||||||
|
"integrity": "sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@types/d3-color": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@types/d3-path": {
|
||||||
|
"version": "3.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/d3-path/-/d3-path-3.1.1.tgz",
|
||||||
|
"integrity": "sha512-VMZBYyQvbGmWyWVea0EHs/BwLgxc+MKi1zLDCONksozI4YJMcTt8ZEuIR4Sb1MMTE8MMW49v0IwI5+b7RmfWlg==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/@types/d3-polygon": {
|
||||||
|
"version": "3.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/d3-polygon/-/d3-polygon-3.0.2.tgz",
|
||||||
|
"integrity": "sha512-ZuWOtMaHCkN9xoeEMr1ubW2nGWsp4nIql+OPQRstu4ypeZ+zk3YKqQT0CXVe/PYqrKpZAi+J9mTs05TKwjXSRA==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/@types/d3-quadtree": {
|
||||||
|
"version": "3.0.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/d3-quadtree/-/d3-quadtree-3.0.6.tgz",
|
||||||
|
"integrity": "sha512-oUzyO1/Zm6rsxKRHA1vH0NEDG58HrT5icx/azi9MF1TWdtttWl0UIUsjEQBBh+SIkrpd21ZjEv7ptxWys1ncsg==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/@types/d3-random": {
|
||||||
|
"version": "3.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/d3-random/-/d3-random-3.0.3.tgz",
|
||||||
|
"integrity": "sha512-Imagg1vJ3y76Y2ea0871wpabqp613+8/r0mCLEBfdtqC7xMSfj9idOnmBYyMoULfHePJyxMAw3nWhJxzc+LFwQ==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/@types/d3-scale": {
|
||||||
|
"version": "4.0.9",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/d3-scale/-/d3-scale-4.0.9.tgz",
|
||||||
|
"integrity": "sha512-dLmtwB8zkAeO/juAMfnV+sItKjlsw2lKdZVVy6LRr0cBmegxSABiLEpGVmSJJ8O08i4+sGR6qQtb6WtuwJdvVw==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@types/d3-time": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@types/d3-scale-chromatic": {
|
||||||
|
"version": "3.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/d3-scale-chromatic/-/d3-scale-chromatic-3.1.0.tgz",
|
||||||
|
"integrity": "sha512-iWMJgwkK7yTRmWqRB5plb1kadXyQ5Sj8V/zYlFGMUBbIPKQScw+Dku9cAAMgJG+z5GYDoMjWGLVOvjghDEFnKQ==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/@types/d3-selection": {
|
||||||
|
"version": "3.0.11",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/d3-selection/-/d3-selection-3.0.11.tgz",
|
||||||
|
"integrity": "sha512-bhAXu23DJWsrI45xafYpkQ4NtcKMwWnAC/vKrd2l+nxMFuvOT3XMYTIj2opv8vq8AO5Yh7Qac/nSeP/3zjTK0w==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/@types/d3-shape": {
|
||||||
|
"version": "3.1.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/d3-shape/-/d3-shape-3.1.7.tgz",
|
||||||
|
"integrity": "sha512-VLvUQ33C+3J+8p+Daf+nYSOsjB4GXp19/S/aGo60m9h1v6XaxjiT82lKVWJCfzhtuZ3yD7i/TPeC/fuKLLOSmg==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@types/d3-path": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@types/d3-time": {
|
||||||
|
"version": "3.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/d3-time/-/d3-time-3.0.4.tgz",
|
||||||
|
"integrity": "sha512-yuzZug1nkAAaBlBBikKZTgzCeA+k1uy4ZFwWANOfKw5z5LRhV0gNA7gNkKm7HoK+HRN0wX3EkxGk0fpbWhmB7g==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/@types/d3-time-format": {
|
||||||
|
"version": "4.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/d3-time-format/-/d3-time-format-4.0.3.tgz",
|
||||||
|
"integrity": "sha512-5xg9rC+wWL8kdDj153qZcsJ0FWiFt0J5RB6LYUNZjwSnesfblqrI/bJ1wBdJ8OQfncgbJG5+2F+qfqnqyzYxyg==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/@types/d3-timer": {
|
||||||
|
"version": "3.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/d3-timer/-/d3-timer-3.0.2.tgz",
|
||||||
|
"integrity": "sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/@types/d3-transition": {
|
||||||
|
"version": "3.0.9",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/d3-transition/-/d3-transition-3.0.9.tgz",
|
||||||
|
"integrity": "sha512-uZS5shfxzO3rGlu0cC3bjmMFKsXv+SmZZcgp0KD22ts4uGXp5EVYGzu/0YdwZeKmddhcAccYtREJKkPfXkZuCg==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@types/d3-selection": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@types/d3-zoom": {
|
||||||
|
"version": "3.0.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/d3-zoom/-/d3-zoom-3.0.8.tgz",
|
||||||
|
"integrity": "sha512-iqMC4/YlFCSlO8+2Ii1GGGliCAY4XdeG748w5vQUbevlbDu0zSjH/+jojorQVBK/se0j6DUFNPBGSqD3YWYnDw==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@types/d3-interpolate": "*",
|
||||||
|
"@types/d3-selection": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@types/debug": {
|
"node_modules/@types/debug": {
|
||||||
"version": "4.1.12",
|
"version": "4.1.12",
|
||||||
"resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.12.tgz",
|
"resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.12.tgz",
|
||||||
@ -3072,6 +3356,13 @@
|
|||||||
"@types/node": "*"
|
"@types/node": "*"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@types/geojson": {
|
||||||
|
"version": "7946.0.16",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.16.tgz",
|
||||||
|
"integrity": "sha512-6C8nqWur3j98U6+lXDfTUWIfgvZU+EumvpHKcYjujKH7woYyLj2sUmff0tRhrqM7BohUw7Pz3ZB1jj2gW9Fvmg==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/@types/glob": {
|
"node_modules/@types/glob": {
|
||||||
"version": "7.2.0",
|
"version": "7.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.2.0.tgz",
|
||||||
@ -5735,6 +6026,416 @@
|
|||||||
"integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==",
|
"integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/d3": {
|
||||||
|
"version": "7.9.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/d3/-/d3-7.9.0.tgz",
|
||||||
|
"integrity": "sha512-e1U46jVP+w7Iut8Jt8ri1YsPOvFpg46k+K8TpCb0P+zjCkjkPnV7WzfDJzMHy1LnA+wj5pLT1wjO901gLXeEhA==",
|
||||||
|
"license": "ISC",
|
||||||
|
"dependencies": {
|
||||||
|
"d3-array": "3",
|
||||||
|
"d3-axis": "3",
|
||||||
|
"d3-brush": "3",
|
||||||
|
"d3-chord": "3",
|
||||||
|
"d3-color": "3",
|
||||||
|
"d3-contour": "4",
|
||||||
|
"d3-delaunay": "6",
|
||||||
|
"d3-dispatch": "3",
|
||||||
|
"d3-drag": "3",
|
||||||
|
"d3-dsv": "3",
|
||||||
|
"d3-ease": "3",
|
||||||
|
"d3-fetch": "3",
|
||||||
|
"d3-force": "3",
|
||||||
|
"d3-format": "3",
|
||||||
|
"d3-geo": "3",
|
||||||
|
"d3-hierarchy": "3",
|
||||||
|
"d3-interpolate": "3",
|
||||||
|
"d3-path": "3",
|
||||||
|
"d3-polygon": "3",
|
||||||
|
"d3-quadtree": "3",
|
||||||
|
"d3-random": "3",
|
||||||
|
"d3-scale": "4",
|
||||||
|
"d3-scale-chromatic": "3",
|
||||||
|
"d3-selection": "3",
|
||||||
|
"d3-shape": "3",
|
||||||
|
"d3-time": "3",
|
||||||
|
"d3-time-format": "4",
|
||||||
|
"d3-timer": "3",
|
||||||
|
"d3-transition": "3",
|
||||||
|
"d3-zoom": "3"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/d3-array": {
|
||||||
|
"version": "3.2.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/d3-array/-/d3-array-3.2.4.tgz",
|
||||||
|
"integrity": "sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==",
|
||||||
|
"license": "ISC",
|
||||||
|
"dependencies": {
|
||||||
|
"internmap": "1 - 2"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/d3-axis": {
|
||||||
|
"version": "3.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/d3-axis/-/d3-axis-3.0.0.tgz",
|
||||||
|
"integrity": "sha512-IH5tgjV4jE/GhHkRV0HiVYPDtvfjHQlQfJHs0usq7M30XcSBvOotpmH1IgkcXsO/5gEQZD43B//fc7SRT5S+xw==",
|
||||||
|
"license": "ISC",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/d3-brush": {
|
||||||
|
"version": "3.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/d3-brush/-/d3-brush-3.0.0.tgz",
|
||||||
|
"integrity": "sha512-ALnjWlVYkXsVIGlOsuWH1+3udkYFI48Ljihfnh8FZPF2QS9o+PzGLBslO0PjzVoHLZ2KCVgAM8NVkXPJB2aNnQ==",
|
||||||
|
"license": "ISC",
|
||||||
|
"dependencies": {
|
||||||
|
"d3-dispatch": "1 - 3",
|
||||||
|
"d3-drag": "2 - 3",
|
||||||
|
"d3-interpolate": "1 - 3",
|
||||||
|
"d3-selection": "3",
|
||||||
|
"d3-transition": "3"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/d3-chord": {
|
||||||
|
"version": "3.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/d3-chord/-/d3-chord-3.0.1.tgz",
|
||||||
|
"integrity": "sha512-VE5S6TNa+j8msksl7HwjxMHDM2yNK3XCkusIlpX5kwauBfXuyLAtNg9jCp/iHH61tgI4sb6R/EIMWCqEIdjT/g==",
|
||||||
|
"license": "ISC",
|
||||||
|
"dependencies": {
|
||||||
|
"d3-path": "1 - 3"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/d3-color": {
|
||||||
|
"version": "3.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/d3-color/-/d3-color-3.1.0.tgz",
|
||||||
|
"integrity": "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==",
|
||||||
|
"license": "ISC",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/d3-contour": {
|
||||||
|
"version": "4.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/d3-contour/-/d3-contour-4.0.2.tgz",
|
||||||
|
"integrity": "sha512-4EzFTRIikzs47RGmdxbeUvLWtGedDUNkTcmzoeyg4sP/dvCexO47AaQL7VKy/gul85TOxw+IBgA8US2xwbToNA==",
|
||||||
|
"license": "ISC",
|
||||||
|
"dependencies": {
|
||||||
|
"d3-array": "^3.2.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/d3-delaunay": {
|
||||||
|
"version": "6.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/d3-delaunay/-/d3-delaunay-6.0.4.tgz",
|
||||||
|
"integrity": "sha512-mdjtIZ1XLAM8bm/hx3WwjfHt6Sggek7qH043O8KEjDXN40xi3vx/6pYSVTwLjEgiXQTbvaouWKynLBiUZ6SK6A==",
|
||||||
|
"license": "ISC",
|
||||||
|
"dependencies": {
|
||||||
|
"delaunator": "5"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/d3-dispatch": {
|
||||||
|
"version": "3.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/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.npmjs.org/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-dsv": {
|
||||||
|
"version": "3.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/d3-dsv/-/d3-dsv-3.0.1.tgz",
|
||||||
|
"integrity": "sha512-UG6OvdI5afDIFP9w4G0mNq50dSOsXHJaRE8arAS5o9ApWnIElp8GZw1Dun8vP8OyHOZ/QJUKUJwxiiCCnUwm+Q==",
|
||||||
|
"license": "ISC",
|
||||||
|
"dependencies": {
|
||||||
|
"commander": "7",
|
||||||
|
"iconv-lite": "0.6",
|
||||||
|
"rw": "1"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"csv2json": "bin/dsv2json.js",
|
||||||
|
"csv2tsv": "bin/dsv2dsv.js",
|
||||||
|
"dsv2dsv": "bin/dsv2dsv.js",
|
||||||
|
"dsv2json": "bin/dsv2json.js",
|
||||||
|
"json2csv": "bin/json2dsv.js",
|
||||||
|
"json2dsv": "bin/json2dsv.js",
|
||||||
|
"json2tsv": "bin/json2dsv.js",
|
||||||
|
"tsv2csv": "bin/dsv2dsv.js",
|
||||||
|
"tsv2json": "bin/dsv2json.js"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/d3-dsv/node_modules/commander": {
|
||||||
|
"version": "7.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz",
|
||||||
|
"integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 10"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/d3-ease": {
|
||||||
|
"version": "3.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-3.0.1.tgz",
|
||||||
|
"integrity": "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==",
|
||||||
|
"license": "BSD-3-Clause",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/d3-fetch": {
|
||||||
|
"version": "3.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/d3-fetch/-/d3-fetch-3.0.1.tgz",
|
||||||
|
"integrity": "sha512-kpkQIM20n3oLVBKGg6oHrUchHM3xODkTzjMoj7aWQFq5QEM+R6E4WkzT5+tojDY7yjez8KgCBRoj4aEr99Fdqw==",
|
||||||
|
"license": "ISC",
|
||||||
|
"dependencies": {
|
||||||
|
"d3-dsv": "1 - 3"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/d3-force": {
|
||||||
|
"version": "3.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/d3-force/-/d3-force-3.0.0.tgz",
|
||||||
|
"integrity": "sha512-zxV/SsA+U4yte8051P4ECydjD/S+qeYtnaIyAs9tgHCqfguma/aAQDjo85A9Z6EKhBirHRJHXIgJUlffT4wdLg==",
|
||||||
|
"license": "ISC",
|
||||||
|
"dependencies": {
|
||||||
|
"d3-dispatch": "1 - 3",
|
||||||
|
"d3-quadtree": "1 - 3",
|
||||||
|
"d3-timer": "1 - 3"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/d3-format": {
|
||||||
|
"version": "3.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/d3-format/-/d3-format-3.1.0.tgz",
|
||||||
|
"integrity": "sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA==",
|
||||||
|
"license": "ISC",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/d3-geo": {
|
||||||
|
"version": "3.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/d3-geo/-/d3-geo-3.1.1.tgz",
|
||||||
|
"integrity": "sha512-637ln3gXKXOwhalDzinUgY83KzNWZRKbYubaG+fGVuc/dxO64RRljtCTnf5ecMyE1RIdtqpkVcq0IbtU2S8j2Q==",
|
||||||
|
"license": "ISC",
|
||||||
|
"dependencies": {
|
||||||
|
"d3-array": "2.5.0 - 3"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/d3-hierarchy": {
|
||||||
|
"version": "3.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/d3-hierarchy/-/d3-hierarchy-3.1.2.tgz",
|
||||||
|
"integrity": "sha512-FX/9frcub54beBdugHjDCdikxThEqjnR93Qt7PvQTOHxyiNCAlvMrHhclk3cD5VeAaq9fxmfRp+CnWw9rEMBuA==",
|
||||||
|
"license": "ISC",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/d3-interpolate": {
|
||||||
|
"version": "3.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/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-path": {
|
||||||
|
"version": "3.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/d3-path/-/d3-path-3.1.0.tgz",
|
||||||
|
"integrity": "sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==",
|
||||||
|
"license": "ISC",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/d3-polygon": {
|
||||||
|
"version": "3.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/d3-polygon/-/d3-polygon-3.0.1.tgz",
|
||||||
|
"integrity": "sha512-3vbA7vXYwfe1SYhED++fPUQlWSYTTGmFmQiany/gdbiWgU/iEyQzyymwL9SkJjFFuCS4902BSzewVGsHHmHtXg==",
|
||||||
|
"license": "ISC",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/d3-quadtree": {
|
||||||
|
"version": "3.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/d3-quadtree/-/d3-quadtree-3.0.1.tgz",
|
||||||
|
"integrity": "sha512-04xDrxQTDTCFwP5H6hRhsRcb9xxv2RzkcsygFzmkSIOJy3PeRJP7sNk3VRIbKXcog561P9oU0/rVH6vDROAgUw==",
|
||||||
|
"license": "ISC",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/d3-random": {
|
||||||
|
"version": "3.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/d3-random/-/d3-random-3.0.1.tgz",
|
||||||
|
"integrity": "sha512-FXMe9GfxTxqd5D6jFsQ+DJ8BJS4E/fT5mqqdjovykEB2oFbTMDVdg1MGFxfQW+FBOGoB++k8swBrgwSHT1cUXQ==",
|
||||||
|
"license": "ISC",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/d3-scale": {
|
||||||
|
"version": "4.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-4.0.2.tgz",
|
||||||
|
"integrity": "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==",
|
||||||
|
"license": "ISC",
|
||||||
|
"dependencies": {
|
||||||
|
"d3-array": "2.10.0 - 3",
|
||||||
|
"d3-format": "1 - 3",
|
||||||
|
"d3-interpolate": "1.2.0 - 3",
|
||||||
|
"d3-time": "2.1.1 - 3",
|
||||||
|
"d3-time-format": "2 - 4"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/d3-scale-chromatic": {
|
||||||
|
"version": "3.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/d3-scale-chromatic/-/d3-scale-chromatic-3.1.0.tgz",
|
||||||
|
"integrity": "sha512-A3s5PWiZ9YCXFye1o246KoscMWqf8BsD9eRiJ3He7C9OBaxKhAd5TFCdEx/7VbKtxxTsu//1mMJFrEt572cEyQ==",
|
||||||
|
"license": "ISC",
|
||||||
|
"dependencies": {
|
||||||
|
"d3-color": "1 - 3",
|
||||||
|
"d3-interpolate": "1 - 3"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/d3-selection": {
|
||||||
|
"version": "3.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-3.0.0.tgz",
|
||||||
|
"integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==",
|
||||||
|
"license": "ISC",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/d3-shape": {
|
||||||
|
"version": "3.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-3.2.0.tgz",
|
||||||
|
"integrity": "sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==",
|
||||||
|
"license": "ISC",
|
||||||
|
"dependencies": {
|
||||||
|
"d3-path": "^3.1.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/d3-time": {
|
||||||
|
"version": "3.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/d3-time/-/d3-time-3.1.0.tgz",
|
||||||
|
"integrity": "sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==",
|
||||||
|
"license": "ISC",
|
||||||
|
"dependencies": {
|
||||||
|
"d3-array": "2 - 3"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/d3-time-format": {
|
||||||
|
"version": "4.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-4.1.0.tgz",
|
||||||
|
"integrity": "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==",
|
||||||
|
"license": "ISC",
|
||||||
|
"dependencies": {
|
||||||
|
"d3-time": "1 - 3"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/d3-timer": {
|
||||||
|
"version": "3.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/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.npmjs.org/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.npmjs.org/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/dateformat": {
|
"node_modules/dateformat": {
|
||||||
"version": "4.6.3",
|
"version": "4.6.3",
|
||||||
"resolved": "https://registry.npmjs.org/dateformat/-/dateformat-4.6.3.tgz",
|
"resolved": "https://registry.npmjs.org/dateformat/-/dateformat-4.6.3.tgz",
|
||||||
@ -5881,6 +6582,15 @@
|
|||||||
"url": "https://github.com/sponsors/sindresorhus"
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/delaunator": {
|
||||||
|
"version": "5.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/delaunator/-/delaunator-5.0.1.tgz",
|
||||||
|
"integrity": "sha512-8nvh+XBe96aCESrGOqMp/84b13H9cdKbG5P2ejQCh4d4sK9RL4371qou9drQjMhvnPmhWl5hnmqbEE0fXr9Xnw==",
|
||||||
|
"license": "ISC",
|
||||||
|
"dependencies": {
|
||||||
|
"robust-predicates": "^3.0.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/delayed-stream": {
|
"node_modules/delayed-stream": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
|
||||||
@ -6077,6 +6787,12 @@
|
|||||||
"vue": "^3.2.0"
|
"vue": "^3.2.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/elkjs": {
|
||||||
|
"version": "0.10.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/elkjs/-/elkjs-0.10.0.tgz",
|
||||||
|
"integrity": "sha512-v/3r+3Bl2NMrWmVoRTMBtHtWvRISTix/s9EfnsfEWApNrsmNjqgqJOispCGg46BPwIFdkag3N/HYSxJczvCm6w==",
|
||||||
|
"license": "EPL-2.0"
|
||||||
|
},
|
||||||
"node_modules/emoji-regex": {
|
"node_modules/emoji-regex": {
|
||||||
"version": "8.0.0",
|
"version": "8.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
|
||||||
@ -7950,6 +8666,15 @@
|
|||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"optional": true
|
"optional": true
|
||||||
},
|
},
|
||||||
|
"node_modules/internmap": {
|
||||||
|
"version": "2.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/internmap/-/internmap-2.0.3.tgz",
|
||||||
|
"integrity": "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==",
|
||||||
|
"license": "ISC",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/interpret": {
|
"node_modules/interpret": {
|
||||||
"version": "3.1.1",
|
"version": "3.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/interpret/-/interpret-3.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/interpret/-/interpret-3.1.1.tgz",
|
||||||
@ -11291,6 +12016,12 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/robust-predicates": {
|
||||||
|
"version": "3.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/robust-predicates/-/robust-predicates-3.0.2.tgz",
|
||||||
|
"integrity": "sha512-IXgzBWvWQwE6PrDI05OvmXUIruQTcoMDzRsOd5CDvHCVLcLHMTSYvOK5Cm46kWqlV3yAbuSpBZdJ5oP5OUoStg==",
|
||||||
|
"license": "Unlicense"
|
||||||
|
},
|
||||||
"node_modules/rollup": {
|
"node_modules/rollup": {
|
||||||
"version": "4.44.0",
|
"version": "4.44.0",
|
||||||
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.44.0.tgz",
|
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.44.0.tgz",
|
||||||
@ -11523,6 +12254,12 @@
|
|||||||
"queue-microtask": "^1.2.2"
|
"queue-microtask": "^1.2.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/rw": {
|
||||||
|
"version": "1.3.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/rw/-/rw-1.3.3.tgz",
|
||||||
|
"integrity": "sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ==",
|
||||||
|
"license": "BSD-3-Clause"
|
||||||
|
},
|
||||||
"node_modules/safe-buffer": {
|
"node_modules/safe-buffer": {
|
||||||
"version": "5.2.1",
|
"version": "5.2.1",
|
||||||
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
|
||||||
@ -14155,7 +14892,9 @@
|
|||||||
"chalk": "^5.4.1",
|
"chalk": "^5.4.1",
|
||||||
"codemirror": "^6.0.1",
|
"codemirror": "^6.0.1",
|
||||||
"core-js": "^3.8.3",
|
"core-js": "^3.8.3",
|
||||||
|
"d3": "^7.9.0",
|
||||||
"element-plus": "^2.9.9",
|
"element-plus": "^2.9.9",
|
||||||
|
"elkjs": "^0.10.0",
|
||||||
"json-schema-faker": "^0.5.9",
|
"json-schema-faker": "^0.5.9",
|
||||||
"katex": "^0.16.21",
|
"katex": "^0.16.21",
|
||||||
"lodash": "^4.17.21",
|
"lodash": "^4.17.21",
|
||||||
@ -14179,6 +14918,7 @@
|
|||||||
"@originjs/vite-plugin-commonjs": "^1.0.3",
|
"@originjs/vite-plugin-commonjs": "^1.0.3",
|
||||||
"@rollup/pluginutils": "^5.1.4",
|
"@rollup/pluginutils": "^5.1.4",
|
||||||
"@tsconfig/node22": "^22.0.1",
|
"@tsconfig/node22": "^22.0.1",
|
||||||
|
"@types/d3": "^7.4.3",
|
||||||
"@types/markdown-it": "^14.1.2",
|
"@types/markdown-it": "^14.1.2",
|
||||||
"@types/node": "^22.14.0",
|
"@types/node": "^22.14.0",
|
||||||
"@types/prismjs": "^1.26.5",
|
"@types/prismjs": "^1.26.5",
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
"name": "openmcp",
|
"name": "openmcp",
|
||||||
"displayName": "OpenMCP",
|
"displayName": "OpenMCP",
|
||||||
"description": "An all in one MCP Client/TestTool",
|
"description": "An all in one MCP Client/TestTool",
|
||||||
"version": "0.1.8",
|
"version": "0.1.9",
|
||||||
"publisher": "kirigaya",
|
"publisher": "kirigaya",
|
||||||
"private": true,
|
"private": true,
|
||||||
"author": {
|
"author": {
|
||||||
@ -224,7 +224,7 @@
|
|||||||
"setup": "yarn install && yarn prepare:ocr",
|
"setup": "yarn install && yarn prepare:ocr",
|
||||||
"serve": "turbo serve",
|
"serve": "turbo serve",
|
||||||
"build": "turbo build && tsc -p ./ && node esbuild.config.js",
|
"build": "turbo build && tsc -p ./ && node esbuild.config.js",
|
||||||
"build:plugin": "yarn build && tsc && vsce package",
|
"build:plugin": "yarn build && tsc && vsce package --allow-package-all-secrets",
|
||||||
"vscode:prepublish": "node esbuild.config.js",
|
"vscode:prepublish": "node esbuild.config.js",
|
||||||
"compile": "tsc -p ./",
|
"compile": "tsc -p ./",
|
||||||
"watch": "tsc -watch -p ./",
|
"watch": "tsc -watch -p ./",
|
||||||
|
@ -23,7 +23,9 @@
|
|||||||
"chalk": "^5.4.1",
|
"chalk": "^5.4.1",
|
||||||
"codemirror": "^6.0.1",
|
"codemirror": "^6.0.1",
|
||||||
"core-js": "^3.8.3",
|
"core-js": "^3.8.3",
|
||||||
|
"d3": "^7.9.0",
|
||||||
"element-plus": "^2.9.9",
|
"element-plus": "^2.9.9",
|
||||||
|
"elkjs": "^0.10.0",
|
||||||
"json-schema-faker": "^0.5.9",
|
"json-schema-faker": "^0.5.9",
|
||||||
"katex": "^0.16.21",
|
"katex": "^0.16.21",
|
||||||
"lodash": "^4.17.21",
|
"lodash": "^4.17.21",
|
||||||
@ -47,6 +49,7 @@
|
|||||||
"@originjs/vite-plugin-commonjs": "^1.0.3",
|
"@originjs/vite-plugin-commonjs": "^1.0.3",
|
||||||
"@rollup/pluginutils": "^5.1.4",
|
"@rollup/pluginutils": "^5.1.4",
|
||||||
"@tsconfig/node22": "^22.0.1",
|
"@tsconfig/node22": "^22.0.1",
|
||||||
|
"@types/d3": "^7.4.3",
|
||||||
"@types/markdown-it": "^14.1.2",
|
"@types/markdown-it": "^14.1.2",
|
||||||
"@types/node": "^22.14.0",
|
"@types/node": "^22.14.0",
|
||||||
"@types/prismjs": "^1.26.5",
|
"@types/prismjs": "^1.26.5",
|
||||||
|
@ -3,6 +3,15 @@
|
|||||||
--main-color: #CB81DA;
|
--main-color: #CB81DA;
|
||||||
--main-dark-color: #2D323B;
|
--main-dark-color: #2D323B;
|
||||||
--main-light-color: rgba(203, 129, 218, 0.7);
|
--main-light-color: rgba(203, 129, 218, 0.7);
|
||||||
|
--main-light-color-90: rgba(203, 129, 218, 0.9);
|
||||||
|
--main-light-color-80: rgba(203, 129, 218, 0.8);
|
||||||
|
--main-light-color-70: rgba(203, 129, 218, 0.7);
|
||||||
|
--main-light-color-60: rgba(203, 129, 218, 0.6);
|
||||||
|
--main-light-color-50: rgba(203, 129, 218, 0.5);
|
||||||
|
--main-light-color-40: rgba(203, 129, 218, 0.4);
|
||||||
|
--main-light-color-30: rgba(203, 129, 218, 0.3);
|
||||||
|
--main-light-color-20: rgba(203, 129, 218, 0.2);
|
||||||
|
--main-light-color-10: rgba(203, 129, 218, 0.1);
|
||||||
--sidebar-width: 330px;
|
--sidebar-width: 330px;
|
||||||
--right-nav-width: 50px;
|
--right-nav-width: 50px;
|
||||||
--time-scale-height: 30px;
|
--time-scale-height: 30px;
|
||||||
|
@ -163,24 +163,26 @@ function getDefaultValue(property: any): string {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 修改 watch 逻辑
|
watch(
|
||||||
// watch(
|
() => props.modelValue,
|
||||||
// () => props.modelValue,
|
(newVal) => {
|
||||||
// (newVal) => {
|
// 当前编辑器内容
|
||||||
// const currentParsed = tryParse(inputValue.value)
|
const currentContent = editorView.value?.state.doc.toString() ?? '';
|
||||||
// if (!isDeepEqual(currentParsed, newVal)) {
|
const newContent = JSON.stringify(newVal ?? {}, null, 2);
|
||||||
// const newContent = JSON.stringify(newVal, null, 2)
|
|
||||||
// editorView.value?.dispatch({
|
if (currentContent !== newContent && editorView.value) {
|
||||||
// changes: {
|
editorView.value.dispatch({
|
||||||
// from: 0,
|
changes: {
|
||||||
// to: editorView.value.state.doc.length,
|
from: 0,
|
||||||
// insert: newContent
|
to: editorView.value.state.doc.length,
|
||||||
// }
|
insert: newContent
|
||||||
// })
|
}
|
||||||
// }
|
});
|
||||||
// },
|
}
|
||||||
// { deep: true }
|
},
|
||||||
// )
|
{ deep: true }
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
// 辅助函数:尝试解析 JSON
|
// 辅助函数:尝试解析 JSON
|
||||||
const tryParse = (value: string): any => {
|
const tryParse = (value: string): any => {
|
||||||
|
@ -58,7 +58,7 @@ export interface EnableToolItem {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface ChatSetting {
|
export interface ChatSetting {
|
||||||
modelIndex: number
|
modelIndex?: number
|
||||||
systemPrompt: string
|
systemPrompt: string
|
||||||
enableTools: EnableToolItem[]
|
enableTools: EnableToolItem[]
|
||||||
temperature: number
|
temperature: number
|
||||||
|
@ -56,6 +56,7 @@ import { llmManager, llms } from '@/views/setting/llm';
|
|||||||
import { mcpClientAdapter } from '@/views/connect/core';
|
import { mcpClientAdapter } from '@/views/connect/core';
|
||||||
import { ElMessage } from 'element-plus';
|
import { ElMessage } from 'element-plus';
|
||||||
import { useMessageBridge } from '@/api/message-bridge';
|
import { useMessageBridge } from '@/api/message-bridge';
|
||||||
|
import { gotoWebsite } from '@/hook/util';
|
||||||
|
|
||||||
const { t, locale } = useI18n();
|
const { t, locale } = useI18n();
|
||||||
|
|
||||||
@ -166,11 +167,11 @@ const exportCode = async () => {
|
|||||||
|
|
||||||
const gotoHowtoUse = () => {
|
const gotoHowtoUse = () => {
|
||||||
if (locale.value === 'zh') {
|
if (locale.value === 'zh') {
|
||||||
window.open('https://kirigaya.cn/openmcp/zh/sdk-tutorial/#%E4%BD%BF%E7%94%A8');
|
gotoWebsite('https://kirigaya.cn/openmcp/zh/sdk-tutorial/#%E4%BD%BF%E7%94%A8');
|
||||||
} else if (locale.value === 'ja') {
|
} else if (locale.value === 'ja') {
|
||||||
window.open('https://kirigaya.cn/openmcp/ja/sdk-tutorial/#%E4%BD%BF%E7%94%A8%E6%96%B9%E6%B3%95');
|
gotoWebsite('https://kirigaya.cn/openmcp/ja/sdk-tutorial/#%E4%BD%BF%E7%94%A8%E6%96%B9%E6%B3%95');
|
||||||
} else {
|
} else {
|
||||||
window.open('https://kirigaya.cn/openmcp/sdk-tutorial/#usage');
|
gotoWebsite('https://kirigaya.cn/openmcp/sdk-tutorial/#usage');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -64,4 +64,15 @@ const onRadioGroupChange = () => {
|
|||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style></style>
|
<style>
|
||||||
|
|
||||||
|
.setting-button:hover {
|
||||||
|
background: var(--main-light-color, #f0f8ff);
|
||||||
|
box-shadow: 0 2px 8px 0 rgba(64,158,255,0.08);
|
||||||
|
border-color: var(--el-color-primary-light-7, #c6e2ff);
|
||||||
|
}
|
||||||
|
|
||||||
|
.setting-button:active {
|
||||||
|
transform: scale(0.95);
|
||||||
|
}
|
||||||
|
</style>
|
@ -162,16 +162,15 @@ provide('tabStorage', tabStorage);
|
|||||||
color: var(--el-text-color-secondary);
|
color: var(--el-text-color-secondary);
|
||||||
}
|
}
|
||||||
|
|
||||||
.tools-dialog-container .el-switch__core {
|
.el-switch__core {
|
||||||
border: 1px solid var(--main-color) !important;
|
border: 1px solid var(--main-color) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.el-switch .el-switch__action {
|
||||||
.tools-dialog-container .el-switch .el-switch__action {
|
|
||||||
background-color: var(--main-color);
|
background-color: var(--main-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
.tools-dialog-container .el-switch.is-checked .el-switch__action {
|
.el-switch.is-checked .el-switch__action {
|
||||||
background-color: var(--sidebar);
|
background-color: var(--sidebar);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -234,6 +234,7 @@ function handleCompositionEnd() {
|
|||||||
.rich-editor {
|
.rich-editor {
|
||||||
min-height: 100px;
|
min-height: 100px;
|
||||||
outline: none;
|
outline: none;
|
||||||
|
white-space: pre-wrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
.rich-editor:empty::before {
|
.rich-editor:empty::before {
|
||||||
|
@ -48,6 +48,7 @@ export class TaskLoop {
|
|||||||
private bridge: MessageBridge;
|
private bridge: MessageBridge;
|
||||||
private streamingContent: Ref<string>;
|
private streamingContent: Ref<string>;
|
||||||
private streamingToolCalls: Ref<ToolCall[]>;
|
private streamingToolCalls: Ref<ToolCall[]>;
|
||||||
|
private aborted = false;
|
||||||
|
|
||||||
private currentChatId = '';
|
private currentChatId = '';
|
||||||
private onError: (error: IErrorMssage) => void = (msg) => { };
|
private onError: (error: IErrorMssage) => void = (msg) => { };
|
||||||
@ -318,6 +319,7 @@ export class TaskLoop {
|
|||||||
});
|
});
|
||||||
this.streamingContent.value = '';
|
this.streamingContent.value = '';
|
||||||
this.streamingToolCalls.value = [];
|
this.streamingToolCalls.value = [];
|
||||||
|
this.aborted = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -545,6 +547,7 @@ export class TaskLoop {
|
|||||||
maxEpochs = 50,
|
maxEpochs = 50,
|
||||||
verbose = 0
|
verbose = 0
|
||||||
} = this.taskOptions || {};
|
} = this.taskOptions || {};
|
||||||
|
this.aborted = false;
|
||||||
|
|
||||||
for (let i = 0; i < maxEpochs; ++i) {
|
for (let i = 0; i < maxEpochs; ++i) {
|
||||||
|
|
||||||
@ -570,6 +573,12 @@ export class TaskLoop {
|
|||||||
// 发送请求
|
// 发送请求
|
||||||
const doConverationResult = await this.doConversation(chatData, toolcallIndexAdapter);
|
const doConverationResult = await this.doConversation(chatData, toolcallIndexAdapter);
|
||||||
|
|
||||||
|
// 如果在调用过程中出发了 abort,则直接中断
|
||||||
|
if (this.aborted) {
|
||||||
|
this.aborted = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
// 如果存在需要调度的工具
|
// 如果存在需要调度的工具
|
||||||
if (this.streamingToolCalls.value.length > 0) {
|
if (this.streamingToolCalls.value.length > 0) {
|
||||||
|
|
||||||
@ -597,8 +606,19 @@ export class TaskLoop {
|
|||||||
|
|
||||||
// ready to call tools
|
// ready to call tools
|
||||||
toolCall = this.consumeToolCalls(toolCall);
|
toolCall = this.consumeToolCalls(toolCall);
|
||||||
|
|
||||||
|
if (this.aborted) {
|
||||||
|
this.aborted = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
let toolCallResult = await handleToolCalls(toolCall);
|
let toolCallResult = await handleToolCalls(toolCall);
|
||||||
|
|
||||||
|
if (this.aborted) {
|
||||||
|
this.aborted = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
// hook : finish call tools
|
// hook : finish call tools
|
||||||
toolCallResult = this.consumeToolCalleds(toolCallResult);
|
toolCallResult = this.consumeToolCalleds(toolCallResult);
|
||||||
|
|
||||||
@ -656,6 +676,11 @@ export class TaskLoop {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this.aborted) {
|
||||||
|
this.aborted = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
} else if (this.streamingContent.value) {
|
} else if (this.streamingContent.value) {
|
||||||
tabStorage.messages.push({
|
tabStorage.messages.push({
|
||||||
role: 'assistant',
|
role: 'assistant',
|
||||||
|
@ -1,39 +1,28 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="main-panel-container">
|
<div class="main-panel-container">
|
||||||
<div class="tabs-container">
|
<div class="tabs-container">
|
||||||
<el-scrollbar>
|
<el-scrollbar>
|
||||||
<div class="scroll-tabs-container">
|
<div class="scroll-tabs-container">
|
||||||
<span
|
<span class="tab" v-for="(tab, index) of tabs.content" :key="tab.id"
|
||||||
class="tab"
|
:class="{ 'active-tab': tabs.activeIndex === index }" @click="setActiveTab(index)">
|
||||||
v-for="(tab, index) of tabs.content"
|
|
||||||
:key="tab.id"
|
|
||||||
:class="{ 'active-tab': tabs.activeIndex === index }"
|
|
||||||
@click="setActiveTab(index)"
|
|
||||||
>
|
|
||||||
<span>
|
<span>
|
||||||
<span :class="`iconfont ${tab.icon}`"></span>
|
<span :class="`iconfont ${tab.icon}`"></span>
|
||||||
<span class="tab-name">{{ tab.name }}</span>
|
<span class="tab-name">{{ tab.name }}</span>
|
||||||
</span>
|
</span>
|
||||||
<span
|
<span class="iconfont icon-close" @click.stop="closeTab(index)"></span>
|
||||||
class="iconfont icon-close"
|
|
||||||
@click.stop="closeTab(index)"
|
|
||||||
></span>
|
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</el-scrollbar>
|
</el-scrollbar>
|
||||||
|
|
||||||
<span
|
<span class="add-button iconfont icon-add" @click="pageAddNewTab">
|
||||||
class="add-button iconfont icon-add"
|
</span>
|
||||||
@click="pageAddNewTab"
|
</div>
|
||||||
>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
<div class="main-panel">
|
<div class="main-panel">
|
||||||
<router-view />
|
<router-view />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
@ -50,41 +39,41 @@ const route = useRoute();
|
|||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
|
||||||
function pageAddNewTab() {
|
function pageAddNewTab() {
|
||||||
addNewTab();
|
addNewTab();
|
||||||
|
|
||||||
// 如果当前不在 debug 路由,则切换到 debug 路由
|
// 如果当前不在 debug 路由,则切换到 debug 路由
|
||||||
if (route.name !== 'debug') {
|
if (route.name !== 'debug') {
|
||||||
router.push(baseURL + 'debug');
|
router.push(baseURL + 'debug');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function setActiveTab(index: number) {
|
function setActiveTab(index: number) {
|
||||||
if (index >= 0 && index < tabs.content.length) {
|
if (index >= 0 && index < tabs.content.length) {
|
||||||
tabs.activeIndex = index;
|
tabs.activeIndex = index;
|
||||||
// 如果不在 debug 路由,则进入
|
// 如果不在 debug 路由,则进入
|
||||||
if (route.name !== 'debug') {
|
if (route.name !== 'debug') {
|
||||||
router.push(baseURL + 'debug');
|
router.push(baseURL + 'debug');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
.main-panel-container {
|
.main-panel-container {
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
min-width: 800px;
|
min-width: 800px;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
margin-left: 5px;
|
margin-left: 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.main-panel {
|
.main-panel {
|
||||||
background-color: var(--sidebar);
|
background-color: var(--sidebar);
|
||||||
border-radius: 1.2em;
|
border-radius: 1.2em;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: calc(100% - 35px);
|
height: calc(100% - 35px);
|
||||||
}
|
}
|
||||||
|
|
||||||
.scroll-tabs-container {
|
.scroll-tabs-container {
|
||||||
@ -93,13 +82,13 @@ function setActiveTab(index: number) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.tabs-container {
|
.tabs-container {
|
||||||
height: 30px;
|
height: 30px;
|
||||||
width: 90%;
|
width: 90%;
|
||||||
background-color: var(--background);
|
background-color: var(--background);
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
user-select: none;
|
user-select: none;
|
||||||
margin-bottom: 5px;
|
margin-bottom: 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tabs-container .el-scrollbar {
|
.tabs-container .el-scrollbar {
|
||||||
@ -107,42 +96,47 @@ function setActiveTab(index: number) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.tabs-container .tab {
|
.tabs-container .tab {
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
margin-right: 5px;
|
margin-right: 5px;
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
width: 120px;
|
width: 120px;
|
||||||
border-radius: .5em;
|
border-radius: .5em;
|
||||||
background-color: var(--sidebar);
|
background-color: var(--sidebar);
|
||||||
padding: 3px 10px;
|
padding: 3px 10px;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
transition: var(--animation-3s);
|
transition: var(--animation-3s);
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tabs-container .tab > span:first-child {
|
.tabs-container .tab:active {
|
||||||
|
transform: scale(0.95);
|
||||||
|
transition: var(--animation-3s);
|
||||||
|
}
|
||||||
|
|
||||||
|
.tabs-container .tab>span:first-child {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tabs-container .tab .tab-name {
|
.tabs-container .tab .tab-name {
|
||||||
max-width: 70px;
|
max-width: 70px;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tabs-container .tab:hover {
|
.tabs-container .tab:hover {
|
||||||
background-color: var(--input-active-background);
|
background-color: var(--input-active-background);
|
||||||
}
|
}
|
||||||
|
|
||||||
.tabs-container .tab.active-tab {
|
.tabs-container .tab.active-tab {
|
||||||
background-color: var(--main-color);
|
background-color: var(--main-color);
|
||||||
color: white;
|
color: white;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tabs-container .tab .iconfont {
|
.tabs-container .tab .iconfont {
|
||||||
margin-right: 10px;
|
margin-right: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tabs-container .icon-close {
|
.tabs-container .icon-close {
|
||||||
@ -160,30 +154,30 @@ function setActiveTab(index: number) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.tabs-container .add-button {
|
.tabs-container .add-button {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
font-size: 15px;
|
font-size: 15px;
|
||||||
margin-left: 5px;
|
margin-left: 5px;
|
||||||
border-radius: .5em;
|
border-radius: .5em;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
transition: var(--animation-3s);
|
transition: var(--animation-3s);
|
||||||
}
|
}
|
||||||
|
|
||||||
.tabs-container .add-button:hover {
|
.tabs-container .add-button:hover {
|
||||||
color: var(--main-color);
|
color: var(--main-color);
|
||||||
background-color: var(--sidebar);
|
background-color: var(--sidebar);
|
||||||
transition: var(--animation-3s);
|
transition: var(--animation-3s);
|
||||||
}
|
}
|
||||||
|
|
||||||
.close-icon {
|
.close-icon {
|
||||||
margin-left: 8px;
|
margin-left: 8px;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
padding: 2px;
|
padding: 2px;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.close-icon:hover {
|
.close-icon:hover {
|
||||||
background-color: rgba(255, 255, 255, 0.2);
|
background-color: rgba(255, 255, 255, 0.2);
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
@ -4,7 +4,7 @@
|
|||||||
<div class="left">
|
<div class="left">
|
||||||
<h2>
|
<h2>
|
||||||
<span class="iconfont icon-chat"></span>
|
<span class="iconfont icon-chat"></span>
|
||||||
提示词模块
|
{{ t('prompt-module') }}
|
||||||
</h2>
|
</h2>
|
||||||
|
|
||||||
<PromptTemplates :tab-id="props.tabId"></PromptTemplates>
|
<PromptTemplates :tab-id="props.tabId"></PromptTemplates>
|
||||||
@ -24,6 +24,9 @@ import { defineProps } from 'vue';
|
|||||||
import PromptTemplates from './prompt-templates.vue';
|
import PromptTemplates from './prompt-templates.vue';
|
||||||
import PromptReader from './prompt-reader.vue';
|
import PromptReader from './prompt-reader.vue';
|
||||||
import PromptLogger from './prompt-logger.vue';
|
import PromptLogger from './prompt-logger.vue';
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
|
|
||||||
|
const { t } = useI18n();
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
tabId: {
|
tabId: {
|
||||||
|
@ -8,23 +8,27 @@
|
|||||||
<span>prompts/list</span>
|
<span>prompts/list</span>
|
||||||
<span @click.stop="reloadPrompts(client, { first: false })" class="iconfont icon-restart"></span>
|
<span @click.stop="reloadPrompts(client, { first: false })" class="iconfont icon-restart"></span>
|
||||||
</h3>
|
</h3>
|
||||||
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<!-- body -->
|
<!-- body -->
|
||||||
|
|
||||||
<div class="prompt-template-container-scrollbar">
|
<div class="prompt-template-container-scrollbar">
|
||||||
<el-scrollbar height="500px">
|
<el-scrollbar height="fit-content" v-if="(client.promptTemplates?.size || 0) > 0">
|
||||||
<div class="prompt-template-container">
|
<div class="prompt-template-container">
|
||||||
<div class="item"
|
<div class="item"
|
||||||
:class="{ 'active': props.tabId >= 0 && tabStorage.currentPromptName === template.name }"
|
:class="{ 'active': props.tabId >= 0 && tabStorage.currentPromptName === template.name }"
|
||||||
v-for="template of client.promptTemplates?.values()" :key="template.name"
|
v-for="template of client.promptTemplates?.values()" :key="template.name"
|
||||||
@click="handleClick(template)">
|
@click="handleClick(template)">
|
||||||
<span>{{ template.name }}</span>
|
<span class="prompt-title">{{ template.name }}</span>
|
||||||
<span>{{ template.description || '' }}</span>
|
<span class="prompt-description">{{ template.description || '' }}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</el-scrollbar>
|
</el-scrollbar>
|
||||||
|
<div v-else style="padding: 10px;">
|
||||||
|
<div class="empty-description">
|
||||||
|
<span class="iconfont icon-empty" style="font-size: 22px; opacity: 0.4; margin-right: 6px;"></span>
|
||||||
|
<span style="opacity: 0.6;">No prompts found.</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</el-collapse-item>
|
</el-collapse-item>
|
||||||
</el-collapse>
|
</el-collapse>
|
||||||
@ -126,8 +130,8 @@ onMounted(async () => {
|
|||||||
user-select: none;
|
user-select: none;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
flex-direction: column;
|
||||||
justify-content: space-between;
|
align-items: flex-start;
|
||||||
transition: var(--animation-3s);
|
transition: var(--animation-3s);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -136,24 +140,40 @@ onMounted(async () => {
|
|||||||
transition: var(--animation-3s);
|
transition: var(--animation-3s);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.prompt-template-container>.item:active {
|
||||||
|
transform: scale(0.95);
|
||||||
|
transition: var(--animation-3s);
|
||||||
|
}
|
||||||
|
|
||||||
.prompt-template-container>.item.active {
|
.prompt-template-container>.item.active {
|
||||||
background-color: var(--main-light-color);
|
background-color: var(--main-light-color);
|
||||||
transition: var(--animation-3s);
|
transition: var(--animation-3s);
|
||||||
}
|
}
|
||||||
|
|
||||||
.prompt-template-container>.item>span:first-child {
|
.prompt-title {
|
||||||
max-width: 200px;
|
font-weight: bold;
|
||||||
|
max-width: 250px;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
.prompt-template-container>.item>span:last-child {
|
.prompt-description {
|
||||||
opacity: 0.6;
|
opacity: 0.6;
|
||||||
font-size: 12.5px;
|
font-size: 12.5px;
|
||||||
max-width: 200px;
|
max-width: 250px;
|
||||||
overflow: hidden;
|
overflow: visible;
|
||||||
text-overflow: ellipsis;
|
text-overflow: unset;
|
||||||
white-space: nowrap;
|
white-space: normal;
|
||||||
|
word-break: break-all;
|
||||||
|
}
|
||||||
|
|
||||||
|
.empty-description {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
color: var(--el-text-color-placeholder, #bbb);
|
||||||
|
font-size: 15px;
|
||||||
|
min-height: 40px;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
@ -115,7 +115,7 @@ const formRules = computed<FormRules>(() => {
|
|||||||
currentResource.value?.params.forEach(param => {
|
currentResource.value?.params.forEach(param => {
|
||||||
rules[param] = [
|
rules[param] = [
|
||||||
{
|
{
|
||||||
message: `${param} 是必填字段`,
|
message: `${param} ` + t('is-required'),
|
||||||
trigger: 'blur'
|
trigger: 'blur'
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
@ -12,19 +12,22 @@
|
|||||||
|
|
||||||
<!-- body -->
|
<!-- body -->
|
||||||
<div class="resource-template-container-scrollbar">
|
<div class="resource-template-container-scrollbar">
|
||||||
<el-scrollbar height="500px" v-if="(client.resourceTemplates?.size || 0) > 0">
|
<el-scrollbar height="fit-content" v-if="(client.resourceTemplates?.size || 0) > 0">
|
||||||
<div class="resource-template-container">
|
<div class="resource-template-container">
|
||||||
<div class="item"
|
<div class="item"
|
||||||
:class="{ 'active': props.tabId >= 0 && tabStorage.currentType === 'template' && tabStorage.currentResourceName === template.name }"
|
:class="{ 'active': props.tabId >= 0 && tabStorage.currentType === 'template' && tabStorage.currentResourceName === template.name }"
|
||||||
v-for="template of client.resourceTemplates?.values()" :key="template.name"
|
v-for="template of client.resourceTemplates?.values()" :key="template.name"
|
||||||
@click="handleClick(template)">
|
@click="handleClick(template)">
|
||||||
<span>{{ template.name }}</span>
|
<span class="resource-title">{{ template.name }}</span>
|
||||||
<span>{{ template.description || '' }}</span>
|
<span class="resource-description">{{ template.description || '' }}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</el-scrollbar>
|
</el-scrollbar>
|
||||||
<div v-else style="padding: 10px;">
|
<div v-else style="padding: 10px;">
|
||||||
empty
|
<div class="empty-description">
|
||||||
|
<span class="iconfont icon-empty" style="font-size: 22px; opacity: 0.4; margin-right: 6px;"></span>
|
||||||
|
<span style="opacity: 0.6;">No resource templates found.</span>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</el-collapse-item>
|
</el-collapse-item>
|
||||||
@ -137,41 +140,57 @@ h3.resource-template .iconfont.icon-restart:hover {
|
|||||||
width: 175px;
|
width: 175px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.resource-template-container>.item {
|
.resource-template-container > .item {
|
||||||
margin: 3px;
|
margin: 3px;
|
||||||
padding: 5px 10px;
|
padding: 5px 10px;
|
||||||
border-radius: .3em;
|
border-radius: .3em;
|
||||||
user-select: none;
|
user-select: none;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
flex-direction: column;
|
||||||
justify-content: space-between;
|
align-items: flex-start;
|
||||||
transition: var(--animation-3s);
|
transition: var(--animation-3s);
|
||||||
}
|
}
|
||||||
|
|
||||||
.resource-template-container>.item:hover {
|
.resource-template-container > .item:hover {
|
||||||
background-color: var(--main-light-color);
|
background-color: var(--main-light-color);
|
||||||
transition: var(--animation-3s);
|
transition: var(--animation-3s);
|
||||||
}
|
}
|
||||||
|
|
||||||
.resource-template-container>.item.active {
|
.resource-template-container > .item:active {
|
||||||
background-color: var(--main-light-color);
|
transform: scale(0.95);
|
||||||
transition: var(--animation-3s);
|
transition: var(--animation-3s);
|
||||||
}
|
}
|
||||||
|
|
||||||
.resource-template-container>.item>span:first-child {
|
.resource-template-container > .item.active {
|
||||||
max-width: 200px;
|
background-color: var(--main-light-color);
|
||||||
overflow: hidden;
|
transition: var(--animation-3s);
|
||||||
text-overflow: ellipsis;
|
|
||||||
white-space: nowrap;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.resource-template-container>.item>span:last-child {
|
.resource-title {
|
||||||
opacity: 0.6;
|
font-weight: bold;
|
||||||
font-size: 12.5px;
|
max-width: 250px;
|
||||||
max-width: 200px;
|
overflow: hidden;
|
||||||
overflow: hidden;
|
text-overflow: ellipsis;
|
||||||
text-overflow: ellipsis;
|
white-space: nowrap;
|
||||||
white-space: nowrap;
|
}
|
||||||
|
|
||||||
|
.resource-description {
|
||||||
|
opacity: 0.6;
|
||||||
|
font-size: 12.5px;
|
||||||
|
max-width: 250px;
|
||||||
|
overflow: visible;
|
||||||
|
text-overflow: unset;
|
||||||
|
white-space: normal;
|
||||||
|
word-break: break-all;
|
||||||
|
}
|
||||||
|
|
||||||
|
.empty-description {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
color: var(--el-text-color-placeholder, #bbb);
|
||||||
|
font-size: 15px;
|
||||||
|
min-height: 40px;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
@ -12,17 +12,23 @@
|
|||||||
|
|
||||||
<!-- body -->
|
<!-- body -->
|
||||||
<div class="resource-template-container-scrollbar">
|
<div class="resource-template-container-scrollbar">
|
||||||
<el-scrollbar height="500px">
|
<el-scrollbar height="fit-content" v-if="(client.resources?.size || 0) > 0">
|
||||||
<div class="resource-template-container">
|
<div class="resource-template-container">
|
||||||
<div class="item"
|
<div class="item"
|
||||||
:class="{ 'active': props.tabId >= 0 && tabStorage.currentType === 'resource' && tabStorage.currentResourceName === resource.name }"
|
:class="{ 'active': props.tabId >= 0 && tabStorage.currentType === 'resource' && tabStorage.currentResourceName === resource.name }"
|
||||||
v-for="resource of client.resources?.values()" :key="resource.uri"
|
v-for="resource of client.resources?.values()" :key="resource.uri"
|
||||||
@click="handleClick(resource)">
|
@click="handleClick(resource)">
|
||||||
<span>{{ resource.name }}</span>
|
<span class="resource-title">{{ resource.name }}</span>
|
||||||
<span>{{ resource.mimeType }}</span>
|
<span class="resource-description">{{ resource.mimeType }}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</el-scrollbar>
|
</el-scrollbar>
|
||||||
|
<div v-else style="padding: 10px;">
|
||||||
|
<div class="empty-description">
|
||||||
|
<span class="iconfont icon-empty" style="font-size: 22px; opacity: 0.4; margin-right: 6px;"></span>
|
||||||
|
<span style="opacity: 0.6;">No resources found.</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</el-collapse-item>
|
</el-collapse-item>
|
||||||
</el-collapse>
|
</el-collapse>
|
||||||
@ -139,41 +145,58 @@ h3.resource-template .iconfont.icon-restart:hover {
|
|||||||
width: 175px;
|
width: 175px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.resource-template-container>.item {
|
.resource-template-container > .item {
|
||||||
margin: 3px;
|
margin: 3px;
|
||||||
padding: 5px 10px;
|
padding: 5px 10px;
|
||||||
border-radius: .3em;
|
border-radius: .3em;
|
||||||
user-select: none;
|
user-select: none;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
flex-direction: column;
|
||||||
justify-content: space-between;
|
align-items: flex-start;
|
||||||
transition: var(--animation-3s);
|
transition: var(--animation-3s);
|
||||||
}
|
}
|
||||||
|
|
||||||
.resource-template-container>.item:hover {
|
.resource-template-container > .item:hover {
|
||||||
background-color: var(--main-light-color);
|
background-color: var(--main-light-color);
|
||||||
transition: var(--animation-3s);
|
transition: var(--animation-3s);
|
||||||
}
|
}
|
||||||
|
|
||||||
.resource-template-container>.item.active {
|
.resource-template-container > .item.active {
|
||||||
background-color: var(--main-light-color);
|
background-color: var(--main-light-color);
|
||||||
transition: var(--animation-3s);
|
transition: var(--animation-3s);
|
||||||
}
|
}
|
||||||
|
|
||||||
.resource-template-container>.item>span:first-child {
|
.resource-template-container > .item:active {
|
||||||
max-width: 200px;
|
transform: scale(0.95);
|
||||||
|
transition: var(--animation-3s);
|
||||||
|
}
|
||||||
|
|
||||||
|
.resource-title {
|
||||||
|
font-weight: bold;
|
||||||
|
max-width: 250px;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
.resource-template-container>.item>span:last-child {
|
.resource-description {
|
||||||
opacity: 0.6;
|
opacity: 0.6;
|
||||||
font-size: 12.5px;
|
font-size: 12.5px;
|
||||||
max-width: 200px;
|
max-width: 250px;
|
||||||
overflow: hidden;
|
/* Remove ellipsis and allow full text wrap */
|
||||||
text-overflow: ellipsis;
|
overflow: visible;
|
||||||
white-space: nowrap;
|
text-overflow: unset;
|
||||||
|
white-space: normal;
|
||||||
|
word-break: break-all;
|
||||||
|
}
|
||||||
|
|
||||||
|
.empty-description {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
color: var(--el-text-color-placeholder, #bbb);
|
||||||
|
font-size: 15px;
|
||||||
|
min-height: 40px;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
@ -0,0 +1,160 @@
|
|||||||
|
<template>
|
||||||
|
<div class="diagram-item-record" v-if="props.dataView && props.dataView.tool">
|
||||||
|
<div class="item-header">
|
||||||
|
<span class="item-title">{{ props.dataView.tool.name }}</span>
|
||||||
|
<span class="item-status" :class="props.dataView.status">{{ props.dataView.status }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="item-desc">{{ props.dataView.tool.description }}</div>
|
||||||
|
|
||||||
|
<div v-if="props.dataView.function !== undefined" class="item-result">
|
||||||
|
<div class="item-label">Function</div>
|
||||||
|
<div class="item-json">{{ props.dataView.function.name }}</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-if="props.dataView.function !== undefined" class="item-result">
|
||||||
|
<span class="item-label">Arguments</span>
|
||||||
|
<json-render :json="props.dataView.function.arguments" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-if="props.dataView.result !== undefined" class="item-result">
|
||||||
|
<span class="item-label">Result</span>
|
||||||
|
<template v-if="Array.isArray(props.dataView.result)">
|
||||||
|
<div v-for="(item, idx) in props.dataView.result" :key="idx" class="result-block"
|
||||||
|
:class="[props.dataView.status]">
|
||||||
|
<pre class="item-json"
|
||||||
|
v-if="typeof item === 'object' && item.text !== undefined">{{ item.text }}</pre>
|
||||||
|
<pre class="item-json" v-else>{{ formatJson(item) }}</pre>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<pre class="item-json"
|
||||||
|
v-else-if="typeof props.dataView.result === 'string'">{{ props.dataView.result }}</pre>
|
||||||
|
<pre class="item-json" v-else>{{ formatJson(props.dataView.result) }}</pre>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div v-else class="diagram-item-record">
|
||||||
|
<div class="item-header">
|
||||||
|
<span class="item-title">No Tool Selected</span>
|
||||||
|
</div>
|
||||||
|
<div class="item-desc">Please select a tool to view its details.</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import type { PropType } from 'vue';
|
||||||
|
import type { NodeDataView } from './diagram';
|
||||||
|
|
||||||
|
import JsonRender from '@/components/json-render/index.vue';
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
dataView: {
|
||||||
|
type: Object as PropType<NodeDataView | undefined | null>,
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
function formatJson(obj: any) {
|
||||||
|
try {
|
||||||
|
return JSON.stringify(obj, null, 2)
|
||||||
|
} catch {
|
||||||
|
return String(obj)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.diagram-item-record {
|
||||||
|
padding: 14px 18px;
|
||||||
|
border-radius: 8px;
|
||||||
|
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.04);
|
||||||
|
font-size: 15px;
|
||||||
|
max-width: 1000px;
|
||||||
|
word-break: break-all;
|
||||||
|
}
|
||||||
|
|
||||||
|
.item-header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
margin-bottom: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.item-title {
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: 17px;
|
||||||
|
color: var(--main-color, #409EFF);
|
||||||
|
}
|
||||||
|
|
||||||
|
.item-status {
|
||||||
|
font-size: 13px;
|
||||||
|
padding: 2px 10px;
|
||||||
|
border-radius: 12px;
|
||||||
|
margin-left: 8px;
|
||||||
|
text-transform: capitalize;
|
||||||
|
}
|
||||||
|
|
||||||
|
.item-status.running {
|
||||||
|
color: #2196f3;
|
||||||
|
}
|
||||||
|
|
||||||
|
.item-status.success {
|
||||||
|
color: #43a047;
|
||||||
|
}
|
||||||
|
|
||||||
|
.item-status.error {
|
||||||
|
color: #e53935;
|
||||||
|
}
|
||||||
|
|
||||||
|
.item-status.waiting {
|
||||||
|
color: #aaa;
|
||||||
|
}
|
||||||
|
|
||||||
|
.item-status.default {
|
||||||
|
color: #888;
|
||||||
|
}
|
||||||
|
|
||||||
|
.item-desc {
|
||||||
|
margin-bottom: 8px;
|
||||||
|
opacity: 0.8;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.item-label {
|
||||||
|
font-weight: 500;
|
||||||
|
margin-right: 4px;
|
||||||
|
color: var(--main-color, #409EFF);
|
||||||
|
}
|
||||||
|
|
||||||
|
.item-json {
|
||||||
|
border-radius: 4px;
|
||||||
|
padding: 6px 10px;
|
||||||
|
font-size: 13px;
|
||||||
|
font-family: var(--code-font-family, monospace);
|
||||||
|
margin: 2px 0 8px 0;
|
||||||
|
white-space: pre-wrap;
|
||||||
|
word-break: break-all;
|
||||||
|
overflow-x: auto;
|
||||||
|
max-width: 100%;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.item-result {
|
||||||
|
margin-top: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.result-block {
|
||||||
|
margin-bottom: 6px;
|
||||||
|
border-radius: .5em;
|
||||||
|
margin: 5px 0;
|
||||||
|
overflow-x: auto;
|
||||||
|
max-width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.result-block.error {
|
||||||
|
background-color: rgba(245, 108, 108, 0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
.result-block.success {
|
||||||
|
background-color: rgba(67, 160, 71, 0.5);
|
||||||
|
}
|
||||||
|
</style>
|
248
renderer/src/components/main-panel/tool/auto-detector/diagram.ts
Normal file
248
renderer/src/components/main-panel/tool/auto-detector/diagram.ts
Normal file
@ -0,0 +1,248 @@
|
|||||||
|
import type { ElkNode } from 'elkjs/lib/elk-api';
|
||||||
|
import { MessageState, TaskLoop } from '../../chat/core/task-loop';
|
||||||
|
import type { Reactive } from 'vue';
|
||||||
|
import type { ChatStorage } from '../../chat/chat-box/chat';
|
||||||
|
import { ElMessage } from 'element-plus';
|
||||||
|
import type { ToolItem } from '@/hook/type';
|
||||||
|
|
||||||
|
import I18n from '@/i18n';
|
||||||
|
import type { ChatCompletionChunk } from 'openai/resources/index.mjs';
|
||||||
|
|
||||||
|
const { t } = I18n.global;
|
||||||
|
|
||||||
|
export interface Edge {
|
||||||
|
id: string;
|
||||||
|
sources: string[];
|
||||||
|
targets: string[];
|
||||||
|
sections?: any; // { startPoint: { x, y }, endPoint: { x,
|
||||||
|
}
|
||||||
|
|
||||||
|
export type Node = ElkNode & {
|
||||||
|
[key: string]: any;
|
||||||
|
width: number;
|
||||||
|
height: number;
|
||||||
|
id: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export interface DiagramState {
|
||||||
|
nodes: Node[];
|
||||||
|
edges: Edge[];
|
||||||
|
selectedNodeId: string | null;
|
||||||
|
dataView: Map<string, NodeDataView>;
|
||||||
|
[key: string]: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface CanConnectResult {
|
||||||
|
canConnect: boolean;
|
||||||
|
reason?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface NodeDataView {
|
||||||
|
tool: ToolItem;
|
||||||
|
status: 'default' | 'running' | 'waiting' | 'success' | 'error';
|
||||||
|
function?: ChatCompletionChunk.Choice.Delta.ToolCall.Function;
|
||||||
|
result?: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface DiagramContext {
|
||||||
|
reset: () => void,
|
||||||
|
render: () => void,
|
||||||
|
state?: DiagramState,
|
||||||
|
setCaption: (value: string) => void
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 判断两个节点是否可以连接
|
||||||
|
*/
|
||||||
|
export function invalidConnectionDetector(state: DiagramState, d: Node): CanConnectResult {
|
||||||
|
const from = state.selectedNodeId;
|
||||||
|
const to = d.id;
|
||||||
|
|
||||||
|
if (!from) {
|
||||||
|
return { canConnect: false, reason: t('not-select-begin-node') };
|
||||||
|
}
|
||||||
|
|
||||||
|
if (from === to) {
|
||||||
|
return { canConnect: false, reason: '' };
|
||||||
|
}
|
||||||
|
|
||||||
|
// 建立邻接表
|
||||||
|
const adjacencyList: Record<string, Set<string>> = {};
|
||||||
|
state.edges.forEach(edge => {
|
||||||
|
const src = edge.sources[0];
|
||||||
|
const tgt = edge.targets[0];
|
||||||
|
if (!adjacencyList[src]) {
|
||||||
|
adjacencyList[src] = new Set();
|
||||||
|
}
|
||||||
|
adjacencyList[src].add(tgt);
|
||||||
|
});
|
||||||
|
|
||||||
|
// DFS 检测是否存在
|
||||||
|
function hasPath(current: string, target: string, visited: Set<string>): boolean {
|
||||||
|
if (current === target) return true;
|
||||||
|
visited.add(current);
|
||||||
|
const neighbors = adjacencyList[current] || new Set();
|
||||||
|
for (const neighbor of neighbors) {
|
||||||
|
if (!visited.has(neighbor)) {
|
||||||
|
if (hasPath(neighbor, target, visited)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hasPath(to, from, new Set())) {
|
||||||
|
return { canConnect: false, reason: t('can-make-loop') };
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hasPath(from, to, new Set())) {
|
||||||
|
return { canConnect: false, reason: t('this-is-repeat-connection') };
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
canConnect: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 拓扑排序,输出每一层可以并行调度的节点id数组
|
||||||
|
* @returns string[][] 每一层可以并行调度的节点id数组
|
||||||
|
*/
|
||||||
|
export function topoSortParallel(state: DiagramState): string[][] {
|
||||||
|
// 统计每个节点的入度
|
||||||
|
const inDegree: Record<string, number> = {};
|
||||||
|
state.nodes.forEach(node => {
|
||||||
|
inDegree[node.id] = 0;
|
||||||
|
});
|
||||||
|
state.edges.forEach(edge => {
|
||||||
|
const tgt = edge.targets[0];
|
||||||
|
if (tgt in inDegree) {
|
||||||
|
inDegree[tgt]++;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 初始化队列,收集所有入度为0的节点
|
||||||
|
const result: string[][] = [];
|
||||||
|
let queue: string[] = Object.keys(inDegree).filter(id => inDegree[id] === 0);
|
||||||
|
|
||||||
|
const visited = new Set<string>();
|
||||||
|
|
||||||
|
while (queue.length > 0) {
|
||||||
|
// 当前层可以并行的节点
|
||||||
|
result.push([...queue]);
|
||||||
|
const nextQueue: string[] = [];
|
||||||
|
for (const id of queue) {
|
||||||
|
visited.add(id);
|
||||||
|
// 遍历所有以当前节点为源的边,减少目标节点的入度
|
||||||
|
state.edges.forEach(edge => {
|
||||||
|
if (edge.sources[0] === id) {
|
||||||
|
const tgt = edge.targets[0];
|
||||||
|
inDegree[tgt]--;
|
||||||
|
// 如果目标节点入度为0且未访问过,加入下一层
|
||||||
|
if (inDegree[tgt] === 0 && !visited.has(tgt)) {
|
||||||
|
nextQueue.push(tgt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
queue = nextQueue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查是否有环
|
||||||
|
if (visited.size !== state.nodes.length) {
|
||||||
|
throw new Error('图中存在环,无法进行拓扑排序');
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function makeNodeTest(
|
||||||
|
dataView: Reactive<NodeDataView>,
|
||||||
|
enableXmlWrapper: boolean,
|
||||||
|
prompt: string | null = null,
|
||||||
|
context: DiagramContext
|
||||||
|
) {
|
||||||
|
if (!dataView.tool.inputSchema) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
dataView.status = 'running';
|
||||||
|
context.render();
|
||||||
|
|
||||||
|
try {
|
||||||
|
const loop = new TaskLoop({ maxEpochs: 1 });
|
||||||
|
const usePrompt = (prompt || 'please call the tool {tool} to make some test').replace('{tool}', dataView.tool.name);
|
||||||
|
const chatStorage = {
|
||||||
|
messages: [],
|
||||||
|
settings: {
|
||||||
|
temperature: 0.6,
|
||||||
|
systemPrompt: '',
|
||||||
|
enableTools: [{
|
||||||
|
name: dataView.tool.name,
|
||||||
|
description: dataView.tool.description,
|
||||||
|
inputSchema: dataView.tool.inputSchema,
|
||||||
|
enabled: true
|
||||||
|
}],
|
||||||
|
enableWebSearch: false,
|
||||||
|
contextLength: 5,
|
||||||
|
enableXmlWrapper,
|
||||||
|
parallelToolCalls: false
|
||||||
|
}
|
||||||
|
} as ChatStorage;
|
||||||
|
|
||||||
|
loop.setMaxEpochs(1);
|
||||||
|
|
||||||
|
let aiMockJson: any = undefined;
|
||||||
|
|
||||||
|
loop.registerOnToolCall(toolCall => {
|
||||||
|
dataView.function = toolCall.function;
|
||||||
|
|
||||||
|
if (toolCall.function?.name === dataView.tool?.name) {
|
||||||
|
try {
|
||||||
|
const toolArgs = JSON.parse(toolCall.function?.arguments || '{}');
|
||||||
|
aiMockJson = toolArgs;
|
||||||
|
} catch (e) {
|
||||||
|
// ElMessage.error('AI 生成的 JSON 解析错误');
|
||||||
|
dataView.status = 'error';
|
||||||
|
dataView.result = t('ai-gen-error-json');
|
||||||
|
context.render();
|
||||||
|
loop.abort();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// ElMessage.error('AI 调用了未知的工具');
|
||||||
|
dataView.status = 'error';
|
||||||
|
dataView.result = t('ai-invoke-unknown-tool') + ' ' + toolCall.function?.name;
|
||||||
|
context.render();
|
||||||
|
loop.abort();
|
||||||
|
}
|
||||||
|
return toolCall;
|
||||||
|
});
|
||||||
|
|
||||||
|
loop.registerOnToolCalled(toolCalled => {
|
||||||
|
if (toolCalled.state === MessageState.Success) {
|
||||||
|
dataView.status = 'success';
|
||||||
|
dataView.result = toolCalled.content;
|
||||||
|
} else {
|
||||||
|
dataView.status = 'error';
|
||||||
|
dataView.result = toolCalled.content;
|
||||||
|
}
|
||||||
|
loop.abort();
|
||||||
|
return toolCalled;
|
||||||
|
})
|
||||||
|
|
||||||
|
loop.registerOnError(error => {
|
||||||
|
dataView.status = 'error';
|
||||||
|
dataView.result = error;
|
||||||
|
context.render();
|
||||||
|
});
|
||||||
|
|
||||||
|
await loop.start(chatStorage, usePrompt);
|
||||||
|
|
||||||
|
} finally {
|
||||||
|
if (dataView.status === 'running') {
|
||||||
|
dataView.status = 'success';
|
||||||
|
context.render();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
@ -0,0 +1,654 @@
|
|||||||
|
<template>
|
||||||
|
<div style="display: flex; align-items: center; gap: 16px;">
|
||||||
|
<div ref="svgContainer" class="diagram-container"></div>
|
||||||
|
|
||||||
|
<template v-for="(node, index) in state.nodes" :key="node.id + '-popup'">
|
||||||
|
<transition name="collapse-from-top" mode="out-in">
|
||||||
|
<div
|
||||||
|
v-show="state.hoverNodeId === node.id && state.dataView.get(node.id)?.status !== 'waiting'"
|
||||||
|
@mouseenter="setHoverItem(node.id)"
|
||||||
|
@mouseleave="clearHoverItem()"
|
||||||
|
:style="getNodePopupStyle(node)"
|
||||||
|
class="node-popup"
|
||||||
|
>
|
||||||
|
<el-scrollbar height="100%" width="100%">
|
||||||
|
<DiagramItemRecord :data-view="state.dataView.get(node.id)"/>
|
||||||
|
</el-scrollbar>
|
||||||
|
</div>
|
||||||
|
</transition>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { ref, onMounted, nextTick, reactive, inject } from 'vue';
|
||||||
|
import * as d3 from 'd3';
|
||||||
|
import ELK from 'elkjs/lib/elk.bundled.js';
|
||||||
|
import { mcpClientAdapter } from '@/views/connect/core';
|
||||||
|
import { invalidConnectionDetector, type Edge, type Node, type NodeDataView } from './diagram';
|
||||||
|
import { ElMessage } from 'element-plus';
|
||||||
|
|
||||||
|
|
||||||
|
import DiagramItemRecord from './diagram-item-record.vue';
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
|
import type { ToolStorage } from '../tools';
|
||||||
|
import { tabs } from '../../panel';
|
||||||
|
|
||||||
|
const { t } = useI18n();
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
tabId: {
|
||||||
|
type: Number,
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const svgContainer = ref<HTMLDivElement | null>(null);
|
||||||
|
let prevNodes: any[] = [];
|
||||||
|
let prevEdges: any[] = [];
|
||||||
|
|
||||||
|
const state = reactive({
|
||||||
|
nodes: [] as Node[],
|
||||||
|
edges: [] as Edge[],
|
||||||
|
selectedNodeId: null as string | null,
|
||||||
|
draggingNodeId: null as string | null,
|
||||||
|
hoverNodeId: null as string | null,
|
||||||
|
offset: { x: 0, y: 0 },
|
||||||
|
dataView: new Map<string, NodeDataView>
|
||||||
|
});
|
||||||
|
|
||||||
|
const tab = tabs.content[props.tabId];
|
||||||
|
const tabStorage = tab.storage as ToolStorage;
|
||||||
|
const autoDetectDiagram = tabStorage.autoDetectDiagram;
|
||||||
|
|
||||||
|
if (autoDetectDiagram) {
|
||||||
|
// 将 tabStorage.autoDetectDiagram 中的 dataView 保存到 state 中
|
||||||
|
autoDetectDiagram.views?.forEach(item => {
|
||||||
|
state.dataView.set(item.tool.name, {
|
||||||
|
tool: item.tool,
|
||||||
|
status: item.status || 'waiting',
|
||||||
|
result: item.result || null
|
||||||
|
});
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
tabStorage.autoDetectDiagram = {
|
||||||
|
edges: [],
|
||||||
|
views: []
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(tabStorage.autoDetectDiagram!.views);
|
||||||
|
console.log(state.dataView);
|
||||||
|
|
||||||
|
|
||||||
|
let cancelHoverHandler: NodeJS.Timeout | undefined = undefined;
|
||||||
|
|
||||||
|
const setHoverItem = (id: string) => {
|
||||||
|
if (cancelHoverHandler) {
|
||||||
|
clearTimeout(cancelHoverHandler);
|
||||||
|
}
|
||||||
|
state.hoverNodeId = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
const clearHoverItem = () => {
|
||||||
|
cancelHoverHandler = setTimeout(() => {
|
||||||
|
if (cancelHoverHandler) {
|
||||||
|
clearTimeout(cancelHoverHandler);
|
||||||
|
}
|
||||||
|
if (state.hoverNodeId) {
|
||||||
|
state.hoverNodeId = null;
|
||||||
|
}
|
||||||
|
}, 300);
|
||||||
|
};
|
||||||
|
|
||||||
|
const getAllTools = async () => {
|
||||||
|
const items = [];
|
||||||
|
for (const client of mcpClientAdapter.clients) {
|
||||||
|
const clientTools = await client.getTools();
|
||||||
|
items.push(...clientTools.values());
|
||||||
|
}
|
||||||
|
return items;
|
||||||
|
};
|
||||||
|
|
||||||
|
const recomputeLayout = async () => {
|
||||||
|
const elk = new ELK();
|
||||||
|
const elkGraph = {
|
||||||
|
id: 'root',
|
||||||
|
layoutOptions: {
|
||||||
|
'elk.direction': 'DOWN',
|
||||||
|
'elk.spacing.nodeNode': '40',
|
||||||
|
'elk.layered.spacing.nodeNodeBetweenLayers': '40'
|
||||||
|
},
|
||||||
|
children: state.nodes,
|
||||||
|
edges: state.edges
|
||||||
|
};
|
||||||
|
const layout = await elk.layout(elkGraph) as unknown as Node;
|
||||||
|
|
||||||
|
state.nodes.forEach((n, i) => {
|
||||||
|
const ln = layout.children?.find(c => c.id === n.id);
|
||||||
|
if (ln) {
|
||||||
|
n.x = ln.x;
|
||||||
|
n.y = ln.y;
|
||||||
|
n.width = ln.width || 200; // 默认宽度
|
||||||
|
n.height = ln.height || 64; // 默认高度
|
||||||
|
}
|
||||||
|
});
|
||||||
|
state.edges = layout.edges || [];
|
||||||
|
|
||||||
|
// 保存拓扑信息到 tabStorage
|
||||||
|
tabStorage.autoDetectDiagram!.edges = state.edges.map(edge => ({
|
||||||
|
id: edge.id,
|
||||||
|
sources: edge.sources || [],
|
||||||
|
targets: edge.targets || []
|
||||||
|
}));
|
||||||
|
|
||||||
|
return layout;
|
||||||
|
};
|
||||||
|
|
||||||
|
const drawDiagram = async () => {
|
||||||
|
const tools = await getAllTools();
|
||||||
|
|
||||||
|
// 默认按照链表进行串联
|
||||||
|
const nodes = [] as Node[];
|
||||||
|
const edges = [] as Edge[];
|
||||||
|
|
||||||
|
// 如果保存了 edges 信息,则需要进行同步
|
||||||
|
const reservedEdges = autoDetectDiagram?.edges;
|
||||||
|
if (reservedEdges) {
|
||||||
|
for (const edge of reservedEdges) {
|
||||||
|
if (edge.sources && edge.targets && edge.sources.length > 0 && edge.targets.length > 0) {
|
||||||
|
edges.push({
|
||||||
|
id: edge.id,
|
||||||
|
sources: edge.sources || [],
|
||||||
|
targets: edge.targets || [],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (let i = 0; i < tools.length - 1; ++i) {
|
||||||
|
const prev = tools[i];
|
||||||
|
const next = tools[i + 1];
|
||||||
|
edges.push({
|
||||||
|
id: prev.name + '-' + next.name,
|
||||||
|
sources: [prev.name],
|
||||||
|
targets: [next.name]
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const tool of tools) {
|
||||||
|
nodes.push({
|
||||||
|
id: tool.name,
|
||||||
|
width: 200,
|
||||||
|
height: 64, // 增加高度
|
||||||
|
labels: [{ text: tool.name || 'Tool' }]
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!state.dataView.has(tool.name)) {
|
||||||
|
// 如果 dataView 中没有该工具,则初始化
|
||||||
|
state.dataView.set(tool.name, {
|
||||||
|
tool,
|
||||||
|
status: 'waiting'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
state.edges = edges;
|
||||||
|
state.nodes = nodes;
|
||||||
|
|
||||||
|
// 重新计算布局
|
||||||
|
await recomputeLayout();
|
||||||
|
|
||||||
|
// 绘制 svg
|
||||||
|
renderSvg();
|
||||||
|
};
|
||||||
|
|
||||||
|
function renderSvg() {
|
||||||
|
const prevNodeMap = new Map(prevNodes.map(n => [n.id, n]));
|
||||||
|
const prevEdgeMap = new Map(prevEdges.map(e => [e.id, e]));
|
||||||
|
|
||||||
|
// 计算所有节点的最小x和最大x
|
||||||
|
const xs = state.nodes.map(n => (n.x || 0));
|
||||||
|
const minX = Math.min(...xs);
|
||||||
|
const maxX = Math.max(...xs.map((x, i) => x + (state.nodes[i].width || 160)));
|
||||||
|
const contentWidth = maxX - minX;
|
||||||
|
const svgWidth = Math.max(contentWidth + 120, 400); // 120为两侧留白
|
||||||
|
const offsetX = (svgWidth - contentWidth) / 2 - minX;
|
||||||
|
|
||||||
|
const height = Math.max(...state.nodes.map(n => (n.y || 0) + (n.height || 48)), 300) + 60;
|
||||||
|
|
||||||
|
// 不再全量清空,只清空 svg 元素
|
||||||
|
let svg = d3.select(svgContainer.value).select('svg');
|
||||||
|
if (svg.empty()) {
|
||||||
|
svg = d3
|
||||||
|
.select(svgContainer.value)
|
||||||
|
.append('svg')
|
||||||
|
.attr('width', svgWidth)
|
||||||
|
.attr('height', height)
|
||||||
|
.style('user-select', 'none') as any;
|
||||||
|
} else {
|
||||||
|
svg.attr('width', svgWidth).attr('height', height);
|
||||||
|
svg.selectAll('defs').remove();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Arrow marker
|
||||||
|
svg
|
||||||
|
.append('defs')
|
||||||
|
.append('marker')
|
||||||
|
.attr('id', 'arrow')
|
||||||
|
.attr('viewBox', '0 0 8 8')
|
||||||
|
.attr('refX', 6)
|
||||||
|
.attr('refY', 4)
|
||||||
|
.attr('markerWidth', 5)
|
||||||
|
.attr('markerHeight', 5)
|
||||||
|
.attr('orient', 'auto')
|
||||||
|
.append('path')
|
||||||
|
.attr('d', 'M 0 0 L 8 4 L 0 8 z')
|
||||||
|
.attr('fill', 'var(--main-color)');
|
||||||
|
|
||||||
|
// 1. 创建/获取 main group
|
||||||
|
let mainGroup = svg.select('g.main-group');
|
||||||
|
if (mainGroup.empty()) {
|
||||||
|
mainGroup = svg.append('g').attr('class', 'main-group') as any;
|
||||||
|
}
|
||||||
|
mainGroup
|
||||||
|
.transition()
|
||||||
|
.duration(600)
|
||||||
|
.attr('transform', `translate(${offsetX}, 0)`);
|
||||||
|
|
||||||
|
// Draw edges with enter animation
|
||||||
|
const allSections: { id: string, section: any }[] = [];
|
||||||
|
(state.edges || []).forEach(edge => {
|
||||||
|
const sections = edge.sections || [];
|
||||||
|
sections.forEach((section: any, idx: number) => {
|
||||||
|
allSections.push({
|
||||||
|
id: (edge.id || '') + '-' + (section.id || idx),
|
||||||
|
section
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
const edgeSelection = mainGroup.selectAll<SVGLineElement, any>('.edge')
|
||||||
|
.data(allSections, d => d.id);
|
||||||
|
|
||||||
|
edgeSelection.exit().remove();
|
||||||
|
|
||||||
|
const edgeEnter = edgeSelection.enter()
|
||||||
|
.append('line')
|
||||||
|
.attr('class', 'edge')
|
||||||
|
.attr('x1', d => {
|
||||||
|
const prev = prevEdgeMap.get(d.id);
|
||||||
|
return prev && prev.sections && prev.sections[0]
|
||||||
|
? prev.sections[0].startPoint.x + 30
|
||||||
|
: d.section.startPoint.x + 30;
|
||||||
|
})
|
||||||
|
.attr('y1', d => {
|
||||||
|
const prev = prevEdgeMap.get(d.id);
|
||||||
|
return prev && prev.sections && prev.sections[0]
|
||||||
|
? prev.sections[0].startPoint.y + 30
|
||||||
|
: d.section.startPoint.y + 30;
|
||||||
|
})
|
||||||
|
.attr('x2', d => {
|
||||||
|
const prev = prevEdgeMap.get(d.id);
|
||||||
|
return prev && prev.sections && prev.sections[0]
|
||||||
|
? prev.sections[0].endPoint.x + 30
|
||||||
|
: d.section.endPoint.x + 30;
|
||||||
|
})
|
||||||
|
.attr('y2', d => {
|
||||||
|
const prev = prevEdgeMap.get(d.id);
|
||||||
|
return prev && prev.sections && prev.sections[0]
|
||||||
|
? prev.sections[0].endPoint.y + 30
|
||||||
|
: d.section.endPoint.y + 30;
|
||||||
|
})
|
||||||
|
.attr('stroke', 'var(--main-color)')
|
||||||
|
.attr('stroke-width', 2.5)
|
||||||
|
.attr('marker-end', 'url(#arrow)')
|
||||||
|
.attr('opacity', 0);
|
||||||
|
|
||||||
|
edgeEnter
|
||||||
|
.transition()
|
||||||
|
.duration(600)
|
||||||
|
.attr('opacity', 1)
|
||||||
|
.attr('x1', d => d.section.startPoint.x + 30)
|
||||||
|
.attr('y1', d => d.section.startPoint.y + 30)
|
||||||
|
.attr('x2', d => d.section.endPoint.x + 30)
|
||||||
|
.attr('y2', d => d.section.endPoint.y + 30);
|
||||||
|
|
||||||
|
// update + 动画(注意这里不再 transition opacity)
|
||||||
|
edgeSelection.merge(edgeEnter)
|
||||||
|
.transition()
|
||||||
|
.duration(600)
|
||||||
|
.ease(d3.easeCubicInOut)
|
||||||
|
.attr('x1', d => d.section.startPoint.x + 30)
|
||||||
|
.attr('y1', d => d.section.startPoint.y + 30)
|
||||||
|
.attr('x2', d => d.section.endPoint.x + 30)
|
||||||
|
.attr('y2', d => d.section.endPoint.y + 30)
|
||||||
|
.attr('opacity', 1);
|
||||||
|
|
||||||
|
// --- 节点动画部分 ---
|
||||||
|
const nodeGroup = mainGroup.selectAll<SVGGElement, any>('.node')
|
||||||
|
.data(state.nodes, d => d.id);
|
||||||
|
|
||||||
|
nodeGroup.exit().remove();
|
||||||
|
|
||||||
|
// 节点 enter
|
||||||
|
const nodeGroupEnter = nodeGroup.enter()
|
||||||
|
.append('g')
|
||||||
|
.attr('class', 'node')
|
||||||
|
.attr('transform', d => {
|
||||||
|
const prev = prevNodeMap.get(d.id);
|
||||||
|
if (prev) {
|
||||||
|
return `translate(${(prev.x || 0) + 30}, ${(prev.y || 0) + 30})`;
|
||||||
|
}
|
||||||
|
return `translate(${(d.x || 0) + 30}, ${(d.y || 0) + 30})`;
|
||||||
|
})
|
||||||
|
.style('cursor', 'pointer')
|
||||||
|
.attr('opacity', 0)
|
||||||
|
.on('mousedown', null)
|
||||||
|
.on('mouseup', function (event, d) {
|
||||||
|
event.stopPropagation();
|
||||||
|
if (state.selectedNodeId) {
|
||||||
|
|
||||||
|
const { canConnect, reason } = invalidConnectionDetector(state, d);
|
||||||
|
console.log(reason);
|
||||||
|
|
||||||
|
if (reason) {
|
||||||
|
ElMessage.warning(reason);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (canConnect) {
|
||||||
|
state.edges.push({
|
||||||
|
id: `e${state.selectedNodeId}_${d.id}_${Date.now()}`,
|
||||||
|
sources: [state.selectedNodeId],
|
||||||
|
targets: [d.id]
|
||||||
|
});
|
||||||
|
state.selectedNodeId = null;
|
||||||
|
recomputeLayout().then(renderSvg);
|
||||||
|
} else {
|
||||||
|
// 已存在则只取消选中
|
||||||
|
state.selectedNodeId = null;
|
||||||
|
renderSvg();
|
||||||
|
}
|
||||||
|
context.setCaption('');
|
||||||
|
|
||||||
|
} else {
|
||||||
|
state.selectedNodeId = d.id;
|
||||||
|
renderSvg();
|
||||||
|
context.setCaption(t('select-node-define-test-tomo'));
|
||||||
|
}
|
||||||
|
state.draggingNodeId = null;
|
||||||
|
})
|
||||||
|
.on('mouseover', function (event, d) {
|
||||||
|
setHoverItem(d.id);
|
||||||
|
d3.select(this).select('rect')
|
||||||
|
.transition()
|
||||||
|
.duration(200)
|
||||||
|
.attr('stroke', 'var(--main-color)')
|
||||||
|
.attr('stroke-width', 2);
|
||||||
|
})
|
||||||
|
.on('mouseout', function (event, d) {
|
||||||
|
clearHoverItem();
|
||||||
|
if (state.selectedNodeId === d.id) return;
|
||||||
|
d3.select(this).select('rect')
|
||||||
|
.transition()
|
||||||
|
.duration(200)
|
||||||
|
.attr('stroke', 'var(--main-light-color-10)')
|
||||||
|
.attr('stroke-width', 1);
|
||||||
|
});
|
||||||
|
|
||||||
|
nodeGroupEnter.append('rect')
|
||||||
|
.attr('width', (d: any) => d.width)
|
||||||
|
.attr('height', (d: any) => d.height)
|
||||||
|
.attr('rx', 16)
|
||||||
|
.attr('fill', 'var(--main-light-color-20)')
|
||||||
|
.attr('stroke', d => state.selectedNodeId === d.id ? 'var(--main-color)' : 'var(--main-light-color-10)')
|
||||||
|
.attr('stroke-width', 2);
|
||||||
|
|
||||||
|
// 节点文字
|
||||||
|
nodeGroupEnter.append('text')
|
||||||
|
.attr('x', d => d.width / 2)
|
||||||
|
.attr('y', d => d.height / 2 - 6) // 上移一点
|
||||||
|
.attr('text-anchor', 'middle')
|
||||||
|
.attr('font-size', 16)
|
||||||
|
.attr('fill', 'var(--main-color)')
|
||||||
|
.attr('font-weight', 600)
|
||||||
|
.text(d => d.labels?.[0]?.text || 'Tool');
|
||||||
|
|
||||||
|
nodeGroupEnter.append('g').attr('class', 'node-status');
|
||||||
|
|
||||||
|
// 合并 enter+update
|
||||||
|
const nodeStatusGroup = nodeGroup.merge(nodeGroupEnter).select('.node-status');
|
||||||
|
|
||||||
|
// 先清空再重绘
|
||||||
|
nodeStatusGroup.each(function (d) {
|
||||||
|
const g = d3.select(this);
|
||||||
|
g.selectAll('*').remove(); // 清空旧内容
|
||||||
|
|
||||||
|
const status = state.dataView.get(d.id)?.status || 'waiting';
|
||||||
|
if (status === 'running') {
|
||||||
|
g.append('circle')
|
||||||
|
.attr('cx', d.width / 2 - 32)
|
||||||
|
.attr('cy', d.height - 16)
|
||||||
|
.attr('r', 6)
|
||||||
|
.attr('fill', 'none')
|
||||||
|
.attr('stroke', 'var(--main-color)')
|
||||||
|
.attr('stroke-width', 3)
|
||||||
|
.attr('stroke-dasharray', 20)
|
||||||
|
.attr('stroke-dashoffset', 0)
|
||||||
|
.append('animateTransform')
|
||||||
|
.attr('attributeName', 'transform')
|
||||||
|
.attr('attributeType', 'XML')
|
||||||
|
.attr('type', 'rotate')
|
||||||
|
.attr('from', `0 ${(d.width / 2 - 32)} ${(d.height - 16)}`)
|
||||||
|
.attr('to', `360 ${(d.width / 2 - 32)} ${(d.height - 16)}`)
|
||||||
|
.attr('dur', '1s')
|
||||||
|
.attr('repeatCount', 'indefinite');
|
||||||
|
g.append('text')
|
||||||
|
.attr('x', d.width / 2 - 16)
|
||||||
|
.attr('y', d.height - 12)
|
||||||
|
.attr('font-size', 13)
|
||||||
|
.attr('fill', 'var(--main-color)')
|
||||||
|
.text('running');
|
||||||
|
} else if (status === 'waiting') {
|
||||||
|
g.append('circle')
|
||||||
|
.attr('cx', d.width / 2 - 32)
|
||||||
|
.attr('cy', d.height - 16)
|
||||||
|
.attr('r', 6)
|
||||||
|
.attr('fill', 'none')
|
||||||
|
.attr('stroke', '#bdbdbd')
|
||||||
|
.attr('stroke-width', 3);
|
||||||
|
g.append('text')
|
||||||
|
.attr('x', d.width / 2 - 16)
|
||||||
|
.attr('y', d.height - 12)
|
||||||
|
.attr('font-size', 13)
|
||||||
|
.attr('fill', '#bdbdbd')
|
||||||
|
.text('waiting');
|
||||||
|
} else if (status === 'success') {
|
||||||
|
g.append('circle')
|
||||||
|
.attr('cx', d.width / 2 - 32)
|
||||||
|
.attr('cy', d.height - 16)
|
||||||
|
.attr('r', 6)
|
||||||
|
.attr('fill', 'none')
|
||||||
|
.attr('stroke', '#4caf50')
|
||||||
|
.attr('stroke-width', 3);
|
||||||
|
g.append('text')
|
||||||
|
.attr('x', d.width / 2 - 16)
|
||||||
|
.attr('y', d.height - 12)
|
||||||
|
.attr('font-size', 13)
|
||||||
|
.attr('fill', '#4caf50')
|
||||||
|
.text('success');
|
||||||
|
} else if (status === 'error') {
|
||||||
|
g.append('circle')
|
||||||
|
.attr('cx', d.width / 2 - 32)
|
||||||
|
.attr('cy', d.height - 16)
|
||||||
|
.attr('r', 6)
|
||||||
|
.attr('fill', 'none')
|
||||||
|
.attr('stroke', '#f44336')
|
||||||
|
.attr('stroke-width', 3);
|
||||||
|
g.append('text')
|
||||||
|
.attr('x', d.width / 2 - 16)
|
||||||
|
.attr('y', d.height - 12)
|
||||||
|
.attr('font-size', 13)
|
||||||
|
.attr('fill', '#f44336')
|
||||||
|
.text('error');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
// 节点 enter 动画
|
||||||
|
nodeGroupEnter
|
||||||
|
.transition()
|
||||||
|
.duration(600)
|
||||||
|
.attr('opacity', 1)
|
||||||
|
.attr('transform', d => `translate(${(d.x || 0) + 30}, ${(d.y || 0) + 30})`);
|
||||||
|
|
||||||
|
// 节点 update 动画
|
||||||
|
nodeGroup
|
||||||
|
.transition()
|
||||||
|
.duration(600)
|
||||||
|
.ease(d3.easeCubicInOut)
|
||||||
|
.attr('transform', d => `translate(${(d.x || 0) + 30}, ${(d.y || 0) + 30})`);
|
||||||
|
|
||||||
|
// 高亮选中节点动画
|
||||||
|
nodeGroup.select('rect')
|
||||||
|
.transition()
|
||||||
|
.duration(400)
|
||||||
|
.attr('stroke', d => state.selectedNodeId === d.id ? 'var(--main-color)' : 'var(--main-light-color-10)');
|
||||||
|
|
||||||
|
// 边高亮
|
||||||
|
svg.selectAll<SVGLineElement, any>('.edge')
|
||||||
|
.on('mouseover', function () {
|
||||||
|
d3.select(this)
|
||||||
|
.transition()
|
||||||
|
.duration(200)
|
||||||
|
.attr('stroke', 'var(--main-color)')
|
||||||
|
.attr('stroke-width', 4.5);
|
||||||
|
|
||||||
|
context.setCaption(t('click-edge-to-delete'));
|
||||||
|
|
||||||
|
})
|
||||||
|
.on('mouseout', function () {
|
||||||
|
d3.select(this)
|
||||||
|
.transition()
|
||||||
|
.duration(200)
|
||||||
|
.attr('stroke', 'var(--main-color)')
|
||||||
|
.attr('stroke-width', 2.5);
|
||||||
|
|
||||||
|
context.setCaption('');
|
||||||
|
})
|
||||||
|
.on('click', function (event, d) {
|
||||||
|
// 只删除当前 edge
|
||||||
|
state.edges = state.edges.filter(e => {
|
||||||
|
// 多段 edge 情况
|
||||||
|
if (e.sections) {
|
||||||
|
// 只保留不是当前 section 的
|
||||||
|
return !e.sections.some((section: any, idx: number) =>
|
||||||
|
((e.id || '') + '-' + (section.id || idx)) === d.id
|
||||||
|
);
|
||||||
|
}
|
||||||
|
// 单段 edge 情况
|
||||||
|
return e.id !== d.id && e.id !== d.section?.id;
|
||||||
|
});
|
||||||
|
recomputeLayout().then(renderSvg);
|
||||||
|
event.stopPropagation();
|
||||||
|
});
|
||||||
|
|
||||||
|
// 渲染结束后保存当前快照
|
||||||
|
prevNodes = state.nodes.map(n => ({ ...n }));
|
||||||
|
prevEdges = (state.edges || []).map(e => ({ ...e, sections: e.sections ? e.sections.map((s: any) => ({ ...s })) : [] }));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 重置连接为链表结构
|
||||||
|
function resetConnections() {
|
||||||
|
if (!state.nodes.length) return;
|
||||||
|
const edges = [];
|
||||||
|
for (let i = 0; i < state.nodes.length - 1; ++i) {
|
||||||
|
const prev = state.nodes[i];
|
||||||
|
const next = state.nodes[i + 1];
|
||||||
|
edges.push({
|
||||||
|
id: prev.id + '-' + next.id,
|
||||||
|
sources: [prev.id],
|
||||||
|
targets: [next.id]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
state.edges = edges;
|
||||||
|
recomputeLayout().then(renderSvg);
|
||||||
|
}
|
||||||
|
|
||||||
|
const context = inject('context') as any;
|
||||||
|
context.reset = resetConnections;
|
||||||
|
context.state = state;
|
||||||
|
context.render = renderSvg;
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
nextTick(drawDiagram);
|
||||||
|
});
|
||||||
|
|
||||||
|
// 4. 计算窗口位置
|
||||||
|
function getNodePopupStyle(node: any): any {
|
||||||
|
// 节点的 svg 坐标转为容器内绝对定位
|
||||||
|
// 注意:这里假设 offsetX、node.x、node.y 已经是最新的
|
||||||
|
const marginX = 50;
|
||||||
|
const marginY = 80;
|
||||||
|
const popupWidth = 300;
|
||||||
|
const popupHeight = 500;
|
||||||
|
|
||||||
|
let left = (node.x || 0) + (node.width || 160) + 100;
|
||||||
|
let top = (node.y || 0) + 30;
|
||||||
|
|
||||||
|
// 获取容器宽高
|
||||||
|
const container = svgContainer.value;
|
||||||
|
let containerWidth = 1200, containerHeight = 800; // 默认值
|
||||||
|
if (container) {
|
||||||
|
const rect = container.getBoundingClientRect();
|
||||||
|
containerWidth = rect.width;
|
||||||
|
containerHeight = rect.height;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 限制 left 和 top 不超出容器
|
||||||
|
left = Math.max(marginX, Math.min(left, containerWidth - popupWidth - marginX));
|
||||||
|
top = Math.max(marginY, Math.min(top, containerHeight - popupHeight - marginY));
|
||||||
|
|
||||||
|
return {
|
||||||
|
position: 'absolute',
|
||||||
|
left: `${left}px`,
|
||||||
|
top: `${top}px`,
|
||||||
|
width: `${popupWidth}px`,
|
||||||
|
height: `${popupHeight}px`
|
||||||
|
};
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.diagram-container {
|
||||||
|
width: 100%;
|
||||||
|
min-height: 200px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: flex-start;
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 24px 0;
|
||||||
|
overflow-x: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.node-popup {
|
||||||
|
position: absolute;
|
||||||
|
background: var(--background);
|
||||||
|
border: 1px solid var(--main-color);
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 8px 12px;
|
||||||
|
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
|
||||||
|
white-space: nowrap;
|
||||||
|
z-index: 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 旋转动画 */
|
||||||
|
.status-running-circle {
|
||||||
|
animation: spin 1s linear infinite;
|
||||||
|
transform-origin: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes spin {
|
||||||
|
100% {
|
||||||
|
transform: rotate(360deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
173
renderer/src/components/main-panel/tool/auto-detector/index.vue
Normal file
173
renderer/src/components/main-panel/tool/auto-detector/index.vue
Normal file
@ -0,0 +1,173 @@
|
|||||||
|
<template>
|
||||||
|
<el-dialog v-model="showDialog" width="800px" class="no-padding-dialog">
|
||||||
|
<template #header>
|
||||||
|
<div style="display: flex; align-items: center;">
|
||||||
|
<span>Tool Diagram</span>
|
||||||
|
 
|
||||||
|
<el-button size="small" type="primary" @click="() => context.reset()">{{ t("reset") }}</el-button>
|
||||||
|
<!-- 自检程序弹出表单 -->
|
||||||
|
<el-popover placement="top" width="350" trigger="click" v-model:visible="testFormVisible">
|
||||||
|
<template #reference>
|
||||||
|
<el-button size="small" type="primary">
|
||||||
|
{{ t('start-auto-detect') }}
|
||||||
|
</el-button>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<el-input type="textarea" v-model="testPrompt" :rows="2" style="margin-bottom: 8px;"
|
||||||
|
placeholder="请输入 prompt" />
|
||||||
|
<div style="display: flex; align-items: center; margin-bottom: 8px;">
|
||||||
|
<el-switch v-model="enableXmlWrapper" style="margin-right: 8px;" />
|
||||||
|
<span :style="{
|
||||||
|
opacity: enableXmlWrapper ? 1 : 0.7,
|
||||||
|
color: enableXmlWrapper ? 'var(--main-color)' : undefined
|
||||||
|
}">XML</span>
|
||||||
|
</div>
|
||||||
|
<div style="text-align: right;">
|
||||||
|
<el-button size="small" @click="testFormVisible = false">{{ t("cancel") }}</el-button>
|
||||||
|
<el-button size="small" type="primary" @click="onTestConfirm">
|
||||||
|
{{ t("confirm") }}
|
||||||
|
</el-button>
|
||||||
|
</div>
|
||||||
|
</el-popover>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<el-scrollbar height="80vh">
|
||||||
|
<Diagram :tab-id="props.tabId" />
|
||||||
|
</el-scrollbar>
|
||||||
|
<transition name="main-fade" mode="out-in">
|
||||||
|
<div class="caption" v-show="showCaption">
|
||||||
|
{{ caption }}
|
||||||
|
</div>
|
||||||
|
</transition>
|
||||||
|
</el-dialog>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { computed, nextTick, provide, ref } from 'vue';
|
||||||
|
import Diagram from './diagram.vue';
|
||||||
|
import { makeNodeTest, topoSortParallel, type DiagramContext, type DiagramState } from './diagram';
|
||||||
|
import { ElMessage } from 'element-plus';
|
||||||
|
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
|
import type { ToolStorage } from '../tools';
|
||||||
|
import { tabs } from '../../panel';
|
||||||
|
|
||||||
|
const { t } = useI18n();
|
||||||
|
|
||||||
|
const caption = ref('');
|
||||||
|
const showCaption = ref(false);
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
modelValue: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
|
tabId: {
|
||||||
|
type: Number,
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
});
|
||||||
|
const emit = defineEmits(['update:modelValue']);
|
||||||
|
|
||||||
|
const showDialog = computed({
|
||||||
|
get: () => props.modelValue,
|
||||||
|
set: v => emit('update:modelValue', v)
|
||||||
|
});
|
||||||
|
|
||||||
|
function setCaption(text: string) {
|
||||||
|
caption.value = text;
|
||||||
|
if (caption.value) {
|
||||||
|
nextTick(() => {
|
||||||
|
showCaption.value = true;
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
nextTick(() => {
|
||||||
|
showCaption.value = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const context: DiagramContext = {
|
||||||
|
reset: () => { },
|
||||||
|
render: () => { },
|
||||||
|
state: undefined,
|
||||||
|
setCaption
|
||||||
|
};
|
||||||
|
|
||||||
|
provide('context', context);
|
||||||
|
|
||||||
|
const tab = tabs.content[props.tabId];
|
||||||
|
const tabStorage = tab.storage as ToolStorage;
|
||||||
|
const autoDetectDiagram = tabStorage.autoDetectDiagram;
|
||||||
|
|
||||||
|
if (autoDetectDiagram) {
|
||||||
|
// ...
|
||||||
|
} else {
|
||||||
|
tabStorage.autoDetectDiagram = {
|
||||||
|
edges: [],
|
||||||
|
views: []
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// 新增:自检参数表单相关
|
||||||
|
const testFormVisible = ref(false);
|
||||||
|
const enableXmlWrapper = ref(false);
|
||||||
|
const testPrompt = ref('please call the tool {tool} to make some test');
|
||||||
|
|
||||||
|
async function onTestConfirm() {
|
||||||
|
testFormVisible.value = false;
|
||||||
|
// 这里可以将 enableXmlWrapper.value 和 testPrompt.value 传递给自检逻辑
|
||||||
|
const state = context.state;
|
||||||
|
|
||||||
|
|
||||||
|
tabStorage.autoDetectDiagram!.views = [];
|
||||||
|
|
||||||
|
if (state) {
|
||||||
|
const dispatches = topoSortParallel(state);
|
||||||
|
for (const nodeIds of dispatches) {
|
||||||
|
for (const id of nodeIds) {
|
||||||
|
const view = state.dataView.get(id);
|
||||||
|
if (view) {
|
||||||
|
await makeNodeTest(view, enableXmlWrapper.value, testPrompt.value, context)
|
||||||
|
tabStorage.autoDetectDiagram!.views!.push({
|
||||||
|
tool: view.tool,
|
||||||
|
status: view.status,
|
||||||
|
function: view.function,
|
||||||
|
result: view.result
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ElMessage.error('error');
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.no-padding-dialog {
|
||||||
|
margin-top: 30px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.no-padding-dialog .caption {
|
||||||
|
position: absolute;
|
||||||
|
left: 20px;
|
||||||
|
bottom: 10px;
|
||||||
|
margin: 0 auto;
|
||||||
|
width: fit-content;
|
||||||
|
min-height: 32px;
|
||||||
|
background: rgba(245, 247, 250, 0.05);
|
||||||
|
border-radius: 8px;
|
||||||
|
box-shadow: 0 2px 8px 0 rgba(0, 0, 0, 0.06);
|
||||||
|
color: var(--main-color);
|
||||||
|
font-size: 15px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 6px 16px;
|
||||||
|
z-index: 10;
|
||||||
|
transition: background 0.2s;
|
||||||
|
}
|
||||||
|
</style>
|
@ -1,29 +1,41 @@
|
|||||||
<template>
|
<template>
|
||||||
<el-scrollbar height="100%">
|
<el-scrollbar height="100%">
|
||||||
|
<AutoDetector :tab-id="props.tabId" />
|
||||||
<div class="tool-module">
|
<div class="tool-module">
|
||||||
<div class="left">
|
<div class="left">
|
||||||
<h2>
|
<h2>
|
||||||
<span class="iconfont icon-tool"></span>
|
<span class="iconfont icon-tool"></span>
|
||||||
工具模块
|
{{ t('tool-module') }}
|
||||||
|
<el-button
|
||||||
|
style="font-size: 12px;"
|
||||||
|
@click="showAutoDetector = true"
|
||||||
|
>
|
||||||
|
{{ t('tool-self-detect') }}
|
||||||
|
</el-button>
|
||||||
</h2>
|
</h2>
|
||||||
<ToolList :tab-id="props.tabId"></ToolList>
|
<ToolList :tab-id="props.tabId"></ToolList>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
<div class="right">
|
<div class="right">
|
||||||
<ToolExecutor :tab-id="props.tabId"></ToolExecutor>
|
<ToolExecutor :tab-id="props.tabId"></ToolExecutor>
|
||||||
|
|
||||||
<ToolLogger :tab-id="props.tabId"></ToolLogger>
|
<ToolLogger :tab-id="props.tabId"></ToolLogger>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<AutoDetector
|
||||||
|
v-model="showAutoDetector"
|
||||||
|
:tab-id="props.tabId"
|
||||||
|
/>
|
||||||
</el-scrollbar>
|
</el-scrollbar>
|
||||||
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { defineProps } from 'vue';
|
import { defineProps, ref } from 'vue';
|
||||||
import ToolList from './tool-list.vue';
|
import ToolList from './tool-list.vue';
|
||||||
import ToolExecutor from './tool-executor.vue';
|
import ToolExecutor from './tool-executor.vue';
|
||||||
import ToolLogger from './tool-logger.vue';
|
import ToolLogger from './tool-logger.vue';
|
||||||
|
import AutoDetector from './auto-detector/index.vue';
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
|
|
||||||
|
const { t } = useI18n();
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
tabId: {
|
tabId: {
|
||||||
@ -31,6 +43,8 @@ const props = defineProps({
|
|||||||
required: true
|
required: true
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const showAutoDetector = ref(false);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
128
renderer/src/components/main-panel/tool/swim-pool.vue
Normal file
128
renderer/src/components/main-panel/tool/swim-pool.vue
Normal file
@ -0,0 +1,128 @@
|
|||||||
|
<template>
|
||||||
|
<div class="swim-pool">
|
||||||
|
<transition-group name="lane-move" tag="div">
|
||||||
|
<div v-for="(lane, laneIdx) in lanes" :key="lane.id" class="swim-lane" @dragover.prevent
|
||||||
|
@drop="onDrop(laneIdx)">
|
||||||
|
<div class="lane-title">Group {{ laneIdx + 1 }}</div>
|
||||||
|
<div v-for="tool in lane.tools" :key="tool.name" class="tool-card" draggable="true"
|
||||||
|
@dragstart="onDragStart(tool, laneIdx)">
|
||||||
|
{{ tool.name }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</transition-group>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import type { ToolItem } from '@/hook/type';
|
||||||
|
import { mcpClientAdapter } from '@/views/connect/core';
|
||||||
|
import { ref, onMounted } from 'vue';
|
||||||
|
|
||||||
|
// 工具类型
|
||||||
|
interface Tool {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Lane {
|
||||||
|
id: string;
|
||||||
|
tools: ToolItem[];
|
||||||
|
}
|
||||||
|
|
||||||
|
// 工具列表
|
||||||
|
const tools = ref<ToolItem[]>([]);
|
||||||
|
|
||||||
|
// 泳道列表
|
||||||
|
const lanes = ref<Lane[]>([]);
|
||||||
|
|
||||||
|
// 获取所有工具
|
||||||
|
const getAllTools = async () => {
|
||||||
|
const items = [];
|
||||||
|
for (const client of mcpClientAdapter.clients) {
|
||||||
|
const clientTools = await client.getTools();
|
||||||
|
items.push(...clientTools.values());
|
||||||
|
}
|
||||||
|
return items;
|
||||||
|
};
|
||||||
|
|
||||||
|
// 初始化
|
||||||
|
onMounted(async () => {
|
||||||
|
tools.value = await getAllTools();
|
||||||
|
console.log(tools.value);
|
||||||
|
|
||||||
|
lanes.value = tools.value.map((tool, idx) => ({
|
||||||
|
id: `lane-${idx}`,
|
||||||
|
tools: [tool]
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
|
||||||
|
// 拖拽信息
|
||||||
|
let dragInfo: { tool: ToolItem | null; fromLane: number } = { tool: null, fromLane: -1 };
|
||||||
|
|
||||||
|
// 拖拽开始
|
||||||
|
function onDragStart(tool: ToolItem, fromLane: number) {
|
||||||
|
dragInfo = { tool, fromLane };
|
||||||
|
}
|
||||||
|
|
||||||
|
// 拖拽释放
|
||||||
|
function onDrop(toLane: number) {
|
||||||
|
if (dragInfo.tool && dragInfo.fromLane !== -1 && dragInfo.fromLane !== toLane) {
|
||||||
|
// 从原泳道移除
|
||||||
|
lanes.value[dragInfo.fromLane].tools = lanes.value[dragInfo.fromLane].tools.filter(
|
||||||
|
t => t.name !== dragInfo.tool!.name
|
||||||
|
);
|
||||||
|
// 加入新泳道
|
||||||
|
lanes.value[toLane].tools.push(dragInfo.tool);
|
||||||
|
|
||||||
|
// 如果原泳道已空,删除该泳道
|
||||||
|
if (lanes.value[dragInfo.fromLane].tools.length === 0) {
|
||||||
|
lanes.value.splice(dragInfo.fromLane, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 重新排序,确保泳道顺序和索引一致
|
||||||
|
lanes.value = lanes.value.map((lane, idx) => ({
|
||||||
|
...lane,
|
||||||
|
// 可选:如果你希望泳道 id 也随序号变化
|
||||||
|
id: `lane-${idx}`
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
dragInfo = { tool: null, fromLane: -1 };
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.swim-pool {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.swim-lane {
|
||||||
|
border: 1px solid var(--sidebar);
|
||||||
|
min-height: 60px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
padding: 8px;
|
||||||
|
border-radius: 8px;
|
||||||
|
transition: box-shadow 0.3s;
|
||||||
|
box-shadow: 0 2px 8px 0 rgba(0,0,0,0.08);
|
||||||
|
}
|
||||||
|
|
||||||
|
.lane-title {
|
||||||
|
font-weight: bold;
|
||||||
|
margin-bottom: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tool-card {
|
||||||
|
border: 1px solid var(--main-color);
|
||||||
|
border-radius: 4px;
|
||||||
|
padding: 4px 12px;
|
||||||
|
margin-bottom: 4px;
|
||||||
|
cursor: grab;
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 动画样式 */
|
||||||
|
.lane-move-move {
|
||||||
|
transition: transform 0.5s cubic-bezier(0.4, 0, 0.2, 1);
|
||||||
|
}
|
||||||
|
</style>
|
@ -20,9 +20,16 @@
|
|||||||
<el-switch v-else-if="property.type === 'boolean'" active-text="true" inactive-text="false"
|
<el-switch v-else-if="property.type === 'boolean'" active-text="true" inactive-text="false"
|
||||||
v-model="tabStorage.formData[name]" />
|
v-model="tabStorage.formData[name]" />
|
||||||
|
|
||||||
|
<el-input-tag
|
||||||
|
v-else-if="property.type === 'array'"
|
||||||
|
v-model="tabStorage.formData[name]"
|
||||||
|
:placeholder="property.description || t('enter') + ' ' + (property.title || name) + ' (逗号分隔)'"
|
||||||
|
/>
|
||||||
|
|
||||||
<k-input-object v-else-if="property.type === 'object'" v-model="tabStorage.formData[name]"
|
<k-input-object v-else-if="property.type === 'object'" v-model="tabStorage.formData[name]"
|
||||||
:schema="property"
|
:schema="property"
|
||||||
:placeholder="property.description || t('enter') + ' ' + (property.title || name)" />
|
:placeholder="property.description || t('enter') + ' ' + (property.title || name)" />
|
||||||
|
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@ -33,9 +40,35 @@
|
|||||||
<el-button @click="resetForm">
|
<el-button @click="resetForm">
|
||||||
{{ t('reset') }}
|
{{ t('reset') }}
|
||||||
</el-button>
|
</el-button>
|
||||||
<el-button @click="generateMockData">
|
<el-button @click="generateMockData" :loading="mockLoading"
|
||||||
|
:disabled="loading || aiMockLoading || mockLoading">
|
||||||
{{ 'mook' }}
|
{{ 'mook' }}
|
||||||
</el-button>
|
</el-button>
|
||||||
|
|
||||||
|
<el-popover placement="top" width="350" trigger="click" v-model:visible="aiPromptVisible">
|
||||||
|
<template #reference>
|
||||||
|
<el-button :loading="aiMockLoading" :disabled="loading || aiMockLoading || mockLoading">
|
||||||
|
{{ 'AI' }}
|
||||||
|
</el-button>
|
||||||
|
</template>
|
||||||
|
<div style="margin-bottom: 8px; font-weight: bold;">
|
||||||
|
{{ t('edit-ai-mook-prompt') }}
|
||||||
|
</div>
|
||||||
|
<el-input type="textarea" v-model="aiMookPrompt" :rows="2" style="margin-bottom: 8px;" />
|
||||||
|
<div style="display: flex; align-items: center; margin-bottom: 8px;">
|
||||||
|
<el-switch
|
||||||
|
v-model="enableXmlWrapper"
|
||||||
|
style="margin-right: 8px;"
|
||||||
|
/>
|
||||||
|
<span style="opacity: 0.7;">XML</span>
|
||||||
|
</div>
|
||||||
|
<div style="text-align: right;">
|
||||||
|
<el-button size="small" @click="aiPromptVisible = false">{{ t('cancel') }}</el-button>
|
||||||
|
<el-button size="small" type="primary" :loading="aiMockLoading" @click="onAIMookConfirm">
|
||||||
|
{{ t('confirm') }}
|
||||||
|
</el-button>
|
||||||
|
</div>
|
||||||
|
</el-popover>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-form>
|
</el-form>
|
||||||
</div>
|
</div>
|
||||||
@ -44,7 +77,7 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { defineComponent, defineProps, watch, ref, computed } from 'vue';
|
import { defineComponent, defineProps, watch, ref, computed } from 'vue';
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
import type { FormInstance, FormRules } from 'element-plus';
|
import { ElMessage, type FormInstance, type FormRules } from 'element-plus';
|
||||||
import { tabs } from '../panel';
|
import { tabs } from '../panel';
|
||||||
import type { ToolStorage } from './tools';
|
import type { ToolStorage } from './tools';
|
||||||
import { getDefaultValue, normaliseJavascriptType } from '@/hook/mcp';
|
import { getDefaultValue, normaliseJavascriptType } from '@/hook/mcp';
|
||||||
@ -52,9 +85,12 @@ import { getDefaultValue, normaliseJavascriptType } from '@/hook/mcp';
|
|||||||
import KInputObject from '@/components/k-input-object/index.vue';
|
import KInputObject from '@/components/k-input-object/index.vue';
|
||||||
import { mcpClientAdapter } from '@/views/connect/core';
|
import { mcpClientAdapter } from '@/views/connect/core';
|
||||||
import { JSONSchemaFaker } from 'json-schema-faker';
|
import { JSONSchemaFaker } from 'json-schema-faker';
|
||||||
import { faker } from '@faker-js/faker';
|
|
||||||
|
|
||||||
defineComponent({ name: 'tool-executor' });
|
defineComponent({ name: 'tool-executor' });
|
||||||
|
const mockLoading = ref(false);
|
||||||
|
const aiMockLoading = ref(false);
|
||||||
|
const aiPromptVisible = ref(false);
|
||||||
|
const enableXmlWrapper = ref(false);
|
||||||
|
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
|
|
||||||
@ -86,6 +122,7 @@ const currentTool = computed(() => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const aiMookPrompt = ref(`please call the tool ${currentTool.value?.name || ''} to make some test`);
|
||||||
|
|
||||||
const formRules = computed<FormRules>(() => {
|
const formRules = computed<FormRules>(() => {
|
||||||
const rules: FormRules = {};
|
const rules: FormRules = {};
|
||||||
@ -97,7 +134,7 @@ const formRules = computed<FormRules>(() => {
|
|||||||
rules[name] = [
|
rules[name] = [
|
||||||
{
|
{
|
||||||
required: true,
|
required: true,
|
||||||
message: `${property.title || name} 是必填字段`,
|
message: `${property.title || name} ` + t("is-required"),
|
||||||
trigger: 'blur'
|
trigger: 'blur'
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
@ -119,7 +156,8 @@ const initFormData = () => {
|
|||||||
|
|
||||||
Object.entries(currentTool.value.inputSchema.properties).forEach(([name, property]) => {
|
Object.entries(currentTool.value.inputSchema.properties).forEach(([name, property]) => {
|
||||||
newSchemaDataForm[name] = getDefaultValue(property);
|
newSchemaDataForm[name] = getDefaultValue(property);
|
||||||
const originType = normaliseJavascriptType(typeof tabStorage.formData[name]);
|
const rawType = Array.isArray(tabStorage.formData[name]) ? 'array' : typeof tabStorage.formData[name];
|
||||||
|
const originType = normaliseJavascriptType(rawType);
|
||||||
|
|
||||||
if (tabStorage.formData[name] !== undefined && originType === property.type) {
|
if (tabStorage.formData[name] !== undefined && originType === property.type) {
|
||||||
newSchemaDataForm[name] = tabStorage.formData[name];
|
newSchemaDataForm[name] = tabStorage.formData[name];
|
||||||
@ -133,24 +171,92 @@ const resetForm = () => {
|
|||||||
formRef.value?.resetFields();
|
formRef.value?.resetFields();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
import { TaskLoop } from '@/components/main-panel/chat/core/task-loop';
|
||||||
|
import type { ChatStorage } from '../chat/chat-box/chat';
|
||||||
|
|
||||||
|
const onAIMookConfirm = async () => {
|
||||||
|
aiPromptVisible.value = false;
|
||||||
|
await generateAIMockData(aiMookPrompt.value);
|
||||||
|
};
|
||||||
|
|
||||||
|
const generateAIMockData = async (prompt?: string) => {
|
||||||
|
if (!currentTool.value?.inputSchema) return;
|
||||||
|
aiMockLoading.value = true;
|
||||||
|
try {
|
||||||
|
const loop = new TaskLoop({ maxEpochs: 1 });
|
||||||
|
const usePrompt = prompt || `please call the tool ${currentTool.value.name} to make some test`;
|
||||||
|
const chatStorage = {
|
||||||
|
messages: [],
|
||||||
|
settings: {
|
||||||
|
temperature: 0.6,
|
||||||
|
systemPrompt: '',
|
||||||
|
enableTools: [{
|
||||||
|
name: currentTool.value.name,
|
||||||
|
description: currentTool.value.description,
|
||||||
|
inputSchema: currentTool.value.inputSchema,
|
||||||
|
enabled: true
|
||||||
|
}],
|
||||||
|
enableWebSearch: false,
|
||||||
|
contextLength: 5,
|
||||||
|
enableXmlWrapper: enableXmlWrapper.value,
|
||||||
|
parallelToolCalls: false
|
||||||
|
}
|
||||||
|
} as ChatStorage;
|
||||||
|
|
||||||
|
loop.setMaxEpochs(1);
|
||||||
|
|
||||||
|
let aiMockJson: any = undefined;
|
||||||
|
|
||||||
|
loop.registerOnToolCall(toolCall => {
|
||||||
|
console.log(toolCall);
|
||||||
|
|
||||||
|
if (toolCall.function?.name === currentTool.value?.name) {
|
||||||
|
try {
|
||||||
|
const toolArgs = JSON.parse(toolCall.function?.arguments || '{}');
|
||||||
|
aiMockJson = toolArgs;
|
||||||
|
} catch (e) {
|
||||||
|
ElMessage.error('AI 生成的 JSON 解析错误');
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ElMessage.error('AI 调用了未知的工具');
|
||||||
|
}
|
||||||
|
loop.abort();
|
||||||
|
return toolCall;
|
||||||
|
});
|
||||||
|
|
||||||
|
loop.registerOnError(error => {
|
||||||
|
ElMessage.error(error + '');
|
||||||
|
});
|
||||||
|
|
||||||
|
await loop.start(chatStorage, usePrompt);
|
||||||
|
|
||||||
|
if (aiMockJson && typeof aiMockJson === 'object') {
|
||||||
|
Object.keys(aiMockJson).forEach(key => {
|
||||||
|
tabStorage.formData[key] = aiMockJson[key];
|
||||||
|
});
|
||||||
|
formRef.value?.clearValidate?.();
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
aiMockLoading.value = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const generateMockData = async () => {
|
const generateMockData = async () => {
|
||||||
if (!currentTool.value?.inputSchema) return;
|
if (!currentTool.value?.inputSchema) return;
|
||||||
|
mockLoading.value = true;
|
||||||
// 注册faker到json-schema-faker
|
try {
|
||||||
JSONSchemaFaker.option({
|
JSONSchemaFaker.option({
|
||||||
useDefaultValue: true,
|
useDefaultValue: true,
|
||||||
alwaysFakeOptionals: true
|
alwaysFakeOptionals: true
|
||||||
});
|
});
|
||||||
|
const mockData = await JSONSchemaFaker.resolve(currentTool.value.inputSchema as any) as any;
|
||||||
// 生成mock数据
|
Object.keys(mockData).forEach(key => {
|
||||||
const mockData = await JSONSchemaFaker.resolve(currentTool.value.inputSchema as any);
|
tabStorage.formData[key] = mockData[key];
|
||||||
|
});
|
||||||
console.log(mockData);
|
formRef.value?.clearValidate?.();
|
||||||
|
} finally {
|
||||||
// 更新表单数据
|
mockLoading.value = false;
|
||||||
// Object.keys(mockData).forEach(key => {
|
}
|
||||||
// tabStorage.formData[key] = mockData[key];
|
|
||||||
// });
|
|
||||||
};
|
};
|
||||||
|
|
||||||
async function handleExecute() {
|
async function handleExecute() {
|
||||||
@ -165,10 +271,15 @@ async function handleExecute() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
watch(currentTool, (tool) => {
|
||||||
|
aiMookPrompt.value = `please call the tool ${tool?.name || ''} to make some test`;
|
||||||
|
});
|
||||||
|
|
||||||
watch(() => tabStorage.currentToolName, () => {
|
watch(() => tabStorage.currentToolName, () => {
|
||||||
initFormData();
|
initFormData();
|
||||||
resetForm();
|
resetForm();
|
||||||
}, { immediate: true });
|
}, { immediate: true });
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
@ -191,4 +302,14 @@ watch(() => tabStorage.currentToolName, () => {
|
|||||||
.tool-executor-container .el-switch__core {
|
.tool-executor-container .el-switch__core {
|
||||||
border: 1px solid var(--main-color) !important;
|
border: 1px solid var(--main-color) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.el-button:active {
|
||||||
|
transform: scale(0.95);
|
||||||
|
transition: transform 0.08s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-tag.el-tag--info {
|
||||||
|
background-color: var(--main-color);
|
||||||
|
}
|
||||||
|
|
||||||
</style>
|
</style>
|
@ -8,23 +8,34 @@
|
|||||||
<span>tools/list</span>
|
<span>tools/list</span>
|
||||||
<span class="iconfont icon-restart" @click.stop="reloadTools(client, { first: false })"></span>
|
<span class="iconfont icon-restart" @click.stop="reloadTools(client, { first: false })"></span>
|
||||||
|
|
||||||
<span>
|
<span>
|
||||||
<span class="cilent-name-tag">
|
<span class="cilent-name-tag">
|
||||||
{{ client.name }}
|
{{ client.name }}
|
||||||
</span>
|
</span>
|
||||||
</span>
|
</span>
|
||||||
</h3>
|
</h3>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<!-- body -->
|
<!-- body -->
|
||||||
<div class="tool-list-container-scrollbar">
|
<div class="tool-list-container-scrollbar">
|
||||||
<el-scrollbar height="fit-content">
|
<el-scrollbar height="fit-content">
|
||||||
<div class="tool-list-container">
|
<div class="tool-list-container-scrollbar">
|
||||||
<div class="item" :class="{ 'active': tabStorage.currentToolName === tool.name }"
|
<el-scrollbar height="fit-content" v-if="(client.tools?.size || 0) > 0">
|
||||||
v-for="tool of client.tools?.values()" :key="tool.name" @click="handleClick(tool)">
|
<div class="tool-list-container">
|
||||||
<span>{{ tool.name }}</span>
|
<div class="item" :class="{ 'active': tabStorage.currentToolName === tool.name }"
|
||||||
<br>
|
v-for="tool of client.tools?.values()" :key="tool.name" @click="handleClick(tool)">
|
||||||
<span class="tool-description">{{ tool.description || '' }}</span>
|
<span>{{ tool.name }}</span>
|
||||||
|
<br>
|
||||||
|
<span class="tool-description">{{ tool.description || '' }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</el-scrollbar>
|
||||||
|
<div v-else style="padding: 10px;">
|
||||||
|
<div class="empty-description">
|
||||||
|
<span class="iconfont icon-empty"
|
||||||
|
style="font-size: 22px; opacity: 0.4; margin-right: 6px;"></span>
|
||||||
|
<span style="opacity: 0.6;">No tools found.</span>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</el-scrollbar>
|
</el-scrollbar>
|
||||||
@ -106,7 +117,7 @@ onMounted(async () => {
|
|||||||
width: 175px;
|
width: 175px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tool-list-container > .item {
|
.tool-list-container>.item {
|
||||||
margin: 3px;
|
margin: 3px;
|
||||||
padding: 5px 10px;
|
padding: 5px 10px;
|
||||||
border-radius: .3em;
|
border-radius: .3em;
|
||||||
@ -117,6 +128,11 @@ onMounted(async () => {
|
|||||||
transition: var(--animation-3s);
|
transition: var(--animation-3s);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.tool-list-container>.item:active {
|
||||||
|
transform: scale(0.95);
|
||||||
|
transition: var(--animation-3s);
|
||||||
|
}
|
||||||
|
|
||||||
.tool-list-container>.item:hover {
|
.tool-list-container>.item:hover {
|
||||||
background-color: var(--main-light-color);
|
background-color: var(--main-light-color);
|
||||||
transition: var(--animation-3s);
|
transition: var(--animation-3s);
|
||||||
@ -158,4 +174,13 @@ onMounted(async () => {
|
|||||||
opacity: 0.6;
|
opacity: 0.6;
|
||||||
font-size: 12.5px;
|
font-size: 12.5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.empty-description {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
color: var(--el-text-color-placeholder, #bbb);
|
||||||
|
font-size: 15px;
|
||||||
|
min-height: 40px;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
@ -20,8 +20,14 @@
|
|||||||
|
|
||||||
<div v-else>
|
<div v-else>
|
||||||
<!-- 展示原本的信息 -->
|
<!-- 展示原本的信息 -->
|
||||||
<template v-if="!showRawJson">
|
<template v-if="!showRawJson && tabStorage.lastToolCallResponse">
|
||||||
{{tabStorage.lastToolCallResponse?.content.map(c => c.text).join('\n')}}
|
<div
|
||||||
|
v-for="(c, idx) in tabStorage.lastToolCallResponse!.content"
|
||||||
|
:key="idx"
|
||||||
|
class="tool-call-block"
|
||||||
|
>
|
||||||
|
<pre class="tool-call-text">{{ c.text }}</pre>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<!-- 展示 json -->
|
<!-- 展示 json -->
|
||||||
@ -105,4 +111,21 @@ const showRawJson = ref(false);
|
|||||||
padding: 5px 9px;
|
padding: 5px 9px;
|
||||||
border-radius: .5em;
|
border-radius: .5em;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.tool-call-block {
|
||||||
|
margin-bottom: 12px;
|
||||||
|
padding: 10px 12px;
|
||||||
|
background: rgba(0,0,0,0.04);
|
||||||
|
border-radius: 6px;
|
||||||
|
box-shadow: 0 1px 2px rgba(0,0,0,0.03);
|
||||||
|
}
|
||||||
|
|
||||||
|
.tool-call-text {
|
||||||
|
font-family: var(--code-font-family, monospace);
|
||||||
|
font-size: 15px;
|
||||||
|
white-space: pre-wrap;
|
||||||
|
word-break: break-all;
|
||||||
|
margin: 0;
|
||||||
|
color: var(--el-text-color-primary, #222);
|
||||||
|
}
|
||||||
</style>
|
</style>
|
@ -1,8 +1,14 @@
|
|||||||
import type { ToolCallResponse } from '@/hook/type';
|
import type { ToolCallResponse } from '@/hook/type';
|
||||||
|
import type { Edge, Node, NodeDataView } from './auto-detector/diagram';
|
||||||
|
|
||||||
export interface ToolStorage {
|
export interface ToolStorage {
|
||||||
activeNames: any[];
|
activeNames: any[];
|
||||||
currentToolName: string;
|
currentToolName: string;
|
||||||
lastToolCallResponse?: ToolCallResponse | string;
|
lastToolCallResponse?: ToolCallResponse | string;
|
||||||
formData: Record<string, any>;
|
formData: Record<string, any>;
|
||||||
|
autoDetectDiagram?: {
|
||||||
|
edges?: Edge[];
|
||||||
|
views?: NodeDataView[];
|
||||||
|
[key: string]: any;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,6 +11,8 @@ export function getDefaultValue(property: TypeAble): any {
|
|||||||
return false;
|
return false;
|
||||||
} else if (property.type === 'object') {
|
} else if (property.type === 'object') {
|
||||||
return {};
|
return {};
|
||||||
|
} else if (property.type === 'array') {
|
||||||
|
return [];
|
||||||
} else {
|
} else {
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
@ -26,6 +28,8 @@ export function normaliseJavascriptType(type: string) {
|
|||||||
return 'boolean';
|
return 'boolean';
|
||||||
case 'string':
|
case 'string':
|
||||||
return 'string';
|
return 'string';
|
||||||
|
case 'array':
|
||||||
|
return 'array';
|
||||||
default:
|
default:
|
||||||
return type;
|
return type;
|
||||||
}
|
}
|
||||||
|
@ -91,3 +91,15 @@ export function getImageBlobUrlByBase64(base64String: string, mimeType: string,
|
|||||||
}
|
}
|
||||||
return blobUrl;
|
return blobUrl;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function gotoWebsite(url: string) {
|
||||||
|
const platform = getPlatform();
|
||||||
|
const bridge = useMessageBridge();
|
||||||
|
if (platform === 'vscode') {
|
||||||
|
// For VSCode, use the webview API to open external links
|
||||||
|
bridge.commandRequest('vscode/openExternal', { url });
|
||||||
|
} else if (platform === 'web') {
|
||||||
|
// For web, use the standard window.open method
|
||||||
|
window.open(url, '_blank');
|
||||||
|
}
|
||||||
|
}
|
@ -183,5 +183,17 @@
|
|||||||
"export": "تصدير",
|
"export": "تصدير",
|
||||||
"export-filename": "اسم ملف التصدير",
|
"export-filename": "اسم ملف التصدير",
|
||||||
"how-to-use": "كيفية الاستخدام؟",
|
"how-to-use": "كيفية الاستخدام؟",
|
||||||
"is-required": "هو حقل مطلوب"
|
"is-required": "هو حقل مطلوب",
|
||||||
|
"edit-ai-mook-prompt": "تحرير إشارات AI Mook",
|
||||||
|
"start-auto-detect": "بدء عملية الفحص الذاتي",
|
||||||
|
"tool-module": "وحدة الأدوات",
|
||||||
|
"prompt-module": "وحدة المطالبات",
|
||||||
|
"not-select-begin-node": "لم يتم تحديد عقدة البداية",
|
||||||
|
"can-make-loop": "سيؤدي الاتصال إلى تكوين حلقة",
|
||||||
|
"this-is-repeat-connection": "هذا رابط مكرر",
|
||||||
|
"ai-gen-error-json": "خطأ في تحليل JSON الذي تم إنشاؤه بواسطة الذكاء الاصطناعي",
|
||||||
|
"ai-invoke-unknown-tool": "استدعت الذكاء الاصطناعي أداة غير معروفة",
|
||||||
|
"click-edge-to-delete": "انقر على الحافة للحذف",
|
||||||
|
"select-node-define-test-tomo": "اختر عقدة أخرى لتحديد طوبولوجيا الاختبار",
|
||||||
|
"tool-self-detect": "الفحص الذاتي للأداة"
|
||||||
}
|
}
|
@ -183,5 +183,17 @@
|
|||||||
"export": "Exportieren",
|
"export": "Exportieren",
|
||||||
"export-filename": "Exportdateiname",
|
"export-filename": "Exportdateiname",
|
||||||
"how-to-use": "Wie benutzt man?",
|
"how-to-use": "Wie benutzt man?",
|
||||||
"is-required": "ist ein Pflichtfeld"
|
"is-required": "ist ein Pflichtfeld",
|
||||||
|
"edit-ai-mook-prompt": "AI Mook-Prompts bearbeiten",
|
||||||
|
"start-auto-detect": "Selbsttest starten",
|
||||||
|
"tool-module": "Werkzeugmodul",
|
||||||
|
"prompt-module": "Aufforderungsmodul",
|
||||||
|
"not-select-begin-node": "Kein Startknoten ausgewählt",
|
||||||
|
"can-make-loop": "Die Verbindung wird eine Schleife bilden",
|
||||||
|
"this-is-repeat-connection": "Dies ist ein doppelter Link",
|
||||||
|
"ai-gen-error-json": "Fehler beim Parsen von KI-generiertem JSON",
|
||||||
|
"ai-invoke-unknown-tool": "KI hat ein unbekanntes Tool aufgerufen",
|
||||||
|
"click-edge-to-delete": "Klicken Sie auf die Kante, um sie zu löschen",
|
||||||
|
"select-node-define-test-tomo": "Wählen Sie einen anderen Knoten aus, um die Testtopologie zu definieren",
|
||||||
|
"tool-self-detect": "Werkzeug-Selbsttest"
|
||||||
}
|
}
|
@ -183,5 +183,17 @@
|
|||||||
"export": "Export",
|
"export": "Export",
|
||||||
"export-filename": "Export filename",
|
"export-filename": "Export filename",
|
||||||
"how-to-use": "How to use?",
|
"how-to-use": "How to use?",
|
||||||
"is-required": "is a required field"
|
"is-required": "is a required field",
|
||||||
|
"edit-ai-mook-prompt": "Edit AI Mook prompts",
|
||||||
|
"start-auto-detect": "Start self-check",
|
||||||
|
"tool-module": "Tool module",
|
||||||
|
"prompt-module": "Prompt Module",
|
||||||
|
"not-select-begin-node": "No starting node selected",
|
||||||
|
"can-make-loop": "The connection will form a loop",
|
||||||
|
"this-is-repeat-connection": "This is a duplicate link",
|
||||||
|
"ai-gen-error-json": "AI-generated JSON parsing error",
|
||||||
|
"ai-invoke-unknown-tool": "AI called an unknown tool",
|
||||||
|
"click-edge-to-delete": "Click the edge to delete",
|
||||||
|
"select-node-define-test-tomo": "Select another node to define the test topology",
|
||||||
|
"tool-self-detect": "Tool Self-Check"
|
||||||
}
|
}
|
@ -183,5 +183,17 @@
|
|||||||
"export": "Exporter",
|
"export": "Exporter",
|
||||||
"export-filename": "Nom du fichier d'exportation",
|
"export-filename": "Nom du fichier d'exportation",
|
||||||
"how-to-use": "Comment utiliser ?",
|
"how-to-use": "Comment utiliser ?",
|
||||||
"is-required": "est un champ obligatoire"
|
"is-required": "est un champ obligatoire",
|
||||||
|
"edit-ai-mook-prompt": "Modifier les invites AI Mook",
|
||||||
|
"start-auto-detect": "Démarrer l'autovérification",
|
||||||
|
"tool-module": "Module d'outils",
|
||||||
|
"prompt-module": "Module d'invite",
|
||||||
|
"not-select-begin-node": "Aucun nœud de départ sélectionné",
|
||||||
|
"can-make-loop": "La connexion formera une boucle",
|
||||||
|
"this-is-repeat-connection": "Ceci est un lien en double",
|
||||||
|
"ai-gen-error-json": "Erreur d'analyse JSON générée par IA",
|
||||||
|
"ai-invoke-unknown-tool": "L'IA a appelé un outil inconnu",
|
||||||
|
"click-edge-to-delete": "Cliquez sur le bord pour supprimer",
|
||||||
|
"select-node-define-test-tomo": "Sélectionnez un autre nœud pour définir la topologie de test",
|
||||||
|
"tool-self-detect": "Auto-vérification de l'outil"
|
||||||
}
|
}
|
@ -183,5 +183,17 @@
|
|||||||
"export": "エクスポート",
|
"export": "エクスポート",
|
||||||
"export-filename": "エクスポートファイル名",
|
"export-filename": "エクスポートファイル名",
|
||||||
"how-to-use": "使用方法",
|
"how-to-use": "使用方法",
|
||||||
"is-required": "は必須フィールドです"
|
"is-required": "は必須フィールドです",
|
||||||
|
"edit-ai-mook-prompt": "AI Mookプロンプトを編集",
|
||||||
|
"start-auto-detect": "自己診断を開始",
|
||||||
|
"tool-module": "ツールモジュール",
|
||||||
|
"prompt-module": "プロンプトモジュール",
|
||||||
|
"not-select-begin-node": "開始ノードが選択されていません",
|
||||||
|
"can-make-loop": "接続によりループが形成されます",
|
||||||
|
"this-is-repeat-connection": "これは重複したリンクです",
|
||||||
|
"ai-gen-error-json": "AI生成JSONの解析エラー",
|
||||||
|
"ai-invoke-unknown-tool": "AIが未知のツールを呼び出しました",
|
||||||
|
"click-edge-to-delete": "クリックして削除",
|
||||||
|
"select-node-define-test-tomo": "テストトポロジを定義するために別のノードを選択してください",
|
||||||
|
"tool-self-detect": "ツール自己診断"
|
||||||
}
|
}
|
@ -183,5 +183,17 @@
|
|||||||
"export": "내보내기",
|
"export": "내보내기",
|
||||||
"export-filename": "내보내기 파일 이름",
|
"export-filename": "내보내기 파일 이름",
|
||||||
"how-to-use": "사용 방법?",
|
"how-to-use": "사용 방법?",
|
||||||
"is-required": "는 필수 필드입니다"
|
"is-required": "는 필수 필드입니다",
|
||||||
|
"edit-ai-mook-prompt": "AI Mook 프롬프트 편집",
|
||||||
|
"start-auto-detect": "자체 점검 시작",
|
||||||
|
"tool-module": "도구 모듈",
|
||||||
|
"prompt-module": "프롬프트 모듈",
|
||||||
|
"not-select-begin-node": "시작 노드가 선택되지 않았습니다",
|
||||||
|
"can-make-loop": "연결이 루프를 형성합니다",
|
||||||
|
"this-is-repeat-connection": "이것은 중복된 링크입니다",
|
||||||
|
"ai-gen-error-json": "AI 생성 JSON 구문 분석 오류",
|
||||||
|
"ai-invoke-unknown-tool": "AI가 알 수 없는 도구를 호출했습니다",
|
||||||
|
"click-edge-to-delete": "가장자리를 클릭하여 삭제",
|
||||||
|
"select-node-define-test-tomo": "테스트 토폴로지를 정의하려면 다른 노드를 선택하세요",
|
||||||
|
"tool-self-detect": "도구 자체 점검"
|
||||||
}
|
}
|
@ -183,5 +183,17 @@
|
|||||||
"export": "Экспорт",
|
"export": "Экспорт",
|
||||||
"export-filename": "Имя файла экспорта",
|
"export-filename": "Имя файла экспорта",
|
||||||
"how-to-use": "Как использовать?",
|
"how-to-use": "Как использовать?",
|
||||||
"is-required": "является обязательным полем"
|
"is-required": "является обязательным полем",
|
||||||
|
"edit-ai-mook-prompt": "Редактировать подсказки AI Mook",
|
||||||
|
"start-auto-detect": "Запустить самопроверку",
|
||||||
|
"tool-module": "Модуль инструментов",
|
||||||
|
"prompt-module": "Модуль подсказок",
|
||||||
|
"not-select-begin-node": "Начальный узел не выбран",
|
||||||
|
"can-make-loop": "Соединение образует петлю",
|
||||||
|
"this-is-repeat-connection": "Это повторяющаяся ссылка",
|
||||||
|
"ai-gen-error-json": "Ошибка разбора JSON, созданного ИИ",
|
||||||
|
"ai-invoke-unknown-tool": "ИИ вызвал неизвестный инструмент",
|
||||||
|
"click-edge-to-delete": "Нажмите на край, чтобы удалить",
|
||||||
|
"select-node-define-test-tomo": "Выберите другой узел для определения тестовой топологии",
|
||||||
|
"tool-self-detect": "Самопроверка инструмента"
|
||||||
}
|
}
|
@ -183,5 +183,17 @@
|
|||||||
"export": "导出",
|
"export": "导出",
|
||||||
"export-filename": "导出文件名",
|
"export-filename": "导出文件名",
|
||||||
"how-to-use": "如何使用?",
|
"how-to-use": "如何使用?",
|
||||||
"is-required": "是必填字段"
|
"is-required": "是必填字段",
|
||||||
|
"edit-ai-mook-prompt": "编辑 AI Mook 提示词",
|
||||||
|
"start-auto-detect": "开启自检程序",
|
||||||
|
"tool-module": "工具模块",
|
||||||
|
"prompt-module": "提示词模块",
|
||||||
|
"not-select-begin-node": "未选择起始节点",
|
||||||
|
"can-make-loop": "连接会形成环路",
|
||||||
|
"this-is-repeat-connection": "这是一个重复的连接",
|
||||||
|
"ai-gen-error-json": "AI 生成的 JSON 解析错误",
|
||||||
|
"ai-invoke-unknown-tool": "AI 调用了未知的工具",
|
||||||
|
"click-edge-to-delete": "点击边以删除",
|
||||||
|
"select-node-define-test-tomo": "选择另一个节点以定义测试拓扑",
|
||||||
|
"tool-self-detect": "工具自检"
|
||||||
}
|
}
|
@ -183,5 +183,17 @@
|
|||||||
"export": "導出",
|
"export": "導出",
|
||||||
"export-filename": "導出文件名",
|
"export-filename": "導出文件名",
|
||||||
"how-to-use": "如何使用?",
|
"how-to-use": "如何使用?",
|
||||||
"is-required": "是必填欄位"
|
"is-required": "是必填欄位",
|
||||||
|
"edit-ai-mook-prompt": "編輯AI Mook提示詞",
|
||||||
|
"start-auto-detect": "開啟自檢程序",
|
||||||
|
"tool-module": "工具模組",
|
||||||
|
"prompt-module": "提示詞模組",
|
||||||
|
"not-select-begin-node": "未選擇起始節點",
|
||||||
|
"can-make-loop": "連接會形成環路",
|
||||||
|
"this-is-repeat-connection": "這是一個重複的連結",
|
||||||
|
"ai-gen-error-json": "AI 生成的 JSON 解析錯誤",
|
||||||
|
"ai-invoke-unknown-tool": "AI調用了未知的工具",
|
||||||
|
"click-edge-to-delete": "點擊邊緣以刪除",
|
||||||
|
"select-node-define-test-tomo": "選擇另一個節點以定義測試拓撲",
|
||||||
|
"tool-self-detect": "工具自檢"
|
||||||
}
|
}
|
@ -27,7 +27,7 @@
|
|||||||
|
|
||||||
<div style="display: inline-flex;">
|
<div style="display: inline-flex;">
|
||||||
<el-button class="join-qq" type="primary"
|
<el-button class="join-qq" type="primary"
|
||||||
@click="joinQQGroup('https://qm.qq.com/cgi-bin/qm/qr?k=C6ZUTZvfqWoI12lWe7L93cWa1hUsuVT0&jump_from=webapi&authKey=McW6B1ogTPjPDrCyGttS890tMZGQ1KB3QLuG4aqVNRaYp4vlTSgf2c6dMcNjMuBD')">
|
@click="gotoWebsite('https://qm.qq.com/cgi-bin/qm/qr?k=C6ZUTZvfqWoI12lWe7L93cWa1hUsuVT0&jump_from=webapi&authKey=McW6B1ogTPjPDrCyGttS890tMZGQ1KB3QLuG4aqVNRaYp4vlTSgf2c6dMcNjMuBD')">
|
||||||
<span class="iconfont icon-QQ"></span>
|
<span class="iconfont icon-QQ"></span>
|
||||||
{{ t('join-discussion') }}
|
{{ t('join-discussion') }}
|
||||||
</el-button>
|
</el-button>
|
||||||
@ -42,24 +42,17 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
import { gotoWebsite } from '@/hook/util';
|
||||||
import { defineComponent } from 'vue';
|
import { defineComponent } from 'vue';
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
|
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
|
|
||||||
const version = '0.1.8';
|
const version = '0.1.9';
|
||||||
const author = 'LSTM-Kirigaya (锦恢)';
|
const author = 'LSTM-Kirigaya (锦恢)';
|
||||||
|
|
||||||
defineComponent({ name: 'about' });
|
defineComponent({ name: 'about' });
|
||||||
|
|
||||||
function joinQQGroup(url: string) {
|
|
||||||
window.open(url, '_blank');
|
|
||||||
}
|
|
||||||
|
|
||||||
function gotoWebsite(url: string) {
|
|
||||||
window.open(url, '_blank');
|
|
||||||
}
|
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
|
@ -108,6 +108,7 @@ function chooseDebugMode(index: number) {
|
|||||||
.welcome-container > span {
|
.welcome-container > span {
|
||||||
flex: 1 1 calc(50% - 100px);
|
flex: 1 1 calc(50% - 100px);
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
|
transition: .3s transform ease-in-out;
|
||||||
}
|
}
|
||||||
|
|
||||||
.debug-option {
|
.debug-option {
|
||||||
|
@ -32,6 +32,11 @@ export function onGeneralColorChange(colorString: string) {
|
|||||||
|
|
||||||
document?.documentElement.style.setProperty(
|
document?.documentElement.style.setProperty(
|
||||||
'--main-light-color', `rgba(${r}, ${g}, ${b}, 0.7)`);
|
'--main-light-color', `rgba(${r}, ${g}, ${b}, 0.7)`);
|
||||||
|
|
||||||
|
for (let i = 1; i <= 9; ++ i) {
|
||||||
|
document?.documentElement.style.setProperty(
|
||||||
|
`--main-light-color-${i}0`, `rgba(${r}, ${g}, ${b}, 0.${i})`);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const predefinedColors = [
|
export const predefinedColors = [
|
||||||
|
2215
resources/changelog/index.html
Normal file
2215
resources/changelog/index.html
Normal file
File diff suppressed because one or more lines are too long
@ -105,6 +105,12 @@ export function revealOpenMcpWebviewPanel(
|
|||||||
exportFile(data.filename, data.content);
|
exportFile(data.filename, data.content);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case 'vscode/openExternal':
|
||||||
|
if (data.url) {
|
||||||
|
vscode.env.openExternal(vscode.Uri.parse(data.url));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
case 'vscode/clipboard/writeText':
|
case 'vscode/clipboard/writeText':
|
||||||
vscode.env.clipboard.writeText(data.text);
|
vscode.env.clipboard.writeText(data.text);
|
||||||
break;
|
break;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user