refactor(layout): 重构布局组件并添加新功能
- 更新 Header 组件,增加项目标题和历史记录切换按钮 - 新增 DataNavigation 组件用于数据导航 - 添加 Playground 相关新组件,包括数据、场景、团队等信息展示 - 重构 Layout 组件,使用 Context 管理历史记录状态 - 更新 zh/option.json 文件,添加新的项目标题和对话相关翻译
BIN
operation/1.png
Normal file
After Width: | Height: | Size: 659 KiB |
BIN
operation/2.png
Normal file
After Width: | Height: | Size: 763 KiB |
BIN
operation/3.png
Normal file
After Width: | Height: | Size: 941 KiB |
BIN
operation/4.png
Normal file
After Width: | Height: | Size: 610 KiB |
1
src/assets/icons/a.svg
Normal file
@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" class="styles__StyledSVGIconPathComponent-sc-i3aj97-0 bxMexi svg-icon-path-icon" viewBox="0 0 64 64" width="24" height="24"><defs></defs><g><path fill="#3e4347" d="M64 57.6c0 3.5-2.9 6.4-6.4 6.4H6.4C2.9 64 0 61.1 0 57.6V6.4C0 2.9 2.9 0 6.4 0h51.2C61.1 0 64 2.9 64 6.4z"></path><circle cx="32" cy="32" r="32" fill="#42ade2"></circle><path fill="#fff" d="M32 40.4c-4.6 0-8.4-3.8-8.4-8.4s3.8-8.4 8.4-8.4s8.4 3.8 8.4 8.4s-3.8 8.4-8.4 8.4"></path><path fill="#3e4347" d="M38.4 24c-3.5 0-6.4 2.9-6.4 6.4v3.2c0 3.5 2.9 6.4 6.4 6.4H64V24z"></path></g></svg>
|
After Width: | Height: | Size: 590 B |
1
src/assets/icons/b.svg
Normal file
@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" class="styles__StyledSVGIconPathComponent-sc-i3aj97-0 bxMexi svg-icon-path-icon" viewBox="0 0 64 64" width="24" height="24"><defs></defs><g><g fill="#333"><path d="M0 0h4v64H0z"></path><path d="M0 60h64v4H0z"></path></g><path fill="#fb4f00" d="M38.7 60h12V6.7L38.7 20z"></path><path fill="#5c750a" d="M21.3 60h12V20l-12 13.3z"></path><path fill="#106995" d="M4 60h12V33.3L4 46.7z"></path><path fill="#9aca0a" d="M33.3 20h13.3v40H33.3z"></path><path fill="#21adf1" d="M16 33.3h13.3V60H16z"></path><path fill="#fc9100" d="M50.7 6.7H64V60H50.7z"></path></g></svg>
|
After Width: | Height: | Size: 601 B |
1
src/assets/icons/bulb.svg
Normal file
@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" class="styles__StyledSVGIconPathComponent-sc-i3aj97-0 bxMexi svg-icon-path-icon" viewBox="0 0 64 64" width="24" height="24"><defs></defs><g><path fill="#616466" d="M28 58c0 2.2 1.8 4 4 4s4-1.8 4-4z"></path><path fill="#ffce31" d="M24.9 48H39c.8-4.3 3.5-8.5 6.3-12.9C48.6 30 52 24.7 52 19.6C52 9.9 43 2 32 2S12 9.9 12 19.6c0 5.1 3.4 10.4 6.6 15.5c2.8 4.4 5.5 8.6 6.3 12.9"></path><path fill="#c79127" d="M26.4 33.6c.1.6.3 1.2.4 1.8c.3 1.1.5 2.1.8 3.2c.9 3.8 1.7 7 2.4 9.5h.6c-.5-2.5-1.2-5.8-2.1-9.6c-.2-1-.5-2.1-.7-3.2c-.1-.5-.2-1.1-.4-1.6c.8-.2 2.7-.8 4.6-2.9c1.9 2.1 3.8 2.7 4.6 2.9c-.1.6-.2 1.1-.4 1.6c-.2 1.1-.5 2.2-.7 3.2c-.9 3.8-1.6 7.1-2.1 9.6h.6c.6-2.5 1.5-5.7 2.4-9.5c.2-1 .5-2.1.8-3.2c.1-.6.3-1.2.4-1.8c.8-.1 1.5-.3 2-.8c.6-.6.9-1.3.7-2.1c-.1-.4-.3-.9-.9-1c-.3-.1-.5-.1-.8 0s-.5.3-.6.4c-.5.6-.7 1.2-.9 1.8l-.3.9s-.8.1-2.7-1.2c-1-.7-1.3-1.2-1.6-1.5c.3-.4.6-.7.9-1.2c.1-.3.2-.5.3-.9c0-.3 0-.7-.2-1s-.4-.6-.9-.8c-.2-.1-.4-.1-.6-.1s-.4 0-.6.1c-.4.2-.7.5-.9.8s-.2.7-.2 1q0 .45.3.9q.3.75.9 1.2c-.3.3-.6.8-1.6 1.5c-1.8 1.2-2.7 1.2-2.7 1.2l-.3-.9c-.2-.6-.4-1.2-.9-1.8c-.1-.1-.3-.3-.6-.4s-.6-.1-.8 0c-.6.2-.8.7-.9 1c-.1.8.2 1.5.7 2.1c.6.5 1.3.7 2 .8M38 32.1c.2-.5.4-1.1.8-1.4c.1-.1.2-.1.2-.1h.2c.1 0 .2.1.3.4c.1.4-.1 1-.5 1.3c-.3.2-.7.4-1.1.5c0-.3 0-.5.1-.7m-6.6-4.8c.2-.2.4-.4.6-.4s.4.1.6.4s.1.7-.1 1.1c-.1.3-.3.6-.5.8c-.2-.3-.4-.5-.5-.8c-.2-.3-.3-.7-.1-1.1m-6.8 3.5c0-.2.1-.3.3-.4h.2s.1.1.2.1c.3.3.6.9.8 1.4c.1.2.1.4.2.6c-.4-.1-.8-.2-1.1-.5c-.4-.2-.6-.7-.6-1.2"></path><path fill="#94989b" d="M24.9 50h14.3v1.8H24.9zm1 3.6h12.3v1.8H25.9z"></path><path fill="#616466" d="M25.9 51.8h12.3v1.8H25.9z"></path><path fill="#94989b" d="m39.2 50l-13.3 3.6v1.9l13.3-3.7zm-12.3 7.3h10.3v1.8H26.9z"></path><path fill="#616466" d="M26.9 55.5h10.3v1.8H26.9z"></path><path fill="#94989b" d="m38.2 53.6l-11.3 3.7v1.9l11.3-3.7z"></path></g></svg>
|
After Width: | Height: | Size: 1.8 KiB |
1
src/assets/icons/c.svg
Normal file
@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" class="styles__StyledSVGIconPathComponent-sc-i3aj97-0 bxMexi svg-icon-path-icon" viewBox="0 0 64 64" width="24" height="24"><defs></defs><g><path fill="#d0d0d0" d="M23.6 36.7H2.9l10.3-26.2zm-18-1.9h15.3l-7.7-19.5zm55.5 1.9H40.4l10.3-26.2zm-18-1.9h15.3l-7.7-19.5z"></path><path fill="#b8c2c4" d="M50.3 10.2s-2.2-.8-3.4-1.3C42.6 7.1 37.3 5 32 5S21.4 7.2 17.1 9c-1.2.5-3.4 1.3-3.4 1.3l-3.3 2.5s2.7 1.7 4 1.2c1.1-.4 2.2-.9 3.5-1.4C22 10.9 27.1 8.8 32 8.8s10 2.1 14.1 3.8c1.2.5 2.4 1 3.5 1.4c1.4.5 4-1.2 4-1.2z"></path><path fill="#428bc1" d="M2 34.8C2 41 7 46 13.2 46s11.2-5 11.2-11.2zm37.5 0c0 6.2 5 11.2 11.2 11.2S62 41 62 34.8z"></path><path fill="#b8c2c4" d="M30.1 12.3h3.8v41.2h-3.8z"></path><path fill="#d0d0d0" d="M29 18.6h6.1v34.9H29z"></path><path fill="#545b60" d="M27.7 36.7h8.6v19.7h-8.6z"></path><circle cx="13.2" cy="13.2" r="3.8" fill="#dbb471"></circle><g fill="#545b60"><circle cx="50.8" cy="13.2" r="3.8"></circle><circle cx="13.2" cy="13.2" r="3.8"></circle></g><g fill="#fff"><circle cx="13.2" cy="13.2" r="1.9"></circle><circle cx="50.8" cy="13.2" r="1.9"></circle></g><g fill="#d0d0d0"><circle cx="32" cy="7.6" r="5.6"></circle><path d="M32 45.1c-8.3 0-15 4.2-15 9.4h30c0-5.2-6.7-9.4-15-9.4"></path></g><path fill="#545b60" d="M15.1 54.5h33.8v3.8H15.1z"></path><path fill="#6b767c" d="M11.4 58.2h41.2V62H11.4z"></path></g></svg>
|
After Width: | Height: | Size: 1.4 KiB |
1
src/assets/icons/d.svg
Normal file
@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" class="styles__StyledSVGIconPathComponent-sc-i3aj97-0 bxMexi svg-icon-path-icon" viewBox="0 0 64 64" width="24" height="24"><defs></defs><g><path fill="#979797" d="M64 31.7h-5.6c-7.3-5-11.9-13.2-12.4-22.1l2.8-5l-1.6-1l-2.8 5c-7.8 4-17 4-24.8 0l-2.8-5l-1.6 1l2.8 5c-.5 8.9-5.1 17.2-12.4 22.1H0v1.9h5.6c7.3 4.9 11.9 13.2 12.4 22.1l-2.8 5l1.6 1l2.8-5c7.8-4 17-4 24.8 0l2.8 5l1.6-1l-2.8-5c.5-8.9 5.1-17.2 12.4-22.1H64zm-8.7 0h-5.5c-4.7-3.2-7.8-8.6-8.1-14.4l2.8-4.9c1 7.6 4.9 14.5 10.8 19.3m-23.3-1l-2.3-4.1c.7.2 1.5.3 2.3.3s1.6-.1 2.3-.3zm3.9-3.1c.5 1.5 1.3 2.9 2.3 4.1h-4.6zm-5.5 4.1h-4.6c1-1.2 1.8-2.6 2.3-4.1zm0 1.9l-2.3 4.1c-.5-1.5-1.3-2.9-2.3-4.1zm1.6.9l2.3 4.1c-1.5-.3-3.1-.3-4.6 0zm1.6-.9h4.6c-1 1.2-1.8 2.6-2.3 4.1zm7.4-1.9c-2.1-1.5-3.5-3.9-3.7-6.6l2.9-5.1c.8 4.5 3.1 8.7 6.5 11.7zm-5.3-7.6c-2.3 1.2-5.1 1.2-7.4 0L25.5 19c2.1.8 4.3 1.1 6.5 1.1s4.4-.4 6.5-1.1zm-9 .9c-.2 2.7-1.5 5.1-3.7 6.6h-5.7c3.4-3 5.7-7.1 6.5-11.7zM23 33.6c2.1 1.5 3.5 3.9 3.7 6.6l-2.9 5.1c-.8-4.5-3.1-8.7-6.5-11.7zm5.3 7.5c2.3-1.2 5.1-1.2 7.4 0l2.9 5.1c-4.2-1.5-8.9-1.5-13.1 0zm9-.9c.2-2.7 1.5-5.1 3.7-6.6h5.7c-3.4 3-5.7 7.1-6.5 11.7zM32 13.5c3.7 0 7.4-.7 10.8-2.1L40 16.3c-5.1 2.6-11.1 2.6-16.1 0l-2.8-4.9c3.5 1.4 7.2 2.1 10.9 2.1m-12.4-1.1l2.8 4.9c-.4 5.8-3.4 11.2-8.1 14.4H8.7c5.9-4.8 9.8-11.7 10.9-19.3M8.8 33.6h5.5c4.7 3.2 7.7 8.5 8 14.4l-2.8 4.9c-1-7.6-4.9-14.6-10.7-19.3m12.4 20.2l2.8-4.9c5.1-2.6 11.1-2.6 16.1 0l2.8 4.9c-7-2.8-14.8-2.8-21.7 0m23.2-.9L41.7 48c.3-5.8 3.3-11.2 8.1-14.4h5.5c-5.9 4.7-9.8 11.7-10.9 19.3"></path></g></svg>
|
After Width: | Height: | Size: 1.5 KiB |
1
src/assets/icons/e.svg
Normal file
@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" class="styles__StyledSVGIconPathComponent-sc-i3aj97-0 bxMexi svg-icon-path-icon" viewBox="0 0 64 64" width="24" height="24"><defs></defs><g><path fill="#212528" d="M21.6 16.1c0 1.2-1 2.2-2.2 2.2H9.7c-1.2 0-2.2-1-2.2-2.2v-2.7c0-1.2 1-2.2 2.2-2.2h9.7c1.2 0 2.2 1 2.2 2.2zm39.1-.2c0 .6-.5 1.2-1.2 1.2h-5.2c-.6 0-1.2-.5-1.2-1.2v-1.4c0-.6.5-1.2 1.2-1.2h5.2c.6 0 1.2.5 1.2 1.2zM64 50.3c0 3-2.4 5.4-5.4 5.4H5.4c-3 0-5.4-2.4-5.4-5.4v-1.6h64z"></path><path fill="#51575b" d="M0 20.1c0-3 2.4-5.4 5.4-5.4h53.1c3 0 5.4 2.4 5.4 5.4v1.6H0z"></path><path fill="#3e4347" d="M0 21.5h64v28.3H0z"></path><path fill="#51575b" d="M54.7 18H22.6l3.2-10.8c.3-.6 1.6-1.6 2.4-1.8C33.1 4 44.1 4 49 5.4c.8.2 2.1 1.2 2.4 1.8z"></path><path fill="#3e4347" d="M53.1 29.6h-29L27 15c.2-.8 1.5-2.1 2.2-2.4c4.4-1.8 14.4-1.8 18.8 0c.7.3 1.9 1.6 2.2 2.4z"></path><path fill="#788287" d="M60.6 37.6c0 12.2-9.9 22-22 22s-22-9.8-22-22c0-12.1 9.9-22 22-22c12.2 0 22 9.9 22 22"></path><path fill="#212528" d="M58.2 37.6c0 10.8-8.8 19.6-19.6 19.6S19 48.4 19 37.6S27.8 18 38.6 18c10.8.1 19.6 8.8 19.6 19.6"></path><circle cx="38.6" cy="37.6" r="15.9" fill="#3e4347"></circle><circle cx="38.6" cy="37.6" r="8.6" fill="#212528"></circle><g fill="#f5f5f5"><path d="M50.3 30.9c0 2.7-2.2 4.9-4.9 4.9s-4.9-2.2-4.9-4.9s2.2-4.9 4.9-4.9c2.7-.1 4.9 2.2 4.9 4.9" opacity=".5"></path><circle cx="35.6" cy="40.7" r="3.1" opacity=".5"></circle><circle cx="30.1" cy="46.2" r="1.9" opacity=".5"></circle></g><path fill="#636c72" d="M15 45.3c0 1.2-1 2.2-2.2 2.2H3.6c-1.2 0-2.2-1-2.2-2.2V25.9c0-1.2 1-2.2 2.2-2.2h9.2c1.2 0 2.2 1 2.2 2.2z"></path><circle cx="10.1" cy="18.6" r="3.2" fill="#212528"></circle></g></svg>
|
After Width: | Height: | Size: 1.7 KiB |
1
src/assets/icons/eye.svg
Normal file
@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" class="styles__StyledSVGIconPathComponent-sc-i3aj97-0 bxMexi svg-icon-path-icon" viewBox="0 0 64 64" width="24" height="24"><defs></defs><g><path fill="#231f20" d="M62 32S51.9 52 32 52S2 32 2 32s10.1-20 30-20s30 20 30 20"></path><path fill="#fff" d="M57 32s-8.4 16.7-25 16.7S7 32 7 32s8.4-16.7 25-16.7S57 32 57 32"></path><path fill="#42ade2" d="M45.4 32c0 7.5-6 13.5-13.5 13.5s-13.5-6-13.5-13.5s6-13.5 13.5-13.5s13.5 6 13.5 13.5"></path><path fill="#231f20" d="M39.4 32c0 4.1-3.4 7.5-7.5 7.5s-7.5-3.4-7.5-7.5s3.4-7.5 7.5-7.5s7.5 3.4 7.5 7.5"></path></g></svg>
|
After Width: | Height: | Size: 601 B |
1
src/assets/icons/f.svg
Normal file
@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" class="styles__StyledSVGIconPathComponent-sc-i3aj97-0 bxMexi svg-icon-path-icon" viewBox="0 0 64 64" width="24" height="24"><defs></defs><g><path fill="#0071bc" d="M57.9 32c.2-.2.3-.4.5-.6c3.9-5.6 4.7-10.7 2.3-14.5c-2.2-3.5-7-5.5-13.4-5.5c-1.1 0-2.3.1-3.5.2C40.9 5.7 36.7 2 32 2s-8.9 3.7-11.8 9.6c-1.2-.1-2.4-.2-3.5-.2c-6.4 0-11.2 1.9-13.4 5.5c-2.4 3.8-1.6 9 2.3 14.5c.2.2.4.4.5.6c-.2.2-.3.4-.5.6C1.8 38.2 1 43.4 3.4 47.2c2.2 3.5 7 5.5 13.4 5.5c1.1 0 2.3-.1 3.5-.2c2.8 5.8 7 9.5 11.7 9.5s8.9-3.7 11.8-9.6c1.2.1 2.4.2 3.5.2c6.4 0 11.2-1.9 13.4-5.5c2.4-3.8 1.6-9-2.3-14.5c-.2-.2-.4-.4-.5-.6M47.2 13.1c5.7 0 9.9 1.6 11.8 4.6c2 3.2 1.2 7.7-2.3 12.7l-.1.1c-2.5-3-5.8-6-9.7-8.6c-.6-3.2-1.4-6.1-2.5-8.7c1-.1 2-.1 2.8-.1m-7.4 31.3c-2.6 1.4-5.2 2.5-7.8 3.5c-2.6-1-5.3-2.1-7.8-3.5c-1.9-1-3.7-2.1-5.5-3.3c-.5-2.9-.8-6-.8-9.2s.3-6.3.8-9.2c1.7-1.2 3.5-2.2 5.5-3.3c2.6-1.4 5.2-2.5 7.8-3.5c2.6 1 5.3 2.1 7.8 3.5c1.9 1 3.7 2.1 5.5 3.3c.5 2.9.8 6 .8 9.2s-.3 6.3-.8 9.2c-1.7 1.2-3.6 2.3-5.5 3.3m5-.8c-.6 2.5-1.3 4.9-2.3 7c-2.6-.3-5.2-.9-7.9-1.8c2.1-.8 4.1-1.8 6.1-2.8c1.5-.8 2.8-1.6 4.1-2.4m-15.4 5.2c-2.7.8-5.4 1.4-7.9 1.8c-.9-2.1-1.7-4.4-2.3-7c1.3.8 2.6 1.6 4 2.3c2.1 1.1 4.1 2 6.2 2.9m-12.8-9.1c-3.2-2.4-6-5-8.1-7.7c2.1-2.7 4.9-5.3 8.1-7.7c-.3 2.4-.5 5-.5 7.7s.1 5.2.5 7.7m2.6-19.3c.6-2.5 1.3-4.9 2.3-7c2.6.3 5.2.9 7.9 1.8c-2.1.8-4.1 1.8-6.1 2.9c-1.5.7-2.8 1.5-4.1 2.3m15.4-5.2c2.7-.8 5.4-1.4 7.9-1.8c.9 2.1 1.7 4.4 2.3 7c-1.3-.8-2.6-1.6-4-2.3c-2.1-1.1-4.1-2-6.2-2.9m12.8 9.1c3.2 2.4 6 5 8.1 7.7c-2.1 2.7-4.9 5.3-8.1 7.7c.3-2.4.5-5 .5-7.7s-.1-5.2-.5-7.7M32 3.9c3.8 0 7.2 3 9.8 7.9c-3.1.5-6.4 1.3-9.8 2.5c-3.3-1.2-6.6-2-9.8-2.5c2.6-4.9 6-7.9 9.8-7.9M7.2 30.4c-3.5-5-4.3-9.5-2.3-12.7c1.9-3 6.1-4.6 11.8-4.6c.9 0 1.8 0 2.7.1c-1.1 2.6-1.9 5.5-2.5 8.7c-3.8 2.6-7.1 5.6-9.7 8.5m9.6 20.5c-5.7 0-9.9-1.6-11.8-4.6c-2-3.2-1.2-7.7 2.3-12.7c0 0 0-.1.1-.1c2.5 3 5.8 6 9.7 8.6c.6 3.2 1.4 6.1 2.5 8.7c-1 .1-1.9.1-2.8.1M32 60.1c-3.8 0-7.2-3-9.8-7.9c3.1-.5 6.4-1.3 9.8-2.5c3.3 1.2 6.6 2 9.8 2.5c-2.6 4.9-6 7.9-9.8 7.9m27-13.8c-1.9 3-6.1 4.6-11.8 4.6c-.9 0-1.8 0-2.7-.1c1.1-2.6 1.9-5.5 2.5-8.7c3.8-2.6 7.1-5.6 9.7-8.6c0 0 0 .1.1.1c3.5 5 4.3 9.5 2.2 12.7"></path><circle cx="32" cy="32" r="5.6" fill="#ed4c5c"></circle></g></svg>
|
After Width: | Height: | Size: 2.2 KiB |
1
src/assets/icons/rocket.svg
Normal file
@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" class="styles__StyledSVGIconPathComponent-sc-i3aj97-0 bxMexi svg-icon-path-icon" viewBox="0 0 64 64" width="24" height="24"><defs></defs><g><g fill="#ff9d27"><path d="M10.9 48.7c4-4 4.4-5 6.9-2.5s1.5 2.8-2.5 6.9c-3 3-6.8 2.4-6.8 2.4s-.6-3.8 2.4-6.8"></path><path d="M18.5 52.8c1.6-4.2 2.1-4.7-.2-6s-2.3-.4-3.8 3.8c-1.2 3.1.2 5.9.2 5.9s2.7-.5 3.8-3.7"></path></g><path fill="#fdf516" d="M16.2 48.9c.9-2.3.9-2.8 2.1-2.1c1.3.7 1 1 .1 3.3c-.6 1.7-2.1 2.1-2.1 2.1s-.7-1.5-.1-3.3"></path><path fill="#ff9d27" d="M17.1 45.7c-1.3-2.3-1.8-1.8-6-.2c-3.1 1.2-3.7 3.8-3.7 3.8s2.8 1.4 5.9.2c4.2-1.6 5.1-1.6 3.8-3.8"></path><g fill="#fdf516"><path d="M15 47.8c2.3-.9 2.8-.9 2.1-2.1c-.7-1.3-1-1-3.3-.1c-1.7.6-2.1 2.1-2.1 2.1s1.6.7 3.3.1"></path><path d="M13.9 47.6c2.2-2.2 2.4-2.8 3.8-1.4s.8 1.6-1.4 3.8c-1.7 1.7-3.8 1.3-3.8 1.3s-.2-2 1.4-3.7"></path></g><path fill="#3baacf" d="M18.5 38C12.3 27.6 2 31.9 2 31.9s14.7-14.7 24.6-4.8z"></path><path fill="#428bc1" d="m23.3 30.3l3.2-3.2C16.7 17.2 2 31.9 2 31.9s12.9-9.2 21.3-1.6"></path><path fill="#3baacf" d="M26 45.5C36.4 51.7 32.1 62 32.1 62s14.7-14.7 4.8-24.6z"></path><path fill="#428bc1" d="m33.7 40.7l3.2-3.2c9.9 9.9-4.8 24.6-4.8 24.6s9.2-13 1.6-21.4"></path><path fill="#c5d0d8" d="M48.8 30.9C37.1 42.5 24.2 48.8 19.7 44.3s1.8-17.4 13.4-29.1c13.6-13.6 28.7-13 28.7-13s.5 15.1-13 28.7"></path><path fill="#dae3ea" d="M45.8 27.6C34.2 39.2 22.6 46.8 19.9 44.1s4.9-14.3 16.5-25.9C50 4.6 62 2 62 2s-2.6 12-16.2 25.6"></path><path fill="#c94747" d="M24.3 47.5c-.5.5-1.3.5-1.8 0l-6-6c-.5-.5-.5-1.4 0-1.9l1.8-1.8l7.8 7.8z"></path><path fill="#f15744" d="M22.6 45.7c-.5.5-1.1.7-1.4.4l-3.4-3.4c-.3-.3-.1-.9.4-1.4l1.8-1.8l4.4 4.4z"></path><path fill="#3e4347" d="M20.9 48.2c-.3.3-1 .3-1.3 0l-3.9-3.9c-.3-.3-.2-.9.1-1.2l1.2-1.2l5.1 5.1z"></path><path fill="#62727a" d="M20.1 47.4c-.3.3-.9.4-1.1.2l-2.7-2.7c-.2-.2-.1-.7.3-1l1.2-1.2l3.5 3.5z"></path><path fill="#c94747" d="M61.8 2.2S56.4 2 49.1 4.8l10.1 10.1C62 7.6 61.8 2.2 61.8 2.2"></path><path fill="#f15744" d="M61.8 2.2s-4.3.9-10.8 4.6l6.2 6.2c3.7-6.5 4.6-10.8 4.6-10.8"></path><circle cx="43.5" cy="20.5" r="5" fill="#edf4f9"></circle><circle cx="43.5" cy="20.5" r="3.3" fill="#3baacf"></circle><circle cx="33.5" cy="30.5" r="5" fill="#edf4f9"></circle><circle cx="33.5" cy="30.5" r="3.3" fill="#3baacf"></circle><g fill="#fff"><path d="M48.9 6.9c-.3.3-.9.3-1.2 0s-.3-.9 0-1.2s.9-.3 1.2 0s.3.9 0 1.2"></path><circle cx="50.6" cy="8.6" r=".8"></circle><circle cx="53" cy="11" r=".8"></circle><circle cx="55.3" cy="13.4" r=".8"></circle><circle cx="57.7" cy="15.7" r=".8"></circle></g></g></svg>
|
After Width: | Height: | Size: 2.6 KiB |
@ -1,16 +1,17 @@
|
|||||||
{
|
{
|
||||||
"newChat": "新聊天",
|
"projectTitle": "数联网科创智能体",
|
||||||
|
"newChat": "新对话",
|
||||||
"selectAPrompt": "选择一个提示词",
|
"selectAPrompt": "选择一个提示词",
|
||||||
"githubRepository": "GitHub 仓库",
|
"githubRepository": "GitHub 仓库",
|
||||||
"settings": "设置",
|
"settings": "设置",
|
||||||
"metering": "计量",
|
"metering": "计量",
|
||||||
"sidebarTitle": "聊天历史",
|
"sidebarTitle": "对话历史",
|
||||||
"error": "错误",
|
"error": "错误",
|
||||||
"somethingWentWrong": "出现了错误",
|
"somethingWentWrong": "出现了错误",
|
||||||
"validationSelectModel": "请选择一个模型以继续",
|
"validationSelectModel": "请选择一个模型以继续",
|
||||||
"deleteHistoryConfirmation": "你确定要删除这个历史记录吗?",
|
"deleteHistoryConfirmation": "你确定要删除这个历史记录吗?",
|
||||||
"editHistoryTitle": "输入一个新的标题",
|
"editHistoryTitle": "输入一个新的标题",
|
||||||
"temporaryChat": "临时聊天",
|
"temporaryChat": "临时对话",
|
||||||
"more": {
|
"more": {
|
||||||
"copy": {
|
"copy": {
|
||||||
"group": "复制",
|
"group": "复制",
|
||||||
|
38
src/components/Common/DataNavigation.tsx
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
import React from "react";
|
||||||
|
import { Typography, Button } from "antd";
|
||||||
|
import { AcademicCapIcon, ChevronRightIcon } from "@heroicons/react/24/outline";
|
||||||
|
|
||||||
|
const { Title } = Typography;
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
Header: React.ReactNode;
|
||||||
|
showButton?: boolean;
|
||||||
|
onClick?: () => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const DataNavigation: React.FC<Props> = ({ Header, showButton = true, onClick }) => {
|
||||||
|
return (
|
||||||
|
<div className="flex items-center justify-between bg-white dark:bg-gray-800 rounded-lg mb-3">
|
||||||
|
{/* 左侧部分 */}
|
||||||
|
<div className="flex items-center">
|
||||||
|
<Title
|
||||||
|
level={4}
|
||||||
|
style={{ marginBottom: 0, color: "#1F2937", fontSize: "16px" }}>
|
||||||
|
{Header}
|
||||||
|
</Title>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* 右侧部分 */}
|
||||||
|
{showButton && (
|
||||||
|
<Button
|
||||||
|
color="default"
|
||||||
|
variant="link"
|
||||||
|
style={{ gap: 4 }}
|
||||||
|
onClick={onClick}>
|
||||||
|
<span className="text-sm">更多</span>
|
||||||
|
<ChevronRightIcon className="w-4 h-4" />
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
};
|
@ -12,7 +12,7 @@ import { preprocessLaTeX } from "@/utils/latex"
|
|||||||
|
|
||||||
function Markdown({
|
function Markdown({
|
||||||
message,
|
message,
|
||||||
className = "prose break-words dark:prose-invert prose-p:leading-relaxed prose-pre:p-0 dark:prose-dark"
|
className = "prose-lg break-words dark:prose-invert prose-p:leading-relaxed prose-pre:p-0 dark:prose-dark"
|
||||||
}: {
|
}: {
|
||||||
message: string
|
message: string
|
||||||
className?: string
|
className?: string
|
||||||
|
175
src/components/Common/Playground/Data.tsx
Normal file
@ -0,0 +1,175 @@
|
|||||||
|
import React from "react"
|
||||||
|
import { DataNavigation } from "@/components/Common/DataNavigation.tsx"
|
||||||
|
import { Card, Drawer, List } from "antd"
|
||||||
|
import { useCallback, useState } from "react"
|
||||||
|
|
||||||
|
export const PlaygroundData = () => {
|
||||||
|
// 模拟数据
|
||||||
|
const data: {
|
||||||
|
title: string
|
||||||
|
description: string
|
||||||
|
time: string
|
||||||
|
metadata?: string
|
||||||
|
}[] = [
|
||||||
|
{
|
||||||
|
title: "2019-2024年黄海清浅海域中河湖代数生物物种数据集",
|
||||||
|
description:
|
||||||
|
"数字对象标识: CSTR:13452.11.01.11.2021.242 国家海洋科学数据中心",
|
||||||
|
time: "包括2019年8月,2021年8月和2024年6月",
|
||||||
|
metadata: "热 榜 第"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "祁连山老虎沟大本营10米气象每日值数据集(V1.0)(2018-2023)",
|
||||||
|
description:
|
||||||
|
"中国科学院西北生态环境资源研究院,2021年8月3日发布,2021年8月3日20:48更新",
|
||||||
|
time: "包括2019年8月,2021年8月和2024年6月",
|
||||||
|
metadata: "热 榜 第"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "李嘉图为研究老虎沟大本营2014-2018年...",
|
||||||
|
description:
|
||||||
|
"中国科学院西北生态环境资源研究院,2021年8月3日发布,2021年8月3日20:48更新",
|
||||||
|
time: "包括2019年8月,2021年8月和2024年6月",
|
||||||
|
metadata: "热 榜 第"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "青海玉树B1区俄日矿勘探数据2017-2023",
|
||||||
|
description:
|
||||||
|
"数字中国集团,CSTR:3260.11.1528414774204895456,DT2023年地质勘探补充调查",
|
||||||
|
time: "包括2019年8月,2021年8月和2024年6月",
|
||||||
|
metadata: "热 榜 第"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
for (let i = 0; i < 10; i++) {
|
||||||
|
data.push({
|
||||||
|
title: "中国资源环境网",
|
||||||
|
description: "中国资源环境网,2021年8月3日发布,2021年8月3日20:48更新",
|
||||||
|
time: "包括2019年8月,2021年8月和2024年6月"
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const [open, setOpen] = useState(false)
|
||||||
|
|
||||||
|
const showDrawer = () => {
|
||||||
|
setOpen(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
const onClose = () => {
|
||||||
|
setOpen(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="flex flex-col h-full overflow-y-hidden">
|
||||||
|
{/* 数据导航 */}
|
||||||
|
<DataNavigation
|
||||||
|
Header={
|
||||||
|
<div className="flex items-center text-[#1d4eD8] gap-1">
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
className="styles__StyledSVGIconPathComponent-sc-i3aj97-0 bxMexi svg-icon-path-icon"
|
||||||
|
viewBox="0 0 32 32"
|
||||||
|
width="20"
|
||||||
|
height="20">
|
||||||
|
<defs></defs>
|
||||||
|
<g>
|
||||||
|
<circle cx="22" cy="24" r="2" fill="rgb(29, 78, 216)"></circle>
|
||||||
|
<path
|
||||||
|
fill="rgb(29, 78, 216)"
|
||||||
|
d="M29.777 23.479A8.64 8.64 0 0 0 22 18a8.64 8.64 0 0 0-7.777 5.479L14 24l.223.522A8.64 8.64 0 0 0 22 30a8.64 8.64 0 0 0 7.777-5.478L30 24zM22 28a4 4 0 1 1 4-4a4.005 4.005 0 0 1-4 4M7 17h5v2H7zm0-5h12v2H7zm0-5h12v2H7z"></path>
|
||||||
|
<path
|
||||||
|
fill="rgb(29, 78, 216)"
|
||||||
|
d="M22 2H4a2.006 2.006 0 0 0-2 2v24a2.006 2.006 0 0 0 2 2h8v-2H4V4h18v11h2V4a2.006 2.006 0 0 0-2-2"></path>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
相关数据
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
onClick={showDrawer}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{/* 数据列表 */}
|
||||||
|
<div className="space-y-1.5 flex-1 overflow-y-auto">
|
||||||
|
{data.slice(0, 3).map((item, index) => (
|
||||||
|
<Card key={index} hoverable className="[&>*:first-child]:!p-3 h-[148px]">
|
||||||
|
<div className="flex flex-col gap-0.5">
|
||||||
|
<h3 className="text-sm font-medium text-gray-900 line-clamp-2">
|
||||||
|
{item.title}
|
||||||
|
</h3>
|
||||||
|
<p className="text-xs text-gray-500 line-clamp-2">
|
||||||
|
{item.description}
|
||||||
|
</p>
|
||||||
|
<p className="text-gray-700 truncate">{item.time}</p>
|
||||||
|
{item.metadata && (
|
||||||
|
<div className="text-green-500 text-xs px-2 py-1 rounded-full flex items-center gap-1">
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
className="styles__StyledSVGIconPathComponent-sc-i3aj97-0 bxMexi svg-icon-path-icon"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
width="12"
|
||||||
|
height="12">
|
||||||
|
<defs></defs>
|
||||||
|
<g>
|
||||||
|
<path
|
||||||
|
fill="rgb(34, 197, 94)"
|
||||||
|
d="m16 11.78l4.24-7.33l1.73 1l-5.23 9.05l-6.51-3.75L5.46 19H22v2H2V3h2v14.54L9.5 8z"></path>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
{item.metadata}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</Card>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* 抽屉 */}
|
||||||
|
<Drawer
|
||||||
|
title="相关数据"
|
||||||
|
closable={{ "aria-label": "Close Button" }}
|
||||||
|
onClose={onClose}
|
||||||
|
open={open}
|
||||||
|
width={600}>
|
||||||
|
<List
|
||||||
|
itemLayout="vertical"
|
||||||
|
dataSource={data}
|
||||||
|
renderItem={(item, index) => (
|
||||||
|
<List.Item>
|
||||||
|
<List.Item.Meta
|
||||||
|
title={
|
||||||
|
<h3 className="text-sm font-medium text-gray-900">
|
||||||
|
{item.title}
|
||||||
|
</h3>
|
||||||
|
}
|
||||||
|
description={
|
||||||
|
<div className="space-y-1">
|
||||||
|
<p className="text-xs text-gray-500">{item.description}</p>
|
||||||
|
<p className="text-gray-700">{item.time}</p>
|
||||||
|
{item.metadata && (
|
||||||
|
<div className="text-green-500 text-xs px-2 py-1 rounded-full flex items-center gap-1">
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
className="styles__StyledSVGIconPathComponent-sc-i3aj97-0 bxMexi svg-icon-path-icon"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
width="12"
|
||||||
|
height="12">
|
||||||
|
<defs></defs>
|
||||||
|
<g>
|
||||||
|
<path
|
||||||
|
fill="rgb(34, 197, 94)"
|
||||||
|
d="m16 11.78l4.24-7.33l1.73 1l-5.23 9.05l-6.51-3.75L5.46 19H22v2H2V3h2v14.54L9.5 8z"></path>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
{item.metadata}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</List.Item>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</Drawer>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
84
src/components/Common/Playground/History.tsx
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
import { Sidebar } from "@/components/Option/Sidebar.tsx"
|
||||||
|
import React, { useContext, useState } from "react"
|
||||||
|
import { useMessageOption } from "@/hooks/useMessageOption.tsx"
|
||||||
|
import { useStoreChatModelSettings } from "@/store/model.tsx"
|
||||||
|
import { Card, Tooltip } from "antd"
|
||||||
|
import { PageAssitDatabase } from "@/db"
|
||||||
|
import { EraserIcon } from "lucide-react"
|
||||||
|
import { useTranslation } from "react-i18next"
|
||||||
|
import { useQueryClient } from "@tanstack/react-query"
|
||||||
|
import { HistoryContext } from "@/components/Layouts/Layout.tsx"
|
||||||
|
|
||||||
|
export const PlaygroundHistory = () => {
|
||||||
|
const { setSystemPrompt } = useStoreChatModelSettings()
|
||||||
|
|
||||||
|
const { show, setShow } = useContext(HistoryContext)
|
||||||
|
|
||||||
|
const {
|
||||||
|
setMessages,
|
||||||
|
setHistory,
|
||||||
|
setHistoryId,
|
||||||
|
historyId,
|
||||||
|
clearChat,
|
||||||
|
setSelectedModel,
|
||||||
|
temporaryChat,
|
||||||
|
setSelectedSystemPrompt
|
||||||
|
} = useMessageOption()
|
||||||
|
|
||||||
|
const { t } = useTranslation(["option", "common", "settings"])
|
||||||
|
|
||||||
|
const queryClient = useQueryClient()
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Card
|
||||||
|
className={`flex flex-col [&>:nth-child(2)]:flex-1 [&>:nth-child(2)]:overflow-y-auto w-[300px] h-full pt-16 pb-5 transition-all duration-300 ease-in-out transform ${
|
||||||
|
show
|
||||||
|
? 'opacity-100 translate-x-0'
|
||||||
|
: 'opacity-0 -translate-x-full absolute'
|
||||||
|
}`}
|
||||||
|
style={{ paddingTop: "4rem" }}
|
||||||
|
title={
|
||||||
|
<div className="flex items-center justify-between w-full">
|
||||||
|
{t("sidebarTitle")}
|
||||||
|
<Tooltip
|
||||||
|
title={t("settings:generalSettings.system.deleteChatHistory.label")}
|
||||||
|
placement="right">
|
||||||
|
<button
|
||||||
|
onClick={async () => {
|
||||||
|
const confirm = window.confirm(
|
||||||
|
t("settings:generalSettings.system.deleteChatHistory.confirm")
|
||||||
|
)
|
||||||
|
|
||||||
|
if (confirm) {
|
||||||
|
const db = new PageAssitDatabase()
|
||||||
|
await db.deleteAllChatHistory()
|
||||||
|
await queryClient.invalidateQueries({
|
||||||
|
queryKey: ["fetchChatHistory"]
|
||||||
|
})
|
||||||
|
clearChat()
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
className="text-gray-600 hover:text-gray-800 dark:text-gray-300 dark:hover:text-gray-100">
|
||||||
|
<EraserIcon className="size-5" />
|
||||||
|
</button>
|
||||||
|
</Tooltip>
|
||||||
|
</div>
|
||||||
|
}>
|
||||||
|
<div className="overflow-y-auto">
|
||||||
|
<Sidebar
|
||||||
|
onClose={() => setShow(true)}
|
||||||
|
setMessages={setMessages}
|
||||||
|
setHistory={setHistory}
|
||||||
|
setHistoryId={setHistoryId}
|
||||||
|
setSelectedModel={setSelectedModel}
|
||||||
|
setSelectedSystemPrompt={setSelectedSystemPrompt}
|
||||||
|
clearChat={clearChat}
|
||||||
|
historyId={historyId}
|
||||||
|
setSystemPrompt={setSystemPrompt}
|
||||||
|
temporaryChat={temporaryChat}
|
||||||
|
history={history}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</Card>
|
||||||
|
)
|
||||||
|
}
|
95
src/components/Common/Playground/IodRelevant.tsx
Normal file
@ -0,0 +1,95 @@
|
|||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
const SuccessIcon = () => {
|
||||||
|
return (<svg
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
fill="none"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
className="w-full h-full text-green-500">
|
||||||
|
<path
|
||||||
|
d="M9 12L11 14L15 10M21 12C21 16.9706 16.9706 21 12 21C7.02944 21 3 16.9706 3 12C3 7.02944 7.02944 3 12 3C16.9706 3 21 7.02944 21 12Z"
|
||||||
|
stroke="currentColor"
|
||||||
|
strokeWidth="2"
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
/>
|
||||||
|
</svg>)
|
||||||
|
}
|
||||||
|
|
||||||
|
const LoadingIcon = () => {
|
||||||
|
return (
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
className="styles__StyledSVGIconPathComponent-sc-i3aj97-0 bxMexi svg-icon-path-icon"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
width="18"
|
||||||
|
height="18">
|
||||||
|
<defs></defs>
|
||||||
|
<g>
|
||||||
|
<path
|
||||||
|
fill="rgb(59, 130, 246)"
|
||||||
|
d="M13 2.03v2.02c4.39.54 7.5 4.53 6.96 8.92c-.46 3.64-3.32 6.53-6.96 6.96v2c5.5-.55 9.5-5.43 8.95-10.93c-.45-4.75-4.22-8.5-8.95-8.97m-2 .03c-1.95.19-3.81.94-5.33 2.2L7.1 5.74c1.12-.9 2.47-1.48 3.9-1.68zM4.26 5.67A9.9 9.9 0 0 0 2.05 11h2c.19-1.42.75-2.77 1.64-3.9zM2.06 13c.2 1.96.97 3.81 2.21 5.33l1.42-1.43A8 8 0 0 1 4.06 13zm5.04 5.37l-1.43 1.37A10 10 0 0 0 11 22v-2a8 8 0 0 1-3.9-1.63M12.5 7v5.25l4.5 2.67l-.75 1.23L11 13V7z"></path>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export const PlaygroundIodRelevant: React.FC = () => {
|
||||||
|
const data = [
|
||||||
|
{
|
||||||
|
title: "已在29个科学数据中心的50万个科学数据集中进行搜索",
|
||||||
|
description: "已发现4个数据集",
|
||||||
|
status: "success"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "已在100万篇论文、2800个科创场景中进行搜索",
|
||||||
|
description: "已发现4个数据集",
|
||||||
|
status: "success"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "正在1000位智库专家、12万个创新机构中进行搜索",
|
||||||
|
status: "loading"
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
for (let i = 0; i < 10; i++) {
|
||||||
|
data.push({
|
||||||
|
title: "已在29个科学数据中心的50万个科学数据集中进行搜索" + i,
|
||||||
|
description: "已发现4个数据集",
|
||||||
|
status: "success"
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="flex flex-col h-full">
|
||||||
|
{/* Header */}
|
||||||
|
<div className="flex justify-between items-center mb-4">
|
||||||
|
<h2 className="text-lg font-semibold text-gray-900">
|
||||||
|
数联网搜索相关内
|
||||||
|
</h2>
|
||||||
|
<span className="text-sm text-blue-600 font-medium">{data.length}个结果</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Content */}
|
||||||
|
<div className="space-y-3 flex-1 overflow-y-auto">
|
||||||
|
{
|
||||||
|
data.map((item, index) => (
|
||||||
|
<div className="flex items-start space-x-3">
|
||||||
|
<div className="w-5 h-5 mt-1 flex-shrink-0">
|
||||||
|
{item.status === "success" ? <SuccessIcon /> : <LoadingIcon />}
|
||||||
|
</div>
|
||||||
|
<div className="flex-1">
|
||||||
|
<p className="text-sm text-gray-700">
|
||||||
|
{item.title}
|
||||||
|
</p>
|
||||||
|
{item.description && <p className="text-xs text-gray-500 mt-1">{item.description}</p>}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
@ -58,7 +58,7 @@ export const PlaygroundMessage = (props: Props) => {
|
|||||||
const { t } = useTranslation("common")
|
const { t } = useTranslation("common")
|
||||||
const { cancel, isSpeaking, speak } = useTTS()
|
const { cancel, isSpeaking, speak } = useTTS()
|
||||||
return (
|
return (
|
||||||
<div className="group relative flex w-full max-w-3xl flex-col items-end justify-center pb-2 md:px-4 lg:w-4/5 text-gray-800 dark:text-gray-100">
|
<div className="group relative flex w-full flex-col items-end justify-center pb-2 md:px-4 text-gray-800 dark:text-gray-100">
|
||||||
{/* <div className="text-base md:max-w-2xl lg:max-w-xl xl:max-w-3xl flex lg:px-0 m-auto w-full"> */}
|
{/* <div className="text-base md:max-w-2xl lg:max-w-xl xl:max-w-3xl flex lg:px-0 m-auto w-full"> */}
|
||||||
<div className="flex flex-row gap-4 md:gap-6 my-2 m-auto w-full">
|
<div className="flex flex-row gap-4 md:gap-6 my-2 m-auto w-full">
|
||||||
<div className="w-8 flex flex-col relative items-end">
|
<div className="w-8 flex flex-col relative items-end">
|
||||||
@ -138,7 +138,7 @@ export const PlaygroundMessage = (props: Props) => {
|
|||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
<p
|
<p
|
||||||
className={`prose dark:prose-invert whitespace-pre-line prose-p:leading-relaxed prose-pre:p-0 dark:prose-dark ${
|
className={`prose-lg dark:prose-invert whitespace-pre-line prose-p:leading-relaxed prose-pre:p-0 dark:prose-dark ${
|
||||||
props.message_type &&
|
props.message_type &&
|
||||||
"italic text-gray-500 dark:text-gray-400 text-sm"
|
"italic text-gray-500 dark:text-gray-400 text-sm"
|
||||||
}`}>
|
}`}>
|
||||||
|
142
src/components/Common/Playground/Scene.tsx
Normal file
@ -0,0 +1,142 @@
|
|||||||
|
import React, { useState } from "react"
|
||||||
|
import { DataNavigation } from "@/components/Common/DataNavigation.tsx"
|
||||||
|
import { Card, Drawer, List } from "antd"
|
||||||
|
|
||||||
|
export const PlaygroundScene = () => {
|
||||||
|
// 模拟数据
|
||||||
|
const data = [
|
||||||
|
{
|
||||||
|
title: "绿色化工工艺项目",
|
||||||
|
description:
|
||||||
|
"基于生物基原料,采用repeal2.0可降解材料技术,开发新型环保材料。",
|
||||||
|
demander: "奥赛康药业 供方:美国Propella公司"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "智能农业解决方案",
|
||||||
|
description: "利用物联网技术,实现精准农业管理,提高农作物产量。",
|
||||||
|
demander: "奥赛康药业 供方:美国Propella公司"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "新能源汽车电池技术",
|
||||||
|
description: "研发高能量密度、长寿命的新型电池材料,推动电动汽车发展。",
|
||||||
|
demander: "奥赛康药业 供方:美国Propella公司"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "碳捕集与封存技术",
|
||||||
|
description: "开发高效的碳捕集技术,减少工业排放,助力碳中和目标。",
|
||||||
|
demander: "奥赛康药业 供方:美国Propella公司"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
for (let i = 0; i < 10; i++) {
|
||||||
|
data.push({
|
||||||
|
title: "开发新型电池材料",
|
||||||
|
description: "研发高能量密度、长寿命的新型电池材料,推动电动汽车发展。",
|
||||||
|
demander: "奥赛康药业 供方:美国Propella公司"
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const [open, setOpen] = useState(false)
|
||||||
|
|
||||||
|
const showDrawer = () => {
|
||||||
|
setOpen(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
const onClose = () => {
|
||||||
|
setOpen(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="h-full overflow-y-hidden flex flex-col">
|
||||||
|
{/* 数据导航 */}
|
||||||
|
<DataNavigation
|
||||||
|
Header={
|
||||||
|
<div className="flex items-center text-[#15803d] gap-1">
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
className="styles__StyledSVGIconPathComponent-sc-i3aj97-0 bxMexi svg-icon-path-icon"
|
||||||
|
viewBox="0 0 32 32"
|
||||||
|
width="20"
|
||||||
|
height="20">
|
||||||
|
<defs></defs>
|
||||||
|
<g>
|
||||||
|
<path
|
||||||
|
fill="rgb(21, 128, 61)"
|
||||||
|
d="M16 18H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h10a2 2 0 0 1 2 2v10a2 2 0 0 1-2 2M6 6v10h10V6zm20 6v4h-4v-4zm0-2h-4a2 2 0 0 0-2 2v4a2 2 0 0 0 2 2h4a2 2 0 0 0 2-2v-4a2 2 0 0 0-2-2m0 12v4h-4v-4zm0-2h-4a2 2 0 0 0-2 2v4a2 2 0 0 0 2 2h4a2 2 0 0 0 2-2v-4a2 2 0 0 0-2-2m-10 2v4h-4v-4zm0-2h-4a2 2 0 0 0-2 2v4a2 2 0 0 0 2 2h4a2 2 0 0 0 2-2v-4a2 2 0 0 0-2-2"></path>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
相关场景
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
onClick={showDrawer}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{/* 场景列表 */}
|
||||||
|
<div className="space-y-1.5 flex-1 overflow-y-auto">
|
||||||
|
{data.slice(0,3).map((item, index) => (
|
||||||
|
<Card key={index} hoverable className="[&>*:first-child]:!p-4 h-[148px]" >
|
||||||
|
<div className="flex flex-col gap-0.5">
|
||||||
|
<h3 className="text-sm font-medium text-gray-900 line-clamp-2">
|
||||||
|
{item.title}
|
||||||
|
</h3>
|
||||||
|
<p className="flex items-center gap-1.5">
|
||||||
|
<span className="inline-block bg-blue-100 text-green-800 text-xs px-2 py-1 rounded-full">
|
||||||
|
技 术 应
|
||||||
|
</span>
|
||||||
|
<span className="inline-block bg-blue-100 text-blue-800 text-xs px-2 py-1 rounded-full">
|
||||||
|
制 造
|
||||||
|
</span>
|
||||||
|
</p>
|
||||||
|
<p className="text-xs text-gray-500 truncate">{item.demander}</p>
|
||||||
|
<span className="text-gray-700 line-clamp-2">
|
||||||
|
{item.description}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</Card>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* 抽屉 */}
|
||||||
|
<Drawer
|
||||||
|
title="相关场景"
|
||||||
|
closable={{ "aria-label": "Close Button" }}
|
||||||
|
onClose={onClose}
|
||||||
|
open={open}
|
||||||
|
width={600}>
|
||||||
|
<List
|
||||||
|
itemLayout="vertical"
|
||||||
|
dataSource={data}
|
||||||
|
renderItem={(item, index) => (
|
||||||
|
<List.Item>
|
||||||
|
<List.Item.Meta
|
||||||
|
title={
|
||||||
|
<h3 className="text-sm font-medium text-gray-900">
|
||||||
|
{item.title}
|
||||||
|
</h3>
|
||||||
|
}
|
||||||
|
description={
|
||||||
|
<div className="space-y-1">
|
||||||
|
<p className="flex items-center gap-1.5">
|
||||||
|
<span className="inline-block bg-blue-100 text-green-800 text-xs px-2 py-1 rounded-full">
|
||||||
|
技 术 应
|
||||||
|
</span>
|
||||||
|
<span className="inline-block bg-blue-100 text-blue-800 text-xs px-2 py-1 rounded-full">
|
||||||
|
制 造
|
||||||
|
</span>
|
||||||
|
</p>
|
||||||
|
<p className="text-xs text-gray-500">
|
||||||
|
{item.demander}
|
||||||
|
</p>
|
||||||
|
<span className="text-gray-700">
|
||||||
|
{item.description}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</List.Item>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</Drawer>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
136
src/components/Common/Playground/Team.tsx
Normal file
@ -0,0 +1,136 @@
|
|||||||
|
import React, { useState } from "react"
|
||||||
|
import { DataNavigation } from "@/components/Common/DataNavigation.tsx"
|
||||||
|
import { Card, Drawer, List } from "antd"
|
||||||
|
|
||||||
|
export const PlaygroundTeam = () => {
|
||||||
|
// 模拟数据
|
||||||
|
const data = [
|
||||||
|
{
|
||||||
|
title: "绿色化工工艺项目",
|
||||||
|
description:
|
||||||
|
"基于生物基原料,采用repeal2.0可降解材料技术,开发新型环保材料。",
|
||||||
|
demander: "奥赛康药业 供方:美国Propella公司"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "智能农业解决方案",
|
||||||
|
description: "利用物联网技术,实现精准农业管理,提高农作物产量。",
|
||||||
|
demander: "奥赛康药业 供方:美国Propella公司"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "新能源汽车电池技术",
|
||||||
|
description: "研发高能量密度、长寿命的新型电池材料,推动电动汽车发展。",
|
||||||
|
demander: "奥赛康药业 供方:美国Propella公司"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "碳捕集与封存技术",
|
||||||
|
description: "开发高效的碳捕集技术,减少工业排放,助力碳中和目标。",
|
||||||
|
demander: "奥赛康药业 供方:美国Propella公司"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
for (let i = 0; i < 10; i++) {
|
||||||
|
data.push({
|
||||||
|
title: "开发新型电池材料",
|
||||||
|
description: "研发高能量密度、长寿命的新型电池材料,推动电动汽车发展。",
|
||||||
|
demander: "奥赛康药业 供方:美国Propella公司"
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const [open, setOpen] = useState(false)
|
||||||
|
|
||||||
|
const showDrawer = () => {
|
||||||
|
setOpen(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
const onClose = () => {
|
||||||
|
setOpen(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="h-full overflow-y-hidden flex flex-col">
|
||||||
|
{/* 数据导航 */}
|
||||||
|
<DataNavigation
|
||||||
|
Header={
|
||||||
|
<div className="flex items-center text-[#15803d] gap-1">
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
className="styles__StyledSVGIconPathComponent-sc-i3aj97-0 bxMexi svg-icon-path-icon"
|
||||||
|
viewBox="0 0 32 32"
|
||||||
|
width="20"
|
||||||
|
height="20">
|
||||||
|
<defs></defs>
|
||||||
|
<g>
|
||||||
|
<path
|
||||||
|
fill="rgb(21, 128, 61)"
|
||||||
|
d="M16 18H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h10a2 2 0 0 1 2 2v10a2 2 0 0 1-2 2M6 6v10h10V6zm20 6v4h-4v-4zm0-2h-4a2 2 0 0 0-2 2v4a2 2 0 0 0 2 2h4a2 2 0 0 0 2-2v-4a2 2 0 0 0-2-2m0 12v4h-4v-4zm0-2h-4a2 2 0 0 0-2 2v4a2 2 0 0 0 2 2h4a2 2 0 0 0 2-2v-4a2 2 0 0 0-2-2m-10 2v4h-4v-4zm0-2h-4a2 2 0 0 0-2 2v4a2 2 0 0 0 2 2h4a2 2 0 0 0 2-2v-4a2 2 0 0 0-2-2"></path>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
相关团队
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
onClick={showDrawer}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{/* 场景列表 */}
|
||||||
|
<div className="grid grid-cols-2 gap-3">
|
||||||
|
{data.slice(0,2).map((item, index) => (
|
||||||
|
<Card key={index} hoverable className="[&>*:first-child]:!p-3" >
|
||||||
|
<div className="flex flex-col gap-0.5">
|
||||||
|
<h3 className="text-sm font-medium text-gray-900 line-clamp-2">
|
||||||
|
{item.title}
|
||||||
|
</h3>
|
||||||
|
<p className="flex items-center gap-1.5">
|
||||||
|
<span className="inline-block bg-blue-100 text-green-800 text-xs px-2 py-1 rounded-full">
|
||||||
|
技 术 应
|
||||||
|
</span>
|
||||||
|
<span className="inline-block bg-blue-100 text-blue-800 text-xs px-2 py-1 rounded-full">
|
||||||
|
制 造
|
||||||
|
</span>
|
||||||
|
</p>
|
||||||
|
<p className="text-xs text-gray-500 line-clamp-2">{item.description}</p>
|
||||||
|
</div>
|
||||||
|
</Card>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* 抽屉 */}
|
||||||
|
<Drawer
|
||||||
|
title="相关团队"
|
||||||
|
closable={{ "aria-label": "Close Button" }}
|
||||||
|
onClose={onClose}
|
||||||
|
open={open}
|
||||||
|
width={600}>
|
||||||
|
<List
|
||||||
|
itemLayout="vertical"
|
||||||
|
dataSource={data}
|
||||||
|
renderItem={(item, index) => (
|
||||||
|
<List.Item>
|
||||||
|
<List.Item.Meta
|
||||||
|
title={
|
||||||
|
<h3 className="text-sm font-medium text-gray-900">
|
||||||
|
{item.title}
|
||||||
|
</h3>
|
||||||
|
}
|
||||||
|
description={
|
||||||
|
<div className="space-y-1">
|
||||||
|
<p className="flex items-center gap-1.5">
|
||||||
|
<span className="inline-block bg-blue-100 text-green-800 text-xs px-2 py-1 rounded-full">
|
||||||
|
技 术 应
|
||||||
|
</span>
|
||||||
|
<span className="inline-block bg-blue-100 text-blue-800 text-xs px-2 py-1 rounded-full">
|
||||||
|
制 造
|
||||||
|
</span>
|
||||||
|
</p>
|
||||||
|
<p className="text-xs text-gray-500">
|
||||||
|
{item.description}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</List.Item>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</Drawer>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
46
src/components/Common/Playground/TokenStatistics.tsx
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
import { DataNavigation } from "@/components/Common/DataNavigation.tsx"
|
||||||
|
import { Card, Descriptions, DescriptionsProps, Drawer, List, Spin } from "antd"
|
||||||
|
import { useCallback, useMemo, useState } from "react"
|
||||||
|
import { useMessageOption } from "@/hooks/useMessageOption.tsx"
|
||||||
|
import { useStoreMessageOption } from "@/store/option.tsx"
|
||||||
|
|
||||||
|
export const PlaygroundTokenStatistics = () => {
|
||||||
|
const { currentMeteringEntry } = useStoreMessageOption()
|
||||||
|
|
||||||
|
const items = useMemo<DescriptionsProps["items"]>(() => {
|
||||||
|
const { data } = currentMeteringEntry
|
||||||
|
return [
|
||||||
|
// {
|
||||||
|
// key: "relatedDataCount",
|
||||||
|
// label: "关联数据个数",
|
||||||
|
// children: data.relatedDataCount
|
||||||
|
// },
|
||||||
|
{
|
||||||
|
key: "iodTokenCount",
|
||||||
|
label: "数联网引用token总数",
|
||||||
|
children: data.iodTokenCount ?? 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "modelInputTokenCount",
|
||||||
|
label: "大模型输入token数量",
|
||||||
|
children: data.modelInputTokenCount ?? 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "modelOutputTokenCount",
|
||||||
|
label: "大模型输出token数量",
|
||||||
|
children: data.modelOutputTokenCount ?? 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}, [currentMeteringEntry])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Card
|
||||||
|
style={{ marginBottom: "1rem" }}
|
||||||
|
className="h-full"
|
||||||
|
title={<DataNavigation title="Token统计" showButton={false} />}>
|
||||||
|
<Spin spinning={currentMeteringEntry.loading}>
|
||||||
|
<Descriptions layout="horizontal" items={items} column={2} />
|
||||||
|
</Spin>
|
||||||
|
</Card>
|
||||||
|
)
|
||||||
|
}
|
@ -24,18 +24,24 @@ import { ProviderIcons } from "../Common/ProviderIcon"
|
|||||||
import { NewChat } from "./NewChat"
|
import { NewChat } from "./NewChat"
|
||||||
import { PageAssistSelect } from "../Select"
|
import { PageAssistSelect } from "../Select"
|
||||||
import { MoreOptions } from "./MoreOptions"
|
import { MoreOptions } from "./MoreOptions"
|
||||||
|
import { useContext } from "react"
|
||||||
|
import { HistoryContext } from "@/components/Layouts/Layout.tsx"
|
||||||
type Props = {
|
type Props = {
|
||||||
setSidebarOpen: (open: boolean) => void
|
sidebarOpen: boolean
|
||||||
|
setSidebarOpen: () => void
|
||||||
setOpenModelSettings: (open: boolean) => void
|
setOpenModelSettings: (open: boolean) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
export const Header: React.FC<Props> = ({
|
export const Header: React.FC<Props> = ({
|
||||||
setOpenModelSettings,
|
setOpenModelSettings,
|
||||||
setSidebarOpen
|
setSidebarOpen,
|
||||||
|
sidebarOpen
|
||||||
}) => {
|
}) => {
|
||||||
const { t, i18n } = useTranslation(["option", "common"])
|
const { t, i18n } = useTranslation(["option", "common"])
|
||||||
const isRTL = i18n?.dir() === "rtl"
|
const isRTL = i18n?.dir() === "rtl"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const [shareModeEnabled] = useStorage("shareMode", false)
|
const [shareModeEnabled] = useStorage("shareMode", false)
|
||||||
const [hideCurrentChatModelSettings] = useStorage(
|
const [hideCurrentChatModelSettings] = useStorage(
|
||||||
"hideCurrentChatModelSettings",
|
"hideCurrentChatModelSettings",
|
||||||
@ -109,10 +115,16 @@ export const Header: React.FC<Props> = ({
|
|||||||
</NavLink>
|
</NavLink>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<div>
|
<div style={{width: sidebarOpen ? "288px" : "205px"}} className="flex items-center justify-between transition-all duration-300 ease-in-out">
|
||||||
|
<h2
|
||||||
|
className="text-xl font-bold text-zinc-700 dark:text-zinc-300 mr-3"
|
||||||
|
style={{ lineHeight: "0" }}>
|
||||||
|
{t("projectTitle")}
|
||||||
|
</h2>
|
||||||
|
|
||||||
<button
|
<button
|
||||||
className="text-gray-500 dark:text-gray-400"
|
className="text-gray-500 dark:text-gray-400"
|
||||||
onClick={() => setSidebarOpen(true)}>
|
onClick={() => setSidebarOpen()}>
|
||||||
<PanelLeftIcon className="w-6 h-6" />
|
<PanelLeftIcon className="w-6 h-6" />
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,103 +1,50 @@
|
|||||||
import React, { useState } from "react"
|
import React, { useCallback, useEffect, useState } from "react"
|
||||||
|
|
||||||
import { Sidebar } from "../Option/Sidebar"
|
|
||||||
import { Drawer, Tooltip } from "antd"
|
|
||||||
|
|
||||||
import { useTranslation } from "react-i18next"
|
|
||||||
|
|
||||||
import { CurrentChatModelSettings } from "../Common/Settings/CurrentChatModelSettings"
|
import { CurrentChatModelSettings } from "../Common/Settings/CurrentChatModelSettings"
|
||||||
import { Header } from "./Header"
|
import { Header } from "./Header"
|
||||||
import { EraserIcon } from "lucide-react"
|
|
||||||
import { PageAssitDatabase } from "@/db"
|
interface History {
|
||||||
import { useMessageOption } from "@/hooks/useMessageOption"
|
show: boolean
|
||||||
import { useQueryClient } from "@tanstack/react-query"
|
setShow: (show: boolean) => void
|
||||||
import { useStoreChatModelSettings } from "@/store/model"
|
}
|
||||||
|
|
||||||
|
export const HistoryContext = React.createContext<History>({
|
||||||
|
show: true,
|
||||||
|
setShow: () => {}
|
||||||
|
})
|
||||||
|
|
||||||
export default function OptionLayout({
|
export default function OptionLayout({
|
||||||
children
|
children
|
||||||
}: {
|
}: {
|
||||||
children: React.ReactNode
|
children: React.ReactNode
|
||||||
}) {
|
}) {
|
||||||
const [sidebarOpen, setSidebarOpen] = useState(false)
|
const [showHistory, setShowHistory] = useState(true)
|
||||||
const { t } = useTranslation(["option", "common", "settings"])
|
|
||||||
const [openModelSettings, setOpenModelSettings] = useState(false)
|
const [openModelSettings, setOpenModelSettings] = useState(false)
|
||||||
const {
|
|
||||||
setMessages,
|
|
||||||
setHistory,
|
|
||||||
setHistoryId,
|
|
||||||
historyId,
|
|
||||||
clearChat,
|
|
||||||
setSelectedModel,
|
|
||||||
temporaryChat,
|
|
||||||
setSelectedSystemPrompt
|
|
||||||
} = useMessageOption()
|
|
||||||
|
|
||||||
const queryClient = useQueryClient()
|
const historyContextValue = {
|
||||||
const { setSystemPrompt } = useStoreChatModelSettings()
|
show: showHistory,
|
||||||
|
setShow: setShowHistory
|
||||||
|
}
|
||||||
|
|
||||||
|
const useToggle = useCallback(() => {
|
||||||
|
setShowHistory(!showHistory)
|
||||||
|
}, [showHistory])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex h-full w-full">
|
<div className="flex h-full w-full">
|
||||||
<main className="relative h-dvh w-full">
|
<main className="relative h-dvh w-full">
|
||||||
<div className="relative z-10 w-full">
|
<div className="relative z-10 w-full">
|
||||||
<Header
|
<Header
|
||||||
setSidebarOpen={setSidebarOpen}
|
sidebarOpen={showHistory}
|
||||||
|
setSidebarOpen={useToggle}
|
||||||
setOpenModelSettings={setOpenModelSettings}
|
setOpenModelSettings={setOpenModelSettings}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
{/* <div className="relative flex h-full flex-col items-center"> */}
|
{/* <div className="relative flex h-full flex-col items-center"> */}
|
||||||
|
<HistoryContext.Provider value={historyContextValue}>
|
||||||
{children}
|
{children}
|
||||||
|
</HistoryContext.Provider>
|
||||||
{/* </div> */}
|
{/* </div> */}
|
||||||
<Drawer
|
|
||||||
title={
|
|
||||||
<div className="flex items-center justify-between">
|
|
||||||
{t("sidebarTitle")}
|
|
||||||
|
|
||||||
<Tooltip
|
|
||||||
title={t(
|
|
||||||
"settings:generalSettings.system.deleteChatHistory.label"
|
|
||||||
)}
|
|
||||||
placement="right">
|
|
||||||
<button
|
|
||||||
onClick={async () => {
|
|
||||||
const confirm = window.confirm(
|
|
||||||
t(
|
|
||||||
"settings:generalSettings.system.deleteChatHistory.confirm"
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
if (confirm) {
|
|
||||||
const db = new PageAssitDatabase()
|
|
||||||
await db.deleteAllChatHistory()
|
|
||||||
await queryClient.invalidateQueries({
|
|
||||||
queryKey: ["fetchChatHistory"]
|
|
||||||
})
|
|
||||||
clearChat()
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
className="text-gray-600 hover:text-gray-800 dark:text-gray-300 dark:hover:text-gray-100">
|
|
||||||
<EraserIcon className="size-5" />
|
|
||||||
</button>
|
|
||||||
</Tooltip>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
placement="left"
|
|
||||||
closeIcon={null}
|
|
||||||
onClose={() => setSidebarOpen(false)}
|
|
||||||
open={sidebarOpen}>
|
|
||||||
<Sidebar
|
|
||||||
onClose={() => setSidebarOpen(false)}
|
|
||||||
setMessages={setMessages}
|
|
||||||
setHistory={setHistory}
|
|
||||||
setHistoryId={setHistoryId}
|
|
||||||
setSelectedModel={setSelectedModel}
|
|
||||||
setSelectedSystemPrompt={setSelectedSystemPrompt}
|
|
||||||
clearChat={clearChat}
|
|
||||||
historyId={historyId}
|
|
||||||
setSystemPrompt={setSystemPrompt}
|
|
||||||
temporaryChat={temporaryChat}
|
|
||||||
history={history}
|
|
||||||
/>
|
|
||||||
</Drawer>
|
|
||||||
|
|
||||||
<CurrentChatModelSettings
|
<CurrentChatModelSettings
|
||||||
open={openModelSettings}
|
open={openModelSettings}
|
||||||
|
@ -1,8 +1,14 @@
|
|||||||
import React from "react"
|
import React from "react"
|
||||||
|
|
||||||
|
import { Card } from "antd"
|
||||||
|
|
||||||
import { PlaygroundForm } from "./PlaygroundForm"
|
import { PlaygroundForm } from "./PlaygroundForm"
|
||||||
import { PlaygroundChat } from "./PlaygroundChat"
|
import { PlaygroundChat } from "./PlaygroundChat"
|
||||||
import { useMessageOption } from "@/hooks/useMessageOption"
|
import { useMessageOption } from "@/hooks/useMessageOption"
|
||||||
import { webUIResumeLastChat } from "@/services/app"
|
import { webUIResumeLastChat } from "@/services/app"
|
||||||
|
import { PlaygroundData } from '@/components/Common/Playground/Data.tsx'
|
||||||
|
import { PlaygroundScene } from "@/components/Common/Playground/Scene.tsx"
|
||||||
|
|
||||||
import {
|
import {
|
||||||
formatToChatHistory,
|
formatToChatHistory,
|
||||||
formatToMessage,
|
formatToMessage,
|
||||||
@ -13,6 +19,11 @@ import { getLastUsedChatSystemPrompt } from "@/services/model-settings"
|
|||||||
import { useStoreChatModelSettings } from "@/store/model"
|
import { useStoreChatModelSettings } from "@/store/model"
|
||||||
import { useSmartScroll } from "@/hooks/useSmartScroll"
|
import { useSmartScroll } from "@/hooks/useSmartScroll"
|
||||||
import { ChevronDown } from "lucide-react"
|
import { ChevronDown } from "lucide-react"
|
||||||
|
import { PlaygroundTeam } from "@/components/Common/Playground/Team.tsx"
|
||||||
|
import { PlaygroundTokenStatistics } from "@/components/Common/Playground/TokenStatistics.tsx"
|
||||||
|
import { PlaygroundHistory } from "@/components/Common/Playground/History.tsx"
|
||||||
|
import { PlaygroundIodRelevant } from "@/components/Common/Playground/IodRelevant.tsx"
|
||||||
|
|
||||||
|
|
||||||
export const Playground = () => {
|
export const Playground = () => {
|
||||||
const drop = React.useRef<HTMLDivElement>(null)
|
const drop = React.useRef<HTMLDivElement>(null)
|
||||||
@ -132,9 +143,11 @@ export const Playground = () => {
|
|||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
ref={drop}
|
ref={drop}
|
||||||
className={`relative flex h-full flex-col items-center ${
|
className={`relative flex gap-3 h-full items-center ${
|
||||||
dropState === "dragging" ? "bg-gray-100 dark:bg-gray-800" : ""
|
dropState === "dragging" ? "bg-gray-100 dark:bg-gray-800" : ""
|
||||||
} bg-white dark:bg-[#171717]`}>
|
} bg-white dark:bg-[#171717]`}>
|
||||||
|
<PlaygroundHistory />
|
||||||
|
<div className="relative h-full flex-1 prose-lg flex justify-center [&>*]:max-w-[848px]">
|
||||||
<div
|
<div
|
||||||
ref={containerRef}
|
ref={containerRef}
|
||||||
className="custom-scrollbar bg-bottom-mask-light dark:bg-bottom-mask-dark mask-bottom-fade will-change-mask flex h-full w-full flex-col items-center overflow-x-hidden overflow-y-auto px-5">
|
className="custom-scrollbar bg-bottom-mask-light dark:bg-bottom-mask-dark mask-bottom-fade will-change-mask flex h-full w-full flex-col items-center overflow-x-hidden overflow-y-auto px-5">
|
||||||
@ -142,7 +155,7 @@ export const Playground = () => {
|
|||||||
</div>
|
</div>
|
||||||
<div className="absolute bottom-0 w-full">
|
<div className="absolute bottom-0 w-full">
|
||||||
{!isAtBottom && (
|
{!isAtBottom && (
|
||||||
<div className="fixed bottom-36 z-20 left-0 right-0 flex justify-center">
|
<div className="absolute bottom-36 z-20 left-0 right-0 flex justify-center">
|
||||||
<button
|
<button
|
||||||
onClick={scrollToBottom}
|
onClick={scrollToBottom}
|
||||||
className="bg-gray-50 shadow border border-gray-200 dark:border-none dark:bg-white/20 p-1.5 rounded-full pointer-events-auto">
|
className="bg-gray-50 shadow border border-gray-200 dark:border-none dark:bg-white/20 p-1.5 rounded-full pointer-events-auto">
|
||||||
@ -153,5 +166,20 @@ export const Playground = () => {
|
|||||||
<PlaygroundForm dropedFile={dropedFile} />
|
<PlaygroundForm dropedFile={dropedFile} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
{messages.length && (
|
||||||
|
<div className="w-1/4 h-full grid grid-rows-[auto_530px_165px] pt-16 pr-5 pb-0 border-l border-gray-200" style={{"paddingTop": "4rem"}}>
|
||||||
|
<div className="w-full overflow-y-auto border-gray-200 border-b p-3">
|
||||||
|
<PlaygroundIodRelevant />
|
||||||
|
</div>
|
||||||
|
<div className="w-full grid grid-cols-2 gap-3 custom-scrollbar border-gray-200 border-b p-3">
|
||||||
|
<PlaygroundData />
|
||||||
|
<PlaygroundScene />
|
||||||
|
</div>
|
||||||
|
<div className="w-full p-3 pb-0">
|
||||||
|
<PlaygroundTeam />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -20,7 +20,7 @@ export const PlaygroundChat = () => {
|
|||||||
<>
|
<>
|
||||||
<div className="relative flex w-full flex-col items-center pt-16 pb-4">
|
<div className="relative flex w-full flex-col items-center pt-16 pb-4">
|
||||||
{messages.length === 0 && (
|
{messages.length === 0 && (
|
||||||
<div className="mt-32 w-full">
|
<div className="mt-3 w-full">
|
||||||
<PlaygroundEmpty />
|
<PlaygroundEmpty />
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
@ -1,130 +1,113 @@
|
|||||||
import { cleanUrl } from "@/libs/clean-url"
|
import { Card, Col, Row } from "antd"
|
||||||
import { useStorage } from "@plasmohq/storage/hook"
|
|
||||||
import { useQuery } from "@tanstack/react-query"
|
import RocketSvg from '@/assets/icons/rocket.svg'
|
||||||
import { RotateCcw } from "lucide-react"
|
import BulbSvg from '@/assets/icons/bulb.svg'
|
||||||
import { useEffect, useState } from "react"
|
import EyeSvg from '@/assets/icons/eye.svg'
|
||||||
import { Trans, useTranslation } from "react-i18next"
|
import ASvg from '@/assets/icons/a.svg'
|
||||||
import {
|
import BSvg from '@/assets/icons/b.svg'
|
||||||
getOllamaURL,
|
import CSvg from '@/assets/icons/c.svg'
|
||||||
isOllamaRunning,
|
import DSvg from '@/assets/icons/d.svg'
|
||||||
setOllamaURL as saveOllamaURL
|
import ESvg from '@/assets/icons/e.svg'
|
||||||
} from "~/services/ollama"
|
import FSvg from '@/assets/icons/f.svg'
|
||||||
|
import { useMessageOption } from "@/hooks/useMessageOption.tsx"
|
||||||
|
import { useMutation, useQueryClient } from "@tanstack/react-query"
|
||||||
|
|
||||||
|
|
||||||
export const PlaygroundEmpty = () => {
|
export const PlaygroundEmpty = () => {
|
||||||
const [ollamaURL, setOllamaURL] = useState<string>("")
|
|
||||||
const { t } = useTranslation(["playground", "common"])
|
|
||||||
|
|
||||||
const [checkOllamaStatus] = useStorage("checkOllamaStatus", true)
|
|
||||||
|
|
||||||
const {
|
const {
|
||||||
data: ollamaInfo,
|
onSubmit,
|
||||||
status: ollamaStatus,
|
setMessages,
|
||||||
refetch,
|
setHistory,
|
||||||
isRefetching
|
setHistoryId,
|
||||||
} = useQuery({
|
historyId,
|
||||||
queryKey: ["ollamaStatus"],
|
clearChat,
|
||||||
queryFn: async () => {
|
setSelectedModel,
|
||||||
const ollamaURL = await getOllamaURL()
|
temporaryChat,
|
||||||
const isOk = await isOllamaRunning()
|
setSelectedSystemPrompt
|
||||||
|
} = useMessageOption()
|
||||||
|
|
||||||
if (ollamaURL) {
|
const queryClient = useQueryClient()
|
||||||
saveOllamaURL(ollamaURL)
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
isOk,
|
const questions = [
|
||||||
ollamaURL
|
{
|
||||||
}
|
title: "最近一年大型语言模型的技术进展有哪些?",
|
||||||
|
icon: <img src={RocketSvg} alt="Rocket" className="w-10 my-0" />,
|
||||||
},
|
},
|
||||||
enabled: checkOllamaStatus
|
{
|
||||||
|
title: "生成式AI在企业中有哪些具体应用场景?",
|
||||||
|
icon: <img src={BulbSvg} alt="Rocket" className="w-10 my-0" />,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "多模态学习技术的最新研究方向是什么?",
|
||||||
|
icon: <img src={EyeSvg} alt="Rocket" className="w-10 my-0" />,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "当前AI芯片市场格局和未来三年发展趋势如何?",
|
||||||
|
icon: <img src={ASvg} alt="Rocket" className="w-10 my-0" />,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "主流深度学习框架性能与易用性对比分析?",
|
||||||
|
icon: <img src={BSvg} alt="Rocket" className="w-10 my-0" />,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "国内外AI伦理治理框架有哪些最佳实践?",
|
||||||
|
icon: <img src={CSvg} alt="Rocket" className="w-10 my-0" />,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "大规模知识图谱构建与应用最新进展?",
|
||||||
|
icon: <img src={DSvg} alt="Rocket" className="w-10 my-0" />,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "计算机视觉领域近期突破性技术有哪些?",
|
||||||
|
icon: <img src={ESvg} alt="Rocket" className="w-10 my-0" />,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "量子计算对AI算法的影响与应用前景?",
|
||||||
|
icon: <img src={FSvg} alt="Rocket" className="w-10 my-0" />,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
const { mutateAsync: sendMessage } = useMutation({
|
||||||
|
mutationFn: onSubmit,
|
||||||
|
onSuccess: () => {
|
||||||
|
queryClient.invalidateQueries({
|
||||||
|
queryKey: ["fetchChatHistory"]
|
||||||
|
})
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (ollamaInfo?.ollamaURL) {
|
function handleQuestion(message: string) {
|
||||||
setOllamaURL(ollamaInfo.ollamaURL)
|
void sendMessage({message, image: ''})
|
||||||
}
|
}
|
||||||
}, [ollamaInfo])
|
|
||||||
|
|
||||||
|
|
||||||
if (!checkOllamaStatus) {
|
|
||||||
return (
|
return (
|
||||||
<div className="mx-auto sm:max-w-xl px-4 mt-10">
|
<div className="w-full p-4">
|
||||||
<div className="rounded-lg justify-center items-center flex flex-col border p-8 bg-gray-50 dark:bg-[#262626] dark:border-gray-600">
|
{/* 标题区域 */}
|
||||||
<h1 className="text-sm font-medium text-center text-gray-500 dark:text-gray-400 flex gap-3 items-center justify-center">
|
<div className="mb-4">
|
||||||
<span >👋</span>
|
<h2 className="text-xl font-bold text-gray-800" style={{lineHeight: '0'}}>数联网科创智能体</h2>
|
||||||
<span className="text-gray-700 dark:text-gray-300">
|
<p className="text-sm text-gray-500">您好!请问有什么可以帮您?</p>
|
||||||
{t("welcome")}
|
|
||||||
</span>
|
|
||||||
</h1>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
return (
|
|
||||||
<div className="mx-auto sm:max-w-xl px-4 mt-10">
|
|
||||||
<div className="rounded-lg justify-center items-center flex flex-col border p-8 bg-gray-50 dark:bg-[#262626] dark:border-gray-600">
|
|
||||||
{(ollamaStatus === "pending" || isRefetching) && (
|
|
||||||
<div className="inline-flex items-center space-x-2">
|
|
||||||
<div className="w-3 h-3 bg-blue-500 rounded-full animate-pulse"></div>
|
|
||||||
<p className="dark:text-gray-400 text-gray-900">
|
|
||||||
{t("ollamaState.searching")}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
{!isRefetching && ollamaStatus === "success" ? (
|
|
||||||
ollamaInfo.isOk ? (
|
|
||||||
<div className="inline-flex items-center space-x-2">
|
|
||||||
<div className="w-3 h-3 bg-green-500 rounded-full animate-pulse"></div>
|
|
||||||
<p className="dark:text-gray-400 text-gray-900">
|
|
||||||
{t("ollamaState.running")}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
<div className="flex flex-col space-y-2 justify-center items-center">
|
|
||||||
<div className="inline-flex space-x-2">
|
|
||||||
<div className="w-3 h-3 bg-red-500 rounded-full animate-pulse"></div>
|
|
||||||
<p className="dark:text-gray-400 text-gray-900">
|
|
||||||
{t("ollamaState.notRunning")}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<input
|
|
||||||
className="bg-gray-100 dark:bg-[#262626] dark:text-gray-100 rounded-md px-4 py-2 mt-2 w-full"
|
|
||||||
type="url"
|
|
||||||
value={ollamaURL}
|
|
||||||
onChange={(e) => setOllamaURL(e.target.value)}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<button
|
|
||||||
onClick={() => {
|
|
||||||
saveOllamaURL(ollamaURL)
|
|
||||||
refetch()
|
|
||||||
}}
|
|
||||||
className="inline-flex mt-4 items-center rounded-md border border-transparent bg-black px-2 py-2 text-sm font-medium leading-4 text-white shadow-sm hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2 dark:bg-white dark:text-gray-800 dark:hover:bg-gray-100 dark:focus:ring-gray-500 dark:focus:ring-offset-gray-100 disabled:opacity-50 ">
|
|
||||||
<RotateCcw className="h-4 w-4 mr-3" />
|
|
||||||
{t("common:retry")}
|
|
||||||
</button>
|
|
||||||
|
|
||||||
{ollamaURL &&
|
|
||||||
cleanUrl(ollamaURL) !== "http://127.0.0.1:11434" && (
|
|
||||||
<p className="text-xs text-gray-500 dark:text-gray-400 mb-4 text-center">
|
|
||||||
<Trans
|
|
||||||
i18nKey="playground:ollamaState.connectionError"
|
|
||||||
components={{
|
|
||||||
anchor: (
|
|
||||||
<a
|
|
||||||
href="https://github.com/n4ze3m/page-assist/blob/main/docs/connection-issue.md"
|
|
||||||
target="__blank"
|
|
||||||
className="text-blue-600 dark:text-blue-400"></a>
|
|
||||||
)
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</p>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
) : null}
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* 卡片网格布局 */}
|
||||||
|
<Row gutter={[16, 16]} className="w-full">
|
||||||
|
{questions.map((item, index) => (
|
||||||
|
<Col key={index} xs={24} sm={12} md={8}>
|
||||||
|
<Card
|
||||||
|
hoverable
|
||||||
|
style={{backgroundColor: "#f3f4f6"}}
|
||||||
|
className="border border-gray-200 rounded-lg shadow-sm hover:shadow-md transition-shadow duration-200 cursor-pointer"
|
||||||
|
onClick={() => handleQuestion(item.title)}
|
||||||
|
>
|
||||||
|
<div className="flex items-center">
|
||||||
|
<div className="text-blue-500 mr-2">{item.icon}</div>
|
||||||
|
<div className="font-medium text-sm text-gray-800">{item.title}</div>
|
||||||
|
</div>
|
||||||
|
</Card>
|
||||||
|
</Col>
|
||||||
|
))}
|
||||||
|
</Row>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -207,11 +207,11 @@ export const PlaygroundForm = ({ dropedFile }: Props) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex w-full flex-col items-center p-2 pt-1 pb-4">
|
<div className="flex w-full flex-col items-center p-2 px-5 pt-1 pb-4">
|
||||||
<div className="relative z-10 flex w-full flex-col items-center justify-center gap-2 text-base">
|
<div className="relative z-10 flex w-full flex-col items-center justify-center gap-2 text-base">
|
||||||
<div className="relative flex w-full flex-row justify-center gap-2 lg:w-4/5">
|
<div className="relative flex w-full flex-row justify-center gap-2 lg:w-5/5">
|
||||||
<div
|
<div
|
||||||
className={` bg-neutral-50 dark:bg-[#262626] relative w-full max-w-[48rem] p-1 backdrop-blur-lg duration-100 border border-gray-300 rounded-xl dark:border-gray-600
|
className={` bg-neutral-50 dark:bg-[#262626] relative w-full max-w-[65rem] p-1 backdrop-blur-lg duration-100 border border-gray-300 rounded-xl dark:border-gray-600
|
||||||
${temporaryChat ? "!bg-gray-200 dark:!bg-black " : ""}
|
${temporaryChat ? "!bg-gray-200 dark:!bg-black " : ""}
|
||||||
`}>
|
`}>
|
||||||
<div
|
<div
|
||||||
|
@ -60,6 +60,7 @@ export const useMessageOption = () => {
|
|||||||
setHistory,
|
setHistory,
|
||||||
meteringEntries,
|
meteringEntries,
|
||||||
setMeteringEntries,
|
setMeteringEntries,
|
||||||
|
setCurrentMeteringEntry,
|
||||||
setStreaming,
|
setStreaming,
|
||||||
streaming,
|
streaming,
|
||||||
setIsFirstMessage,
|
setIsFirstMessage,
|
||||||
@ -200,6 +201,11 @@ export const useMessageOption = () => {
|
|||||||
date: new Date().getTime()
|
date: new Date().getTime()
|
||||||
} as MeteringEntry
|
} as MeteringEntry
|
||||||
|
|
||||||
|
setCurrentMeteringEntry({
|
||||||
|
loading: true,
|
||||||
|
data: meter
|
||||||
|
})
|
||||||
|
|
||||||
if (!isRegenerate) {
|
if (!isRegenerate) {
|
||||||
newMessage = [
|
newMessage = [
|
||||||
...messages,
|
...messages,
|
||||||
@ -500,7 +506,7 @@ export const useMessageOption = () => {
|
|||||||
|
|
||||||
// Save metering entry
|
// Save metering entry
|
||||||
const { cot, content } = responseResolver(fullText)
|
const { cot, content } = responseResolver(fullText)
|
||||||
const _meteringEntries = [{
|
const currentMeteringEntry = {
|
||||||
...meter,
|
...meter,
|
||||||
modelInputTokenCount: prompt.length,
|
modelInputTokenCount: prompt.length,
|
||||||
modelOutputTokenCount: fullText.length,
|
modelOutputTokenCount: fullText.length,
|
||||||
@ -511,9 +517,15 @@ export const useMessageOption = () => {
|
|||||||
cot,
|
cot,
|
||||||
responseContent: content,
|
responseContent: content,
|
||||||
modelResponseContent: fullText,
|
modelResponseContent: fullText,
|
||||||
},
|
}
|
||||||
|
const _meteringEntries = [
|
||||||
|
currentMeteringEntry,
|
||||||
...meteringEntries,
|
...meteringEntries,
|
||||||
]
|
]
|
||||||
|
setCurrentMeteringEntry({
|
||||||
|
loading: false,
|
||||||
|
data: currentMeteringEntry,
|
||||||
|
})
|
||||||
setMeteringEntries(_meteringEntries)
|
setMeteringEntries(_meteringEntries)
|
||||||
localStorage.setItem("meteringEntries", JSON.stringify(_meteringEntries))
|
localStorage.setItem("meteringEntries", JSON.stringify(_meteringEntries))
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
@ -633,6 +645,16 @@ export const useMessageOption = () => {
|
|||||||
|
|
||||||
let newMessage: Message[] = []
|
let newMessage: Message[] = []
|
||||||
let generateMessageId = generateID()
|
let generateMessageId = generateID()
|
||||||
|
const meter: MeteringEntry = {
|
||||||
|
id: generateMessageId,
|
||||||
|
queryContent: message,
|
||||||
|
date: new Date().getTime()
|
||||||
|
} as MeteringEntry
|
||||||
|
|
||||||
|
setCurrentMeteringEntry({
|
||||||
|
loading: true,
|
||||||
|
data: meter,
|
||||||
|
})
|
||||||
|
|
||||||
if (!isRegenerate) {
|
if (!isRegenerate) {
|
||||||
newMessage = [
|
newMessage = [
|
||||||
@ -759,6 +781,7 @@ export const useMessageOption = () => {
|
|||||||
let reasoningStartTime: Date | null = null
|
let reasoningStartTime: Date | null = null
|
||||||
let reasoningEndTime: Date | null = null
|
let reasoningEndTime: Date | null = null
|
||||||
let apiReasoning: boolean = false
|
let apiReasoning: boolean = false
|
||||||
|
const chatStartTime = new Date()
|
||||||
|
|
||||||
for await (const chunk of chunks) {
|
for await (const chunk of chunks) {
|
||||||
if (chunk?.additional_kwargs?.reasoning_content) {
|
if (chunk?.additional_kwargs?.reasoning_content) {
|
||||||
@ -859,6 +882,31 @@ export const useMessageOption = () => {
|
|||||||
setStreaming(false)
|
setStreaming(false)
|
||||||
setIsProcessing(false)
|
setIsProcessing(false)
|
||||||
setStreaming(false)
|
setStreaming(false)
|
||||||
|
|
||||||
|
|
||||||
|
// Save metering entry
|
||||||
|
const { cot, content } = responseResolver(fullText)
|
||||||
|
const currentMeteringEntry = {
|
||||||
|
...meter,
|
||||||
|
modelInputTokenCount: prompt.length,
|
||||||
|
modelOutputTokenCount: fullText.length,
|
||||||
|
model: ollama.modelName ?? ollama.model,
|
||||||
|
relatedDataCount: 0,
|
||||||
|
timeTaken: new Date().getTime() - chatStartTime.getTime(),
|
||||||
|
date: chatStartTime.getTime(),
|
||||||
|
cot,
|
||||||
|
responseContent: content,
|
||||||
|
modelResponseContent: fullText,
|
||||||
|
}
|
||||||
|
const _meteringEntries = [
|
||||||
|
currentMeteringEntry,
|
||||||
|
...meteringEntries,
|
||||||
|
]
|
||||||
|
setCurrentMeteringEntry({
|
||||||
|
loading: false,
|
||||||
|
data: currentMeteringEntry,
|
||||||
|
})
|
||||||
|
setMeteringEntries(_meteringEntries)
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
const errorSave = await saveMessageOnError({
|
const errorSave = await saveMessageOnError({
|
||||||
e,
|
e,
|
||||||
|
@ -35,6 +35,8 @@ type State = {
|
|||||||
setMessages: (messages: Message[]) => void
|
setMessages: (messages: Message[]) => void
|
||||||
history: ChatHistory
|
history: ChatHistory
|
||||||
setHistory: (history: ChatHistory) => void
|
setHistory: (history: ChatHistory) => void
|
||||||
|
currentMeteringEntry: {data: MeteringEntry, loading: boolean}
|
||||||
|
setCurrentMeteringEntry: (meteringEntry: {data: MeteringEntry, loading: boolean}) => void
|
||||||
meteringEntries: MeteringEntry[]
|
meteringEntries: MeteringEntry[]
|
||||||
setMeteringEntries: (meteringEntries: MeteringEntry[]) => void
|
setMeteringEntries: (meteringEntries: MeteringEntry[]) => void
|
||||||
streaming: boolean
|
streaming: boolean
|
||||||
@ -120,6 +122,8 @@ export const useStoreMessageOption = create<State>((set) => ({
|
|||||||
setMessages: (messages) => set({ messages }),
|
setMessages: (messages) => set({ messages }),
|
||||||
history: [],
|
history: [],
|
||||||
setHistory: (history) => set({ history }),
|
setHistory: (history) => set({ history }),
|
||||||
|
currentMeteringEntry: {data: {} as MeteringEntry, loading: false},
|
||||||
|
setCurrentMeteringEntry: (currentMeteringEntry) => set({ currentMeteringEntry }),
|
||||||
meteringEntries: JSON.parse(localStorage.getItem("meteringEntries") || JSON.stringify([])),
|
meteringEntries: JSON.parse(localStorage.getItem("meteringEntries") || JSON.stringify([])),
|
||||||
setMeteringEntries: (meteringEntries) => set({ meteringEntries }),
|
setMeteringEntries: (meteringEntries) => set({ meteringEntries }),
|
||||||
streaming: false,
|
streaming: false,
|
||||||
|
@ -26,10 +26,10 @@ export const tokenizeInput = function (input: string): string[] {
|
|||||||
}
|
}
|
||||||
//doipUrl = tcp://reg01.public.internetofdata.cn:21037
|
//doipUrl = tcp://reg01.public.internetofdata.cn:21037
|
||||||
export const iodConfig = {
|
export const iodConfig = {
|
||||||
"gatewayUrl": "tcp://127.0.0.1:21036",
|
"gatewayUrl": "tcp://021.node.internetapi.cn:21052",
|
||||||
"registry":"bdware/Registry",
|
"registry":"data/Registry",
|
||||||
"localRepository":"bdtest.local/myrepo1",
|
"localRepository":"data/Repository",
|
||||||
"doBrowser":"http://127.0.0.1:21030/SCIDE/SCManager"
|
"doBrowser":"http://021.node.internetapi.cn:21030/SCIDE/SCManager"
|
||||||
}
|
}
|
||||||
function inGrepList(str: string){
|
function inGrepList(str: string){
|
||||||
return "什么|问题|需要|合适|设计|考虑|合作|精度|传感器|最新|研究|药物".indexOf(str)!=-1;
|
return "什么|问题|需要|合适|设计|考虑|合作|精度|传感器|最新|研究|药物".indexOf(str)!=-1;
|
||||||
@ -185,6 +185,7 @@ export async function localIodSearch(
|
|||||||
const abortController = new AbortController();
|
const abortController = new AbortController();
|
||||||
setTimeout(() => abortController.abort(), 10000);
|
setTimeout(() => abortController.abort(), 10000);
|
||||||
const params = makeRegSearchParams(TOTAL_SEARCH_RESULTS, keywords);
|
const params = makeRegSearchParams(TOTAL_SEARCH_RESULTS, keywords);
|
||||||
|
console.log('params------->',params)
|
||||||
try {
|
try {
|
||||||
const response = await fetch(iodConfig.doBrowser, {
|
const response = await fetch(iodConfig.doBrowser, {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
@ -194,13 +195,11 @@ export async function localIodSearch(
|
|||||||
|
|
||||||
const res = await response.json();
|
const res = await response.json();
|
||||||
if (res.status !== "Success") {
|
if (res.status !== "Success") {
|
||||||
console.log(res);
|
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
const body = JSON.parse(res.result.body);
|
const body = JSON.parse(res.result.body);
|
||||||
if (body.code !== 0) {
|
if (body.code !== 0) {
|
||||||
console.log(body);
|
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,6 +5,9 @@ module.exports = {
|
|||||||
content: ["./src/**/*.tsx"],
|
content: ["./src/**/*.tsx"],
|
||||||
theme: {
|
theme: {
|
||||||
extend: {
|
extend: {
|
||||||
|
width: {
|
||||||
|
'1/10': '10%',
|
||||||
|
},
|
||||||
backgroundImage: {
|
backgroundImage: {
|
||||||
'bottom-mask-light': 'linear-gradient(0deg, transparent 0, #ffffff 160px)',
|
'bottom-mask-light': 'linear-gradient(0deg, transparent 0, #ffffff 160px)',
|
||||||
'bottom-mask-dark': 'linear-gradient(0deg, transparent 0, #171717 160px)',
|
'bottom-mask-dark': 'linear-gradient(0deg, transparent 0, #171717 160px)',
|
||||||
|