Compare commits
3 Commits
main
...
feat/layou
Author | SHA1 | Date | |
---|---|---|---|
|
224ae55d6b | ||
|
8a5c5f1c26 | ||
|
9e379d13cb |
2
.gitignore
vendored
@ -2,7 +2,7 @@
|
||||
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
|
||||
# settings
|
||||
.vscode
|
||||
video.mp4
|
||||
|
||||
# dependencies
|
||||
/node_modules
|
||||
/.pnp
|
||||
|
@ -35,7 +35,6 @@
|
||||
"cheerio": "^1.0.0-rc.12",
|
||||
"d3-dsv": "2",
|
||||
"dayjs": "^1.11.10",
|
||||
"framer-motion": "^12.23.12",
|
||||
"html-to-text": "^9.0.5",
|
||||
"i18next": "^23.10.1",
|
||||
"i18next-browser-languagedetector": "^7.2.0",
|
||||
@ -48,7 +47,6 @@
|
||||
"property-information": "^6.4.1",
|
||||
"pubsub-js": "^1.9.4",
|
||||
"react": "18.2.0",
|
||||
"react-countup": "^6.5.3",
|
||||
"react-dom": "18.2.0",
|
||||
"react-i18next": "^14.1.0",
|
||||
"react-icons": "^5.2.1",
|
||||
|
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 |
@ -88,7 +88,7 @@
|
||||
"system": {
|
||||
"heading": "系统设置",
|
||||
"deleteChatHistory": {
|
||||
"label": "清除最近对话",
|
||||
"label": "系统重置",
|
||||
"button": "全部重置",
|
||||
"confirm": "您确定要执行系统重置吗?这将清除所有数据且无法撤消。"
|
||||
},
|
||||
@ -316,10 +316,6 @@
|
||||
"title": "管理知识",
|
||||
"heading": "配置知识库"
|
||||
},
|
||||
"iodSettings": {
|
||||
"title": "数联网 设置",
|
||||
"heading": "配置数联网"
|
||||
},
|
||||
"rag": {
|
||||
"title": "RAG 设置",
|
||||
"ragSettings": {
|
||||
|
Before Width: | Height: | Size: 224 KiB |
@ -6,7 +6,7 @@
|
||||
|
||||
.arimo {
|
||||
font-family: "Arimo", sans-serif;
|
||||
/*font-weight: 500;*/
|
||||
font-weight: 500;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
|
@ -1,22 +1,18 @@
|
||||
import React from "react"
|
||||
import { Typography } from "antd"
|
||||
import { ChevronRightIcon } from "@heroicons/react/24/outline"
|
||||
import React from "react";
|
||||
import { Typography, Button } from "antd";
|
||||
import { AcademicCapIcon, ChevronRightIcon } from "@heroicons/react/24/outline";
|
||||
|
||||
const { Title } = Typography
|
||||
const { Title } = Typography;
|
||||
|
||||
type Props = {
|
||||
Header: React.ReactNode
|
||||
showButton?: boolean
|
||||
onClick?: () => void
|
||||
}
|
||||
Header: React.ReactNode;
|
||||
showButton?: boolean;
|
||||
onClick?: () => void;
|
||||
};
|
||||
|
||||
export const DataNavigation: React.FC<Props> = ({
|
||||
Header,
|
||||
showButton = true,
|
||||
onClick
|
||||
}) => {
|
||||
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">
|
||||
<div className="flex items-center justify-between bg-white dark:bg-gray-800 rounded-lg mb-3">
|
||||
{/* 左侧部分 */}
|
||||
<div className="flex items-center">
|
||||
<Title
|
||||
@ -29,13 +25,11 @@ export const DataNavigation: React.FC<Props> = ({
|
||||
|
||||
{/* 右侧部分 */}
|
||||
{showButton && (
|
||||
<div
|
||||
className="flex items-center text-[#3a3a3a] cursor-pointer space-x-0.5 hover:text-[#3581e3] transition-colors duration-200"
|
||||
onClick={onClick}>
|
||||
<div className="flex items-center text-[#3a3a3a] cursor-pointer space-x-0.5 hover:text-[#00c0ef] transition" onClick={onClick}>
|
||||
<span className="text-[12px]">更多</span>
|
||||
<ChevronRightIcon className="w-4 h-4" />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
};
|
||||
|
@ -11,11 +11,6 @@ export const PageAssistProvider = ({
|
||||
const [controller, setController] = React.useState<AbortController | null>(
|
||||
null
|
||||
)
|
||||
|
||||
const [iodLoading, setIodLoading] = React.useState<boolean>(false)
|
||||
const [currentMessageId, setCurrentMessageId] = React.useState<string>('')
|
||||
|
||||
|
||||
const [embeddingController, setEmbeddingController] =
|
||||
React.useState<AbortController | null>(null)
|
||||
|
||||
@ -25,12 +20,6 @@ export const PageAssistProvider = ({
|
||||
messages,
|
||||
setMessages,
|
||||
|
||||
iodLoading,
|
||||
setIodLoading,
|
||||
|
||||
currentMessageId,
|
||||
setCurrentMessageId,
|
||||
|
||||
controller,
|
||||
setController,
|
||||
|
||||
|
@ -1,139 +1,150 @@
|
||||
import React, { useMemo } from "react"
|
||||
import React from "react"
|
||||
import { DataNavigation } from "@/components/Common/DataNavigation.tsx"
|
||||
import { Card, Skeleton } from "antd"
|
||||
import { useMessageOption } from "@/hooks/useMessageOption.tsx"
|
||||
import { IodRegistryEntry } from "@/types/iod.ts"
|
||||
import { useIodPlaygroundContext } from "@/components/Option/Playground/PlaygroundIod.tsx"
|
||||
import { Card, Drawer, List } from "antd"
|
||||
import { useCallback, useState } from "react"
|
||||
|
||||
// import { Drawer } from './Drawer.tsx'
|
||||
|
||||
const defaultData: IodRegistryEntry[] = [
|
||||
{
|
||||
name: "固态电池固体电解质材料数据集",
|
||||
doId: "CSTR:16666.11.nbsdc.9bjqrscd",
|
||||
description: "国家基础学科公共科学数据中心"
|
||||
},
|
||||
{
|
||||
name: "固体颗粒物与流体耦合",
|
||||
doId: "CSTR:16666.11.nbsdc.xyzbycl7",
|
||||
description: "清华大学"
|
||||
}
|
||||
]
|
||||
|
||||
type HeaderProps = {
|
||||
title: string
|
||||
showButton?: boolean
|
||||
onClick?: () => void
|
||||
}
|
||||
const Header: React.FC<HeaderProps> = ({
|
||||
title,
|
||||
showButton = true,
|
||||
onClick
|
||||
}) => (
|
||||
<DataNavigation
|
||||
Header={
|
||||
<div className="flex items-center gap-0.5 text-[#3581e3]">
|
||||
<svg
|
||||
className="icon"
|
||||
viewBox="0 0 1024 1024"
|
||||
version="1.1"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
p-id="3572"
|
||||
width="18"
|
||||
height="18">
|
||||
<path
|
||||
d="M877.714286 54.857143H754.285714V9.142857c0-5.028571-4.114286-9.142857-9.142857-9.142857h-64c-5.028571 0-9.142857 4.114286-9.142857 9.142857v45.714286H498.285714V9.142857c0-5.028571-4.114286-9.142857-9.142857-9.142857h-64c-5.028571 0-9.142857 4.114286-9.142857 9.142857v45.714286H292.571429c-20.228571 0-36.571429 16.342857-36.571429 36.571428v137.142858h-109.714286c-20.228571 0-36.571429 16.342857-36.571428 36.571428v722.285714c0 20.228571 16.342857 36.571429 36.571428 36.571429h585.142857c20.228571 0 36.571429-16.342857 36.571429-36.571429v-109.714285h109.714286c20.228571 0 36.571429-16.342857 36.571428-36.571429V91.428571c0-20.228571-16.342857-36.571429-36.571428-36.571428zM685.714286 941.714286H192V310.857143h249.142857v198.857143c0 25.257143 20.457143 45.714286 45.714286 45.714285h198.857143v386.285715z m0-459.428572H514.285714V310.857143h0.228572L685.714286 482.057143v0.228571z m146.285714 313.142857h-64V448L548.571429 228.571429H338.285714v-91.428572h77.714286v36.571429c0 5.028571 4.114286 9.142857 9.142857 9.142857h64c5.028571 0 9.142857-4.114286 9.142857-9.142857v-36.571429h173.714286v36.571429c0 5.028571 4.114286 9.142857 9.142857 9.142857h64c5.028571 0 9.142857-4.114286 9.142857-9.142857v-36.571429h77.714286v658.285714z"
|
||||
p-id="3573"
|
||||
fill="#3581e3"></path>
|
||||
</svg>
|
||||
{title}
|
||||
</div>
|
||||
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: "热 榜 第2"
|
||||
},
|
||||
{
|
||||
title: "祁连山老虎沟大本营10米气象每日值数据集(V1.0)(2018-2023)",
|
||||
description:
|
||||
"中国科学院西北生态环境资源研究院,2021年8月3日发布,2021年8月3日20:48更新",
|
||||
time: "包括2019年8月,2021年8月和2024年6月",
|
||||
metadata: "热 榜 第2"
|
||||
},
|
||||
{
|
||||
title: "李嘉图为研究老虎沟大本营2014-2018年...",
|
||||
description:
|
||||
"中国科学院西北生态环境资源研究院,2021年8月3日发布,2021年8月3日20:48更新",
|
||||
time: "包括2019年8月,2021年8月和2024年6月",
|
||||
metadata: "热 榜 第2"
|
||||
},
|
||||
{
|
||||
title: "青海玉树B1区俄日矿勘探数据2017-2023",
|
||||
description:
|
||||
"数字中国集团,CSTR:3260.11.1528414774204895456,DT2023年地质勘探补充调查",
|
||||
time: "包括2019年8月,2021年8月和2024年6月",
|
||||
metadata: "热 榜 第2"
|
||||
}
|
||||
showButton={showButton}
|
||||
onClick={onClick}
|
||||
/>
|
||||
)
|
||||
]
|
||||
|
||||
type MainProps = {
|
||||
loading: boolean
|
||||
data: IodRegistryEntry[]
|
||||
truncate?: boolean
|
||||
}
|
||||
const Main: React.FC<MainProps> = ({ data, loading, truncate = true }) => (
|
||||
<div className="space-y-1.5 flex-1 overflow-y-auto">
|
||||
{data.map((item, index) => {
|
||||
return (
|
||||
<Card
|
||||
className="[&_.ant-card-body]:!p-2 !bg-[gb(248, 248, 248)] border !border-[#e9e9e9]"
|
||||
key={item.doId}>
|
||||
{loading ? (
|
||||
<Skeleton title={false} active />
|
||||
) : (
|
||||
<div className="flex flex-col gap-0.5">
|
||||
<h3
|
||||
className={`text-base font-medium mb-1 text-[#222222] break-all ${truncate ? "line-clamp-2" : ""}`}
|
||||
title={item.name}>
|
||||
{item.name}
|
||||
</h3>
|
||||
<p
|
||||
className={`text-sm text-[#383838] break-all ${truncate ? "line-clamp-2" : ""}`}
|
||||
title={item.doId}>
|
||||
数字对象标识:{item.doId}
|
||||
</p>
|
||||
<p
|
||||
className={`text-[#828282] text-xs break-all ${truncate ? "truncate" : ""}`}
|
||||
title={item.description}>
|
||||
{item.description}
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
</Card>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
)
|
||||
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月"
|
||||
})
|
||||
}
|
||||
|
||||
type Props = {
|
||||
className?: string
|
||||
}
|
||||
export const PlaygroundData: React.FC<Props> = ({ className }) => {
|
||||
const { iodLoading } = useMessageOption()
|
||||
const [open, setOpen] = useState(false)
|
||||
|
||||
const {
|
||||
setShowPlayground,
|
||||
setDetailHeader,
|
||||
setDetailMain,
|
||||
currentIodMessage
|
||||
} = useIodPlaygroundContext()
|
||||
const showDrawer = () => {
|
||||
setOpen(true)
|
||||
}
|
||||
|
||||
const data = useMemo<IodRegistryEntry[]>(() => {
|
||||
return currentIodMessage ? currentIodMessage.data?.data ?? [] : defaultData
|
||||
}, [currentIodMessage])
|
||||
|
||||
const title = useMemo(() => {
|
||||
return currentIodMessage ? "推荐数据" : "热点数据"
|
||||
}, [currentIodMessage])
|
||||
|
||||
const showMore = () => {
|
||||
setShowPlayground(false)
|
||||
setDetailHeader(
|
||||
<Header
|
||||
title={title}
|
||||
showButton={false}
|
||||
onClick={() => setShowPlayground(false)}
|
||||
/>
|
||||
)
|
||||
setDetailMain(<Main loading={iodLoading && Boolean(currentIodMessage)} data={data} truncate={false} />)
|
||||
const onClose = () => {
|
||||
setOpen(false)
|
||||
}
|
||||
|
||||
return (
|
||||
<Card className={`${className}`} hoverable>
|
||||
<div className="h-full flex flex-col gap-2 relative">
|
||||
<Card
|
||||
className="h-full [&_.ant-card-body]:h-full [&_.ant-card-body]:!p-[20px] overflow-y-hidden"
|
||||
hoverable>
|
||||
<div className="h-full flex flex-col relative">
|
||||
{/* 数据导航 */}
|
||||
<Header title={title} onClick={showMore} />
|
||||
<DataNavigation
|
||||
Header={
|
||||
<div className="flex items-center gap-0.5 text-[#3480e3]">
|
||||
<svg
|
||||
className="icon"
|
||||
viewBox="0 0 1024 1024"
|
||||
version="1.1"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
p-id="3572"
|
||||
width="18"
|
||||
height="18">
|
||||
<path
|
||||
d="M877.714286 54.857143H754.285714V9.142857c0-5.028571-4.114286-9.142857-9.142857-9.142857h-64c-5.028571 0-9.142857 4.114286-9.142857 9.142857v45.714286H498.285714V9.142857c0-5.028571-4.114286-9.142857-9.142857-9.142857h-64c-5.028571 0-9.142857 4.114286-9.142857 9.142857v45.714286H292.571429c-20.228571 0-36.571429 16.342857-36.571429 36.571428v137.142858h-109.714286c-20.228571 0-36.571429 16.342857-36.571428 36.571428v722.285714c0 20.228571 16.342857 36.571429 36.571428 36.571429h585.142857c20.228571 0 36.571429-16.342857 36.571429-36.571429v-109.714285h109.714286c20.228571 0 36.571429-16.342857 36.571428-36.571429V91.428571c0-20.228571-16.342857-36.571429-36.571428-36.571428zM685.714286 941.714286H192V310.857143h249.142857v198.857143c0 25.257143 20.457143 45.714286 45.714286 45.714285h198.857143v386.285715z m0-459.428572H514.285714V310.857143h0.228572L685.714286 482.057143v0.228571z m146.285714 313.142857h-64V448L548.571429 228.571429H338.285714v-91.428572h77.714286v36.571429c0 5.028571 4.114286 9.142857 9.142857 9.142857h64c5.028571 0 9.142857-4.114286 9.142857-9.142857v-36.571429h173.714286v36.571429c0 5.028571 4.114286 9.142857 9.142857 9.142857h64c5.028571 0 9.142857-4.114286 9.142857-9.142857v-36.571429h77.714286v658.285714z"
|
||||
p-id="3573"
|
||||
fill="#3480e3"></path>
|
||||
</svg>
|
||||
相关数据
|
||||
</div>
|
||||
}
|
||||
onClick={showDrawer}
|
||||
/>
|
||||
|
||||
{/* 数据列表 */}
|
||||
<Main loading={iodLoading && Boolean(currentIodMessage)} data={data.slice(0, 3)} />
|
||||
<div className="space-y-1.5 flex-1 overflow-y-auto">
|
||||
{data.slice(0, 3).map((item, index) => (
|
||||
<Card
|
||||
key={index}
|
||||
className="[&_.ant-card-body]:!p-2 !bg-[gb(248, 248, 248)] border !border-[#e9e9e9]">
|
||||
<div className="flex flex-col gap-0.5">
|
||||
<h3 className="text-base font-medium mb-1 text-[#222222] line-clamp-2">
|
||||
{item.title}
|
||||
</h3>
|
||||
<p className="text-sm text-[#383838] line-clamp-2">
|
||||
{item.description}
|
||||
</p>
|
||||
<p className="text-[#828282] text-xs truncate">{item.time}</p>
|
||||
{item.metadata && (
|
||||
<div>
|
||||
<span className="inline-block text-[#D90000] bg-[#eb1c1c30] h-5 leading-5 mt-1 text-xs rounded-full px-2.5">
|
||||
{item.metadata}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</Card>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 抽屉 */}
|
||||
<Drawer
|
||||
title="相关数据"
|
||||
closable={{ "aria-label": "Close Button" }}
|
||||
onClose={onClose}
|
||||
open={open}
|
||||
width="33.33%">
|
||||
<div className="grid grid-cols-1 gap-3 overflow-y-auto">
|
||||
{data.slice(0, 3).map((item, index) => (
|
||||
<Card
|
||||
key={index}
|
||||
hoverable
|
||||
className="[&_.ant-card-body]:!p-2 !bg-[gb(248, 248, 248)] border !border-[#e9e9e9]">
|
||||
<div className="flex flex-col gap-0.5">
|
||||
<h3 className="text-base font-medium mb-1 text-[#222222]">
|
||||
{item.title}
|
||||
</h3>
|
||||
<p className="text-sm text-[#383838]">{item.description}</p>
|
||||
<p className="text-[#828282] text-xs">{item.time}</p>
|
||||
{item.metadata && (
|
||||
<div>
|
||||
<span className="inline-block text-[#D90000] bg-[#eb1c1c30] h-5 leading-5 mt-1 text-xs rounded-full px-2.5">
|
||||
{item.metadata}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</Card>
|
||||
))}
|
||||
</div>
|
||||
</Drawer>
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
|
@ -1,91 +0,0 @@
|
||||
// Drawer.tsx
|
||||
import React, { useEffect } from "react"
|
||||
import styled from "styled-components"
|
||||
import { shadow } from "pdfjs-dist"
|
||||
|
||||
interface DrawerProps {
|
||||
open: boolean
|
||||
onClose: () => void
|
||||
children: React.ReactNode
|
||||
width?: string | number
|
||||
overlay?: boolean
|
||||
keydown?: boolean
|
||||
shadow?: boolean
|
||||
}
|
||||
|
||||
const DrawerOverlay = styled.div<{ isOpen: boolean }>`
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background-color: rgba(0, 0, 0, 0.5);
|
||||
opacity: ${({ isOpen }) => (isOpen ? 1 : 0)};
|
||||
visibility: ${({ isOpen }) => (isOpen ? "visible" : "hidden")};
|
||||
transition:
|
||||
opacity 0.3s ease,
|
||||
visibility 0.3s ease;
|
||||
z-index: 1000;
|
||||
`
|
||||
|
||||
const DrawerContainer = styled.div<{
|
||||
isOpen: boolean
|
||||
width: string | number
|
||||
shadow: boolean
|
||||
}>`
|
||||
position: fixed;
|
||||
top: 0;
|
||||
right: 0;
|
||||
height: 100%;
|
||||
width: ${({ width }) => (typeof width === "number" ? `${width}px` : width)};
|
||||
background: #ffffff;
|
||||
box-shadow: ${shadow ? "-2px 0 8px rgba(0, 0, 0, 0.15)" : ""};
|
||||
transform: translateX(${({ isOpen }) => (isOpen ? "0" : "100%")});
|
||||
transition: transform 0.3s ease;
|
||||
z-index: 9999;
|
||||
overflow-y: auto;
|
||||
`
|
||||
|
||||
export const Drawer: React.FC<DrawerProps> = ({
|
||||
open,
|
||||
onClose,
|
||||
children,
|
||||
overlay = true,
|
||||
keydown = true,
|
||||
shadow = true,
|
||||
width = "300px"
|
||||
}) => {
|
||||
// 处理 Escape 键关闭抽屉
|
||||
useEffect(() => {
|
||||
const handleEscape = (e: KeyboardEvent) => {
|
||||
if (e.key === "Escape" && open) {
|
||||
onClose()
|
||||
}
|
||||
}
|
||||
if (keydown) {
|
||||
document.addEventListener("keydown", handleEscape)
|
||||
}
|
||||
|
||||
return () => {
|
||||
if (keydown) {
|
||||
document.removeEventListener("keydown", handleEscape)
|
||||
}
|
||||
}
|
||||
}, [open, onClose, keydown])
|
||||
|
||||
// 处理点击遮罩层关闭抽屉
|
||||
const handleOverlayClick = (e: React.MouseEvent) => {
|
||||
if (e.target === e.currentTarget) {
|
||||
onClose()
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
{overlay && <DrawerOverlay isOpen={open} onClick={handleOverlayClick} />}
|
||||
<DrawerContainer isOpen={open} width={width}>
|
||||
{children}
|
||||
</DrawerContainer>
|
||||
</>
|
||||
)
|
||||
}
|
@ -1,8 +1,7 @@
|
||||
import { useForm } from "@mantine/form"
|
||||
import React, { useEffect, useState } from "react"
|
||||
import React from "react"
|
||||
import { useTranslation } from "react-i18next"
|
||||
import useDynamicTextareaSize from "~/hooks/useDynamicTextareaSize"
|
||||
import TextArea from "antd/es/input/TextArea"
|
||||
|
||||
type Props = {
|
||||
value: string
|
||||
@ -15,14 +14,6 @@ export const EditMessageForm = (props: Props) => {
|
||||
const [isComposing, setIsComposing] = React.useState(false)
|
||||
const textareaRef = React.useRef<HTMLTextAreaElement>(null)
|
||||
const { t } = useTranslation("common")
|
||||
const [value, setValue] = useState(props.value);
|
||||
|
||||
useEffect(
|
||||
() => {
|
||||
setValue(props.value)
|
||||
},
|
||||
[props.value]
|
||||
);
|
||||
|
||||
const form = useForm({
|
||||
initialValues: {
|
||||
@ -38,27 +29,46 @@ export const EditMessageForm = (props: Props) => {
|
||||
return (
|
||||
<form
|
||||
onSubmit={form.onSubmit((data) => {
|
||||
if (isComposing) return
|
||||
props.onClose()
|
||||
props.onSumbit(value, true)
|
||||
props.onSumbit(data.message, true)
|
||||
})}
|
||||
className="flex flex-col gap-2 w-96 ml-auto">
|
||||
<TextArea
|
||||
className="flex flex-col gap-2">
|
||||
<textarea
|
||||
{...form.getInputProps("message")}
|
||||
onCompositionStart={() => setIsComposing(true)}
|
||||
onCompositionEnd={() => setIsComposing(false)}
|
||||
required
|
||||
rows={2}
|
||||
value={value}
|
||||
rows={1}
|
||||
style={{ minHeight: "60px" }}
|
||||
tabIndex={0}
|
||||
onChange={(e) => {
|
||||
setValue(e.target.value)
|
||||
}}
|
||||
placeholder={t("editMessage.placeholder")}
|
||||
ref={textareaRef}
|
||||
className="w-full bg-transparent focus-within:outline-none focus:ring-0 focus-visible:ring-0 ring-0 dark:ring-0 border-0 dark:text-gray-100"
|
||||
/>
|
||||
<div className="flex flex-wrap gap-2 mt-2">
|
||||
<div
|
||||
className={`w-full flex ${
|
||||
!props.isBot ? "justify-end" : "justify-end"
|
||||
!props.isBot ? "justify-between" : "justify-end"
|
||||
}`}>
|
||||
{!props.isBot && (
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => {
|
||||
props.onSumbit(form.values.message, false)
|
||||
props.onClose()
|
||||
}}
|
||||
aria-label={t("save")}
|
||||
className="border border-gray-600 px-2 py-1.5 rounded-lg text-gray-700 dark:text-gray-300 focus:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:ring-gray-500 hover:bg-gray-100 dark:hover:bg-gray-900 text-sm">
|
||||
{t("save")}
|
||||
</button>
|
||||
)}
|
||||
<div className="flex space-x-2">
|
||||
<button
|
||||
aria-label={t("save")}
|
||||
className="bg-black px-2 py-1.5 rounded-lg text-white focus:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:ring-gray-500 hover:bg-gray-900 text-sm">
|
||||
{props.isBot ? t("save") : t("saveAndSubmit")}
|
||||
</button>
|
||||
|
||||
<button
|
||||
onClick={props.onClose}
|
||||
@ -66,12 +76,6 @@ export const EditMessageForm = (props: Props) => {
|
||||
className="border dark:border-gray-600 px-2 py-1.5 rounded-lg text-gray-700 dark:text-gray-300 focus:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:ring-gray-500 hover:bg-gray-100 dark:hover:bg-gray-900 text-sm">
|
||||
{t("cancel")}
|
||||
</button>
|
||||
|
||||
<button
|
||||
aria-label={t("save")}
|
||||
className="bg-[#0057ff] px-2 py-1.5 rounded-lg text-white focus:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:ring-gray-500 text-sm">
|
||||
{props.isBot ? t("save") : t("saveAndSubmit")}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>{" "}
|
||||
|
@ -1,11 +1,12 @@
|
||||
import { Sidebar } from "@/components/Option/Sidebar.tsx"
|
||||
import React, { useMemo } from "react"
|
||||
import React, { useContext, useMemo, useState } from "react"
|
||||
import { useMessageOption } from "@/hooks/useMessageOption.tsx"
|
||||
import { useStoreChatModelSettings } from "@/store/model.tsx"
|
||||
import {
|
||||
Button,
|
||||
Card,
|
||||
Divider,
|
||||
List,
|
||||
Menu,
|
||||
MenuProps,
|
||||
Popover,
|
||||
@ -16,12 +17,11 @@ import { PageAssitDatabase } from "@/db"
|
||||
import { EraserIcon, PanelLeftIcon } from "lucide-react"
|
||||
import { useTranslation } from "react-i18next"
|
||||
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"
|
||||
import { useOptionLayoutContext } from "@/components/Layouts/Layout.tsx"
|
||||
import { HistoryContext } from "@/components/Layouts/Layout.tsx"
|
||||
import { PlusOutlined, RightOutlined } from "@ant-design/icons"
|
||||
import { qaPrompt } from "@/libs/playground.tsx"
|
||||
import { ProviderIcons } from "@/components/Common/ProviderIcon.tsx"
|
||||
import { fetchChatModels } from "@/services/ollama.ts"
|
||||
import logo from "@/assets/logo.png"
|
||||
|
||||
const ModelIcon = () => {
|
||||
return (
|
||||
@ -41,10 +41,10 @@ const ModelIcon = () => {
|
||||
)
|
||||
}
|
||||
|
||||
export const PlaygroundSidebar = () => {
|
||||
export const PlaygroundHistory = () => {
|
||||
const { setSystemPrompt } = useStoreChatModelSettings()
|
||||
|
||||
const { showOptionSidebar, setShowOptionSidebar, setShowVideo } = useOptionLayoutContext()
|
||||
const { show, setShow } = useContext(HistoryContext)
|
||||
|
||||
const {
|
||||
setMessages,
|
||||
@ -55,8 +55,7 @@ export const PlaygroundSidebar = () => {
|
||||
selectedModel,
|
||||
setSelectedModel,
|
||||
temporaryChat,
|
||||
setSelectedSystemPrompt,
|
||||
stopStreamingRequest
|
||||
setSelectedSystemPrompt
|
||||
} = useMessageOption()
|
||||
|
||||
const { t } = useTranslation(["option", "common", "settings"])
|
||||
@ -68,21 +67,13 @@ export const PlaygroundSidebar = () => {
|
||||
return [
|
||||
{
|
||||
key: "qaPrompt",
|
||||
label: "热点问题",
|
||||
label: "热门搜索",
|
||||
type: "group" as const,
|
||||
children: qaPrompt.map((item) => {
|
||||
return {
|
||||
key: item.id,
|
||||
label: (
|
||||
<div className="flex items-center gap-2 truncate w-full">
|
||||
<p className="w-5 h-5 [&_.ant-avatar]:!w-full [&_.ant-avatar]:!h-full [&_.ant-avatar]:relative [&_.ant-avatar]:-top-3">
|
||||
{item.icon}
|
||||
</p>
|
||||
<span className="flex-1 truncate" title={item.title}>
|
||||
{item.title}
|
||||
</span>
|
||||
</div>
|
||||
)
|
||||
label: <span title={item.title}>{item.title}</span>,
|
||||
icon: <p className="w-3.5">{item.icon}</p>
|
||||
}
|
||||
})
|
||||
}
|
||||
@ -106,36 +97,32 @@ export const PlaygroundSidebar = () => {
|
||||
}
|
||||
|
||||
// 大模型
|
||||
const { data: models, isLoading: isModelsLoading } = useQuery({
|
||||
const {
|
||||
data: models,
|
||||
isLoading: isModelsLoading,
|
||||
refetch
|
||||
} = useQuery({
|
||||
queryKey: ["fetchModel"],
|
||||
queryFn: () => fetchChatModels({ returnEmpty: true }),
|
||||
refetchIntervalInBackground: false,
|
||||
placeholderData: (prev) => prev
|
||||
})
|
||||
|
||||
// 是否隐藏logo
|
||||
const hideLogo = useMemo(() => {
|
||||
return localStorage.getItem("hideLogo") === "true"
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<Card
|
||||
className={`flex flex-col [&_.ant-card-body]:h-full w-[300px] overflow-hidden h-full pb-5 transition-all duration-300 ease-in-out backdrop-blur-lg !bg-[#f3f4f6]`}
|
||||
style={{ width: showOptionSidebar ? "300px" : "0" }}>
|
||||
className={`flex flex-col [&_.ant-card-body]:h-full w-[300px] overflow-hidden h-full pb-5 transition-all duration-300 ease-in-out !bg-[#f3f4f6]`}
|
||||
style={{ width: show ? "300px" : "0" }}>
|
||||
{/*Header*/}
|
||||
<div className="flex flex-col overflow-y-hidden h-full">
|
||||
<div className="flex items-center justify-between transition-all duration-300 ease-in-out w-[250px]">
|
||||
<div className="flex items-center gap-2 cursor-pointer" onClick={() => setShowVideo(true)}>
|
||||
{!hideLogo && <img src={logo} alt="logo" className="w-8" />}
|
||||
<h2 className="text-xl font-bold text-zinc-700 dark:text-zinc-300 mr-3">
|
||||
<span className="text-[#d30100]">数联网</span>科创智能体
|
||||
</h2>
|
||||
</div>
|
||||
<h2 className="text-xl font-bold text-zinc-700 dark:text-zinc-300 mr-3">
|
||||
<span className="text-[#d30100]">数联网</span>科创智能体
|
||||
</h2>
|
||||
|
||||
<button
|
||||
className="text-gray-500 dark:text-gray-400"
|
||||
onClick={() => {
|
||||
setShowOptionSidebar(!showOptionSidebar)
|
||||
setShow(!show)
|
||||
}}>
|
||||
<PanelLeftIcon className="w-6 h-6" />
|
||||
</button>
|
||||
@ -257,7 +244,7 @@ export const PlaygroundSidebar = () => {
|
||||
</div>
|
||||
<div className="overflow-y-auto flex-1 pl-7">
|
||||
<Sidebar
|
||||
onClose={() => setShowOptionSidebar(true)}
|
||||
onClose={() => setShow(true)}
|
||||
setMessages={setMessages}
|
||||
setHistory={setHistory}
|
||||
setHistoryId={setHistoryId}
|
||||
@ -267,7 +254,6 @@ export const PlaygroundSidebar = () => {
|
||||
historyId={historyId}
|
||||
setSystemPrompt={setSystemPrompt}
|
||||
temporaryChat={temporaryChat}
|
||||
stopStreamingRequest={stopStreamingRequest}
|
||||
history={history}
|
||||
/>
|
||||
</div>
|
@ -1,17 +1,8 @@
|
||||
import React, { useEffect, useMemo, useState } from "react"
|
||||
import { Avatar, Card } from "antd"
|
||||
import { AnimatePresence, motion } from "framer-motion" // 使用 CSS-in-JS 方式
|
||||
import styled, { keyframes } from "styled-components"
|
||||
import CountUp from "react-countup"
|
||||
import { TalentPoolIcon } from "@/components/Icons/TalentPool .tsx"
|
||||
import { ResearchPaperIcon } from "@/components/Icons/ResearchPaper.tsx"
|
||||
import { DataProjectIcon } from "@/components/Icons/DataProject.tsx"
|
||||
import { DatasetIcon } from "@/components/Icons/Dataset.tsx"
|
||||
import { TechCompanyIcon } from "@/components/Icons/TechCompany.tsx"
|
||||
import { ResearchInstitutesIcon } from "@/components/Icons/ResearchInstitutes.tsx"
|
||||
import { NSDCIcon } from "@/components/Icons/NSDC.tsx"
|
||||
import { useIodPlaygroundContext } from "@/components/Option/Playground/PlaygroundIod.tsx"
|
||||
import { totalSearchResults } from "@/services/search.ts"
|
||||
import React from "react"
|
||||
import { Button, Card } from "antd"
|
||||
|
||||
// 使用 CSS-in-JS 方式
|
||||
import styled, { keyframes } from 'styled-components'
|
||||
|
||||
const rotate = keyframes`
|
||||
0% {
|
||||
@ -34,44 +25,27 @@ const breathe = keyframes`
|
||||
}
|
||||
`
|
||||
|
||||
// 花瓣 /* ${(props) => (props.playing ? "running" : "paused")}; */
|
||||
const CircleElement = styled.div<{ delay: number }>`
|
||||
position: absolute;
|
||||
width: 300px;
|
||||
height: 160px;
|
||||
background: #3b82f6; // blue-500
|
||||
background: #3b82f6; // blue-500
|
||||
opacity: 0.2;
|
||||
border-radius: 50%;
|
||||
top: 55%;
|
||||
left: 50%;
|
||||
animation:
|
||||
${rotate} 6s linear infinite,
|
||||
${breathe} 2s infinite alternate;
|
||||
animation-delay: ${(props) => props.delay}s;
|
||||
animation-play-state: running;
|
||||
animation-duration: 3s; /* 添加动画总持续时间 */
|
||||
animation-fill-mode: forwards; /* 保持动画结束时的状态 */
|
||||
animation: ${rotate} 6s linear infinite, ${breathe} 2s infinite alternate;
|
||||
animation-delay: ${props => props.delay}s;
|
||||
`
|
||||
|
||||
const FrostedGlassCard = styled(Card)`
|
||||
background: rgba(255, 255, 255, 0.25) !important;
|
||||
backdrop-filter: blur(10px);
|
||||
-webkit-backdrop-filter: blur(10px);
|
||||
border: 1px solid rgba(255, 255, 255, 0.18);
|
||||
`
|
||||
|
||||
const SuccessIcon = React.forwardRef<
|
||||
SVGSVGElement,
|
||||
React.SVGProps<SVGSVGElement>
|
||||
>((props, ref) => {
|
||||
const SuccessIcon = () => {
|
||||
return (
|
||||
<svg
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
className="text-green-500"
|
||||
ref={ref}
|
||||
{...props}>
|
||||
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"
|
||||
@ -81,28 +55,26 @@ const SuccessIcon = React.forwardRef<
|
||||
/>
|
||||
</svg>
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
const LoadingIcon = React.forwardRef<
|
||||
SVGSVGElement,
|
||||
React.SVGProps<SVGSVGElement>
|
||||
>((props, ref) => {
|
||||
const LoadingIcon = () => {
|
||||
return (
|
||||
<svg
|
||||
className="icon animate-spin"
|
||||
viewBox="0 0 1024 1024"
|
||||
version="1.1"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
p-id="29588"
|
||||
ref={ref}
|
||||
{...props}>
|
||||
p-id="8408"
|
||||
width="18"
|
||||
height="18">
|
||||
<path
|
||||
d="M483.712 888.064a52.437333 52.437333 0 1 1 52.48 52.352 52.394667 52.394667 0 0 1-52.48-52.352z m-235.434667-53.76a65.578667 65.578667 0 1 1 46.421334 19.242667 65.962667 65.962667 0 0 1-46.378667-19.242667z m499.584-16.597333a41.984 41.984 0 0 1 59.264-59.434667 42.282667 42.282667 0 0 1 0 59.434667 41.941333 41.941333 0 0 1-59.264 0zM112.853333 546.602667a81.92 81.92 0 1 1 81.92 81.92 81.834667 81.834667 0 0 1-81.92-81.877334z m731.008 0a33.536 33.536 0 1 1 33.493334 33.578666 33.578667 33.578667 0 0 1-33.450667-33.536zM222.208 377.6a102.4 102.4 0 1 1 72.533333 29.866667 102.869333 102.869333 0 0 1-72.533333-29.824z m536.32-53.504a26.666667 26.666667 0 1 1 18.816 7.936 26.368 26.368 0 0 1-18.773333-7.893333zM414.378667 205.184a121.642667 121.642667 0 1 1 121.813333 121.6A121.728 121.728 0 0 1 414.378667 205.226667z"
|
||||
p-id="29589"
|
||||
fill="#4284f6"></path>
|
||||
d="M512 128C299.936 128 128 296.672 128 504.736c0 130.784 67.904 245.984 170.976 313.536l35.52-52.256C248.576 709.696 192 613.696 192 504.736c0-173.376 143.264-313.92 320-313.92s320 140.544 320 313.92c0 98.112-45.856 185.696-117.696 243.296l-73.792-72.416V864h192l-72.768-71.36C843.072 723.52 896 620.16 896 504.704 896 296.672 724.064 128 512 128z"
|
||||
fill="#52c41a"
|
||||
p-id="8409"></path>
|
||||
</svg>
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
const SearchIcon = () => {
|
||||
return (
|
||||
<svg
|
||||
@ -121,274 +93,42 @@ const SearchIcon = () => {
|
||||
)
|
||||
}
|
||||
|
||||
// 自定义统计卡片组件
|
||||
const StatCard: React.FC<{
|
||||
number: number
|
||||
unit?: string
|
||||
label: string
|
||||
decimals?: number
|
||||
icon: React.ReactNode
|
||||
}> = ({ number, unit, label, decimals, icon }) => {
|
||||
return (
|
||||
<div
|
||||
className="flex flex-col items-center justify-center p-3 rounded-xl shadow-sm bg-[rgba(240,245,255,0.3)] backdrop-blur-sm border border-[rgba(200,220,255,0.25)]
|
||||
">
|
||||
<Avatar size={40} className="!bg-[#3581e3b3]" icon={icon} />
|
||||
export const PlaygroundIodRelevant: React.FC = () => {
|
||||
const data = [
|
||||
{
|
||||
title: <p>已在<span className="text-[#9d0000]">29个科学数据中心</span>的<span className="text-[#9d0000]">50万个科学数据集</span>中进行搜索</p>,
|
||||
description: <p>已发现<span className="text-green-700"> 4个 </span>数据集</p>,
|
||||
status: "success"
|
||||
},
|
||||
{
|
||||
title: <p>已在<span className="text-[#9d0000]">100万篇论文</span>、<span className="text-[#9d0000]">2800个科创场景</span>中进行搜索</p>,
|
||||
description: "已发现4个数据集",
|
||||
status: "success"
|
||||
},
|
||||
{
|
||||
title: <p>正在<span className="text-[#9d0000]">1000位智库专家</span>、<span className="text-[#9d0000]">12万个创新机构</span>中进行搜索</p>,
|
||||
status: "loading"
|
||||
}
|
||||
]
|
||||
|
||||
<div className="text-lg font-bold text-[#f00000]">
|
||||
<CountUp
|
||||
end={number}
|
||||
duration={2.5}
|
||||
separator=","
|
||||
decimals={decimals}
|
||||
/>
|
||||
{unit}
|
||||
</div>
|
||||
<div className="text-sm text-[#3581e3] mt-1 flex items-center gap-2">
|
||||
{" "}
|
||||
{label}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export const StatisticGrid: React.FC = () => {
|
||||
return (
|
||||
<div className="p-6">
|
||||
{/* 第一行:3 个卡片 */}
|
||||
<div className="grid grid-cols-3 gap-6 mb-6">
|
||||
<StatCard
|
||||
icon={<NSDCIcon className="w-6 h-6 text-white" color="#3581e3" />}
|
||||
number={11}
|
||||
unit="家"
|
||||
label="国家科学数据中心"
|
||||
/>
|
||||
<StatCard
|
||||
icon={
|
||||
<ResearchInstitutesIcon
|
||||
className="w-6 h-6 text-white"
|
||||
color="#3581e3"
|
||||
/>
|
||||
}
|
||||
number={763}
|
||||
unit="家"
|
||||
label="高等院校和科研机构"
|
||||
/>
|
||||
<StatCard
|
||||
icon={
|
||||
<TechCompanyIcon className="w-6 h-6 text-white" color="#3581e3" />
|
||||
}
|
||||
number={2.1}
|
||||
decimals={1}
|
||||
unit="万"
|
||||
label="科技型企业"
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* 第二行:4 个卡片 */}
|
||||
<div className="grid grid-cols-4 gap-6">
|
||||
<StatCard
|
||||
icon={<DatasetIcon className="w-6 h-6 text-white" color="#3581e3" />}
|
||||
number={537163}
|
||||
label="数据集"
|
||||
/>
|
||||
<StatCard
|
||||
icon={
|
||||
<DataProjectIcon className="w-6 h-6 text-white" color="#3581e3" />
|
||||
}
|
||||
number={183729}
|
||||
label="数据项目"
|
||||
/>
|
||||
<StatCard
|
||||
icon={
|
||||
<ResearchPaperIcon className="w-6 h-6 text-white" color="#3581e3" />
|
||||
}
|
||||
number={1380026}
|
||||
label="数据论文"
|
||||
/>
|
||||
<StatCard
|
||||
icon={
|
||||
<TalentPoolIcon className="w-6 h-6 text-white" color="#3581e3" />
|
||||
}
|
||||
number={2}
|
||||
unit="万"
|
||||
label="科创人才"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
type Props = {
|
||||
className?: string
|
||||
}
|
||||
export const PlaygroundIodRelevant: React.FC<Props> = ({ className }) => {
|
||||
const { iodLoading, iodSearch } = useMessageOption()
|
||||
|
||||
const { currentIodMessage } = useIodPlaygroundContext()
|
||||
|
||||
const showSearchData = useMemo(() => {
|
||||
return currentIodMessage && !iodLoading
|
||||
}, [currentIodMessage, iodLoading])
|
||||
|
||||
const [count, setCount] = useState<number>(0)
|
||||
|
||||
useEffect(() => {
|
||||
totalSearchResults().then((res) => {
|
||||
setCount(res)
|
||||
for (let i = 0; i < 10; i++) {
|
||||
data.push({
|
||||
title: <p>正在<span className="text-[#9d0000]">1000位智库专家</span>、<span className="text-[#9d0000]">12万个创新机构</span>中进行搜索{i}</p>,
|
||||
description: "已发现4个数据集",
|
||||
status: "success"
|
||||
})
|
||||
}, [])
|
||||
|
||||
const getMinNum = (n1: number) => {
|
||||
return Math.min(n1, count)
|
||||
}
|
||||
|
||||
const data = useMemo(() => {
|
||||
const loading = iodSearch && iodLoading
|
||||
const text = loading ? "正" : "已"
|
||||
const text2 = loading ? "进行" : "完成"
|
||||
const text3 = loading ? "……" : ""
|
||||
const duration = loading ? 2.5 : 0
|
||||
|
||||
return [
|
||||
{
|
||||
title: (
|
||||
<p className="font-extrabold">
|
||||
{text}在
|
||||
<span className="text-[#f00000]">
|
||||
<CountUp end={29} duration={duration} separator="," />个
|
||||
</span>
|
||||
国家和省部级科学数据中心、
|
||||
<span className="text-[#f00000]">
|
||||
{" "}
|
||||
<CountUp end={55} duration={duration} separator="," />所
|
||||
</span>
|
||||
高等院校的
|
||||
<span className="text-[#f00000]">
|
||||
<CountUp
|
||||
decimals={1}
|
||||
end={53.7}
|
||||
duration={duration}
|
||||
separator=","
|
||||
/>
|
||||
万个
|
||||
</span>
|
||||
科学数据集中{text2}搜索{text3}
|
||||
</p>
|
||||
),
|
||||
description: showSearchData ? (
|
||||
<p>
|
||||
已发现
|
||||
<span className="text-green-700">
|
||||
{" "}
|
||||
<CountUp
|
||||
end={currentIodMessage?.data.total ?? 0}
|
||||
duration={2.5}
|
||||
separator=","
|
||||
/>
|
||||
个{" "}
|
||||
</span>
|
||||
数据集,引用 {getMinNum(currentIodMessage?.data.total ?? 0)}个 数据集作为参考
|
||||
</p>
|
||||
) : (
|
||||
""
|
||||
)
|
||||
},
|
||||
{
|
||||
title: (
|
||||
<p className="font-extrabold">
|
||||
{text}在
|
||||
<span className="text-[#f00000]">
|
||||
<CountUp end={138} duration={duration} separator="," />
|
||||
万篇
|
||||
</span>
|
||||
数据论文、
|
||||
<span className="text-[#f00000]">
|
||||
<CountUp
|
||||
end={18.3}
|
||||
decimals={1}
|
||||
duration={duration}
|
||||
separator=","
|
||||
/>
|
||||
万个
|
||||
</span>
|
||||
数据项目中{text2}搜索{text3}
|
||||
</p>
|
||||
),
|
||||
description: showSearchData ? (
|
||||
<p>
|
||||
已发现
|
||||
<span className="text-green-700">
|
||||
{" "}
|
||||
<CountUp
|
||||
end={currentIodMessage?.scenario.total ?? 0}
|
||||
duration={2.5}
|
||||
separator=","
|
||||
/>
|
||||
个{" "}
|
||||
</span>
|
||||
场景,引用 {getMinNum(currentIodMessage?.scenario.total ?? 0)}个 场景作为参考
|
||||
</p>
|
||||
) : (
|
||||
""
|
||||
)
|
||||
},
|
||||
{
|
||||
title: (
|
||||
<p className="font-extrabold">
|
||||
{text}在
|
||||
<span className="text-[#f00000]">
|
||||
<CountUp end={763} duration={duration} separator="," />家
|
||||
</span>
|
||||
高等院校和科研机构、
|
||||
<span className="text-[#f00000]">
|
||||
{" "}
|
||||
<CountUp
|
||||
end={2.1}
|
||||
decimals={1}
|
||||
duration={duration}
|
||||
separator=","
|
||||
/>
|
||||
万
|
||||
</span>
|
||||
家科技型企业、
|
||||
<span className="text-[#f00000]">
|
||||
{" "}
|
||||
<CountUp end={2} duration={duration} separator="," />万
|
||||
</span>
|
||||
科技人才中{text2}搜索{text3}
|
||||
</p>
|
||||
),
|
||||
description: showSearchData ? (
|
||||
<p>
|
||||
已发现
|
||||
<span className="text-green-700">
|
||||
{" "}
|
||||
<CountUp
|
||||
end={currentIodMessage?.organization.total ?? 0}
|
||||
duration={2.5}
|
||||
separator=","
|
||||
/>
|
||||
个{" "}
|
||||
</span>
|
||||
组织,引用 {getMinNum(currentIodMessage?.organization.total ?? 0)}个 组织作为参考
|
||||
</p>
|
||||
) : (
|
||||
""
|
||||
)
|
||||
}
|
||||
]
|
||||
}, [showSearchData, iodLoading, count])
|
||||
|
||||
return (
|
||||
<Card
|
||||
hoverable
|
||||
variant="outlined"
|
||||
className={`${className} translate-y-[-2px] !bg-[#d0e6ff] shadow-md`}>
|
||||
className="flex flex-col h-full [&_.ant-card-body]:h-full [&_.ant-card-body]:!p-[20px] translate-y-[-2px] !bg-[#f0f9ff]"
|
||||
>
|
||||
<div className="h-full flex flex-col relative">
|
||||
{/* 花瓣效果 */}
|
||||
<div
|
||||
className={`absolute inset-0 pointer-events-none z-0 overflow-hidden ${showSearchData ? "" : ""}`}>
|
||||
<div className="absolute inset-0 pointer-events-none z-0 overflow-hidden">
|
||||
<div className="absolute top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2 w-64 h-64">
|
||||
<CircleElement delay={0} />
|
||||
<CircleElement delay={1} />
|
||||
@ -398,83 +138,46 @@ export const PlaygroundIodRelevant: React.FC<Props> = ({ className }) => {
|
||||
|
||||
{/* Header */}
|
||||
<div className="p-3">
|
||||
<h2 className="text-xl font-semibold text-[#1a3c87] flex justify-center items-center">
|
||||
<div className="flex items-center gap-2">
|
||||
<h2 className="text-xl font-semibold text-[#08307f] flex justify-between items-center">
|
||||
<div className='flex items-center gap-2'>
|
||||
<SearchIcon />
|
||||
{currentIodMessage ? "科创数联网深度搜索" : "科创数联网连接资源"}
|
||||
数联网搜索相关内容
|
||||
</div>
|
||||
{/*<button className="bg-[#2563eb1a] text-[#08307f] font-medium py-1 px-3 rounded-full text-sm hover:bg-[#2563eb1a] transition-colors float-right">*/}
|
||||
{/* {data.length}个结果*/}
|
||||
{/*</button>*/}
|
||||
<button className="bg-[#2563eb1a] text-[#08307f] font-medium py-1 px-3 rounded-full text-sm hover:bg-[#2563eb1a] transition-colors float-right">
|
||||
{data.length}个结果
|
||||
</button>
|
||||
</h2>
|
||||
<p className="text-sm text-[#1a3c87] mt-1 text-center">
|
||||
{currentIodMessage
|
||||
? "下面是在科创数联网上进行深度搜索得到的相关数据、场景和团队"
|
||||
: "下面是科创数联网连接的数据、场景和团队"}
|
||||
<p className="text-sm text-[#08307f] mt-1 align-middle">
|
||||
下面是在数联网上进行深度搜索的相关内容
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Content */}
|
||||
<div className="space-y-2 flex-1 overflow-y-auto">
|
||||
{currentIodMessage ? (
|
||||
<AnimatePresence mode="wait">
|
||||
<motion.div
|
||||
key="search-results"
|
||||
initial={{ opacity: 0, y: 10 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
exit={{ opacity: 0, y: -10 }}
|
||||
transition={{ duration: 0.3 }}
|
||||
className="space-y-2 flex-1 overflow-y-auto">
|
||||
{data.map((item, index) => (
|
||||
<FrostedGlassCard
|
||||
className="[&_.ant-card-body]:!p-3 [&_.ant-card-body]:h-full shadow-md min-h-[88px]"
|
||||
key={index}>
|
||||
<div
|
||||
className={`flex flex-col gap-2 h-full items-start ${showSearchData ? "justify-start" : "justify-center"}`}>
|
||||
<div className="flex items-center gap-2">
|
||||
<div>
|
||||
{iodSearch && iodLoading ? (
|
||||
<LoadingIcon
|
||||
width={showSearchData ? 16 : 22}
|
||||
height={showSearchData ? 16 : 22}
|
||||
/>
|
||||
) : (
|
||||
<SuccessIcon
|
||||
width={showSearchData ? 16 : 22}
|
||||
height={showSearchData ? 16 : 22}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
<div
|
||||
className={`text-gray-700 ${showSearchData ? "text-sm" : "text-lg"}`}>
|
||||
{item.title}
|
||||
</div>
|
||||
</div>
|
||||
{item.description && (
|
||||
<div className="flex-1">
|
||||
<div className="text-xs text-gray-500 mt-1 pl-7">
|
||||
{item.description}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</FrostedGlassCard>
|
||||
))}
|
||||
</motion.div>
|
||||
</AnimatePresence>
|
||||
) : (
|
||||
<AnimatePresence mode="wait">
|
||||
<motion.div
|
||||
key="statistic-grid"
|
||||
initial={{ opacity: 0, y: 10 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
exit={{ opacity: 0, y: -10 }}
|
||||
transition={{ duration: 0.3 }}
|
||||
className="flex-1 overflow-y-auto">
|
||||
<StatisticGrid />
|
||||
</motion.div>
|
||||
</AnimatePresence>
|
||||
)}
|
||||
{data.map((item, index) => (
|
||||
<Card
|
||||
className="[&>*:first-child]:!p-3 shadow-md"
|
||||
key={index}
|
||||
>
|
||||
<div className="flex items-start gap-2">
|
||||
<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>
|
||||
</Card>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
|
@ -1,34 +1,31 @@
|
||||
import Markdown from "../../Common/Markdown"
|
||||
import React from "react"
|
||||
import { Collapse, Image, Popover, Tag, Tooltip } from "antd"
|
||||
import { Tag, Image, Tooltip, Collapse, Popover } from "antd"
|
||||
import { WebSearch } from "./WebSearch"
|
||||
import {
|
||||
ArrowUpSquare,
|
||||
CheckIcon,
|
||||
ClipboardIcon,
|
||||
InfoIcon,
|
||||
MessageSquareShare,
|
||||
Pen,
|
||||
PlayIcon,
|
||||
RotateCcw,
|
||||
Square,
|
||||
Star,
|
||||
ThumbsUp,
|
||||
ThumbsDown,
|
||||
ThumbsUp
|
||||
MessageSquareShare,
|
||||
ArrowUpSquare
|
||||
} from "lucide-react"
|
||||
import { EditMessageForm } from "./EditMessageForm"
|
||||
import { useTranslation } from "react-i18next"
|
||||
import { MessageSource } from "./MessageSource"
|
||||
import { useTTS } from "@/hooks/useTTS"
|
||||
import { tagColors } from "@/utils/color"
|
||||
import { removeModelSuffix } from "@/db/models"
|
||||
import { GenerationInfo } from "./GenerationInfo"
|
||||
import { parseReasoning } from "@/libs/reasoning"
|
||||
import { humanizeMilliseconds } from "@/utils/humanize-milliseconds"
|
||||
import { AllIodRegistryEntry } from "@/types/iod.ts"
|
||||
import { PiNetwork } from "react-icons/pi"
|
||||
|
||||
type Props = {
|
||||
id?: string
|
||||
message: string
|
||||
message_type?: string
|
||||
hideCopy?: boolean
|
||||
@ -45,18 +42,16 @@ type Props = {
|
||||
webSearch?: {}
|
||||
isSearchingInternet?: boolean
|
||||
webSources?: any[]
|
||||
iodSources?: AllIodRegistryEntry
|
||||
iodSources?: any[]
|
||||
hideEditAndRegenerate?: boolean
|
||||
onSourceClick?: (source: any) => void
|
||||
isTTSEnabled?: boolean
|
||||
generationInfo?: any
|
||||
isStreaming: boolean
|
||||
reasoningTimeTaken?: number
|
||||
iodSearch?: boolean
|
||||
setCurrentMessageId: (id: string) => void
|
||||
}
|
||||
|
||||
export const PlaygroundMessage: React.FC<Props> = (props) => {
|
||||
export const PlaygroundMessage = (props: Props) => {
|
||||
const [isBtnPressed, setIsBtnPressed] = React.useState(false)
|
||||
const [editMode, setEditMode] = React.useState(false)
|
||||
|
||||
@ -65,10 +60,25 @@ export const PlaygroundMessage: React.FC<Props> = (props) => {
|
||||
return (
|
||||
<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={`flex flex-row gap-1 md:gap-1 my-2 m-auto w-full ${props.isBot ? "" : "flex-row-reverse"}`}>
|
||||
<div className="flex flex-col gap-2">
|
||||
<span className="text-xs font-bold text-gray-800 dark:text-white"></span>
|
||||
<div className={`flex flex-row gap-1 md:gap-1 my-2 m-auto w-full ${props.isBot ? "" : "flex-row-reverse"}`}>
|
||||
<div className="w-8 flex flex-col relative items-center justify-center bottom-[8px]">
|
||||
<div className="relative h-7 w-7 p-1 rounded-sm text-white flex items-center justify-center text-opacity-100r">
|
||||
{props.isBot ? (
|
||||
!props.botAvatar ? (
|
||||
<div className="absolute h-8 w-8 rounded-full bg-gradient-to-r from-green-300 to-purple-400 hidden"></div>
|
||||
) : (
|
||||
props.botAvatar
|
||||
)
|
||||
) : !props.userAvatar ? (
|
||||
<div className="absolute h-8 w-8 rounded-full from-blue-400 to-blue-600 bg-gradient-to-r"></div>
|
||||
) : (
|
||||
props.userAvatar
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex w-[calc(100%-50px)] flex-col gap-2 lg:w-[calc(100%-115px)]">
|
||||
<span className="text-xs font-bold text-gray-800 dark:text-white">
|
||||
</span>
|
||||
|
||||
{props.isBot &&
|
||||
props.isSearchingInternet &&
|
||||
@ -82,7 +92,7 @@ export const PlaygroundMessage: React.FC<Props> = (props) => {
|
||||
</Tag>
|
||||
)}
|
||||
</div>
|
||||
<div className={`flex flex-grow flex-col w-full`}>
|
||||
<div className={`flex flex-grow flex-col`}>
|
||||
{!editMode ? (
|
||||
props.isBot ? (
|
||||
<>
|
||||
@ -91,7 +101,6 @@ export const PlaygroundMessage: React.FC<Props> = (props) => {
|
||||
return (
|
||||
<Collapse
|
||||
key={i}
|
||||
defaultActiveKey={["reasoning"]}
|
||||
className="border-none !mb-3"
|
||||
items={[
|
||||
{
|
||||
@ -121,18 +130,13 @@ export const PlaygroundMessage: React.FC<Props> = (props) => {
|
||||
})}
|
||||
</>
|
||||
) : (
|
||||
// <p
|
||||
// className={`bg-[#f1f3f4] font-normal text-[#000000d9] px-4 py-2.5 rounded-2xl prose-lg dark:prose-invert whitespace-pre-line prose-p:leading-relaxed prose-pre:p-0 dark:prose-dark ${
|
||||
// props.message_type && "italic dark:text-gray-400"
|
||||
// } flex flex-row-reverse`}>
|
||||
// {props.message}
|
||||
// </p>
|
||||
<p
|
||||
className={`bg-[#2563eb] font-normal rounded-tr-none
|
||||
text-white px-4 py-2.5 rounded-2xl prose-lg dark:prose-invert whitespace-pre-line prose-p:leading-relaxed prose-pre:p-0 dark:prose-dark ${
|
||||
props.message_type && "italic dark:text-gray-400"
|
||||
} flex flex-row-reverse`}>
|
||||
className={`prose-lg dark:prose-invert whitespace-pre-line prose-p:leading-relaxed prose-pre:p-0 dark:prose-dark ${
|
||||
props.message_type &&
|
||||
"italic dark:text-gray-400"
|
||||
} flex flex-row-reverse`}>
|
||||
{props.message}
|
||||
{/*<span className="bg-[#0000000a] inline-block py-[9px] text-[#000000d9] rounded-xl px-4 font-light text-sm">{props.message}</span>*/}
|
||||
</p>
|
||||
)
|
||||
) : (
|
||||
@ -166,10 +170,9 @@ export const PlaygroundMessage: React.FC<Props> = (props) => {
|
||||
<Collapse
|
||||
className="mt-6"
|
||||
ghost
|
||||
// defaultActiveKey={['webSources']}
|
||||
items={[
|
||||
{
|
||||
key: "webSources",
|
||||
key: "1",
|
||||
label: (
|
||||
<div className="italic text-gray-500 dark:text-gray-400">
|
||||
{t("webCitations")}
|
||||
@ -191,42 +194,34 @@ export const PlaygroundMessage: React.FC<Props> = (props) => {
|
||||
]}
|
||||
/>
|
||||
)}
|
||||
{props.isBot &&
|
||||
props?.iodSources &&
|
||||
Object.values(props?.iodSources)
|
||||
.map((item) => item.data)
|
||||
.flat().length > 0 && (
|
||||
<Collapse
|
||||
className="mt-6"
|
||||
ghost
|
||||
// defaultActiveKey={['iod']}
|
||||
items={[
|
||||
{
|
||||
key: "iod",
|
||||
label: (
|
||||
<div className="italic text-gray-500 dark:text-gray-400">
|
||||
{t("iodCitations")}
|
||||
</div>
|
||||
),
|
||||
children: (
|
||||
<div className="mb-3 flex flex-wrap gap-2">
|
||||
{Object.values(props?.iodSources)
|
||||
.map((item) => item.data)
|
||||
.flat()
|
||||
?.map((source, index) => (
|
||||
<MessageSource
|
||||
onSourceClick={props.onSourceClick}
|
||||
key={index}
|
||||
index={index}
|
||||
source={source}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
]}
|
||||
/>
|
||||
)}
|
||||
{props.isBot && props?.iodSources && props?.iodSources.length > 0 && (
|
||||
<Collapse
|
||||
className="mt-6"
|
||||
ghost
|
||||
items={[
|
||||
{
|
||||
key: "1",
|
||||
label: (
|
||||
<div className="italic text-gray-500 dark:text-gray-400">
|
||||
{t("iodCitations")}
|
||||
</div>
|
||||
),
|
||||
children: (
|
||||
<div className="mb-3 flex flex-wrap gap-2">
|
||||
{props?.iodSources?.map((source, index) => (
|
||||
<MessageSource
|
||||
onSourceClick={props.onSourceClick}
|
||||
key={index}
|
||||
index={index}
|
||||
source={source}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
]}
|
||||
/>
|
||||
)}
|
||||
{!props.isProcessing && !editMode ? (
|
||||
<div
|
||||
className={`space-x-2 gap-2 flex ${
|
||||
@ -239,7 +234,7 @@ export const PlaygroundMessage: React.FC<Props> = (props) => {
|
||||
// : "flex"
|
||||
}`}>
|
||||
{props.isTTSEnabled && (
|
||||
<Tooltip title={t("tts")} className="hidden">
|
||||
<Tooltip title={t("tts")}>
|
||||
<button
|
||||
aria-label={t("tts")}
|
||||
onClick={() => {
|
||||
@ -262,18 +257,6 @@ export const PlaygroundMessage: React.FC<Props> = (props) => {
|
||||
)}
|
||||
{props.isBot && (
|
||||
<>
|
||||
{/*数联网搜索*/}
|
||||
{props.iodSearch && (
|
||||
<Tooltip title="数联网信息">
|
||||
<button
|
||||
onClick={() => props.setCurrentMessageId(props.id)}
|
||||
aria-label="数联网信息"
|
||||
className="flex items-center justify-center w-6 h-6 rounded-full bg-gray-100 dark:bg-gray-800 hover:bg-gray-200 dark:hover:bg-gray-700 transition-colors duration-200 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-gray-500">
|
||||
<PiNetwork className="w-3 h-3 text-gray-400 group-hover:text-gray-500" />
|
||||
</button>
|
||||
</Tooltip>
|
||||
)}
|
||||
|
||||
{!props.hideCopy && (
|
||||
<Tooltip title={t("copyToClipboard")}>
|
||||
<button
|
||||
@ -297,7 +280,6 @@ export const PlaygroundMessage: React.FC<Props> = (props) => {
|
||||
|
||||
{props.generationInfo && (
|
||||
<Popover
|
||||
className="hidden"
|
||||
content={
|
||||
<GenerationInfo generationInfo={props.generationInfo} />
|
||||
}
|
||||
@ -323,8 +305,8 @@ export const PlaygroundMessage: React.FC<Props> = (props) => {
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
{(!props.hideEditAndRegenerate && !props.isBot) && (
|
||||
<Tooltip title={t("edit")} className="hidden">
|
||||
{!props.hideEditAndRegenerate && (
|
||||
<Tooltip title={t("edit")}>
|
||||
<button
|
||||
onClick={() => setEditMode(true)}
|
||||
aria-label={t("edit")}
|
||||
@ -333,51 +315,51 @@ export const PlaygroundMessage: React.FC<Props> = (props) => {
|
||||
</button>
|
||||
</Tooltip>
|
||||
)}
|
||||
{
|
||||
<Tooltip title="收藏" className="hidden">
|
||||
{ (
|
||||
<Tooltip title="收藏">
|
||||
<button
|
||||
aria-label="收藏"
|
||||
className="flex items-center justify-center w-6 h-6 rounded-full bg-gray-100 dark:bg-gray-800 hover:bg-gray-200 dark:hover:bg-gray-700 transition-colors duration-200 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-gray-500">
|
||||
<Star className="w-3 h-3 text-gray-400 group-hover:text-gray-500" />
|
||||
</button>
|
||||
</Tooltip>
|
||||
}
|
||||
{
|
||||
<Tooltip title="发布语用" className="hidden">
|
||||
)}
|
||||
{ (
|
||||
<Tooltip title="发布语用">
|
||||
<button
|
||||
aria-label="发布语用"
|
||||
className="flex items-center justify-center w-6 h-6 rounded-full bg-gray-100 dark:bg-gray-800 hover:bg-gray-200 dark:hover:bg-gray-700 transition-colors duration-200 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-gray-500">
|
||||
<ArrowUpSquare className="w-3 h-3 text-gray-400 group-hover:text-gray-500" />
|
||||
</button>
|
||||
</Tooltip>
|
||||
}
|
||||
{
|
||||
<Tooltip title="发布对话" className="hidden">
|
||||
)}
|
||||
{ (
|
||||
<Tooltip title="发布对话">
|
||||
<button
|
||||
aria-label="发布对话"
|
||||
className="flex items-center justify-center w-6 h-6 rounded-full bg-gray-100 dark:bg-gray-800 hover:bg-gray-200 dark:hover:bg-gray-700 transition-colors duration-200 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-gray-500">
|
||||
<MessageSquareShare className="w-3 h-3 text-gray-400 group-hover:text-gray-500" />
|
||||
</button>
|
||||
</Tooltip>
|
||||
}
|
||||
{
|
||||
<Tooltip title="点赞" className="hidden">
|
||||
)}
|
||||
{ (
|
||||
<Tooltip title="点赞">
|
||||
<button
|
||||
aria-label="点赞"
|
||||
className="flex items-center justify-center w-6 h-6 rounded-full bg-gray-100 dark:bg-gray-800 hover:bg-gray-200 dark:hover:bg-gray-700 transition-colors duration-200 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-gray-500">
|
||||
<ThumbsUp className="w-3 h-3 text-gray-400 group-hover:text-gray-500" />
|
||||
</button>
|
||||
</Tooltip>
|
||||
}
|
||||
{
|
||||
<Tooltip title="点踩" className="hidden">
|
||||
)}
|
||||
{ (
|
||||
<Tooltip title="点踩">
|
||||
<button
|
||||
aria-label="点踩"
|
||||
className="flex items-center justify-center w-6 h-6 rounded-full bg-gray-100 dark:bg-gray-800 hover:bg-gray-200 dark:hover:bg-gray-700 transition-colors duration-200 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-gray-500">
|
||||
<ThumbsDown className="w-3 h-3 text-gray-400 group-hover:text-gray-500" />
|
||||
</button>
|
||||
</Tooltip>
|
||||
}
|
||||
)}
|
||||
</div>
|
||||
) : (
|
||||
// add invisible div to prevent layout shift
|
||||
|
@ -1,150 +1,143 @@
|
||||
import React, { useMemo } from "react"
|
||||
import React, { useState } from "react"
|
||||
import { DataNavigation } from "@/components/Common/DataNavigation.tsx"
|
||||
import { Card, Skeleton } from "antd"
|
||||
import { IodRegistryEntry } from "@/types/iod.ts"
|
||||
import { useIodPlaygroundContext } from "@/components/Option/Playground/PlaygroundIod.tsx"
|
||||
import { Card, Drawer, List } from "antd"
|
||||
|
||||
const defaultData: IodRegistryEntry[] = [
|
||||
{
|
||||
name: "绿色化工工艺项目",
|
||||
description:
|
||||
"基于生物基原料,采用repeal2.0可降解材料技术,开发新型环保材料。",
|
||||
doId: "CSTR:13552.11.01.61.2021.742"
|
||||
},
|
||||
{
|
||||
name: "智能农业解决方案",
|
||||
description: "利用物联网技术,实现精准农业管理,提高农作物产量。",
|
||||
doId: "CSTR:14542.11.01.61.2031.528"
|
||||
},
|
||||
{
|
||||
name: "新能源汽车电池技术",
|
||||
description: "研发高能量密度、长寿命的新型电池材料,推动电动汽车发展。",
|
||||
doId: "CSTR:147842.11.04.91.2031.680"
|
||||
},
|
||||
{
|
||||
name: "碳捕集与封存技术",
|
||||
description: "开发高效的碳捕集技术,减少工业排放,助力碳中和目标。",
|
||||
doId: "CSTR:14242.19.11.61.2131.428"
|
||||
}
|
||||
]
|
||||
|
||||
type HeaderProps = {
|
||||
title: string
|
||||
showButton?: boolean
|
||||
onClick?: () => void
|
||||
}
|
||||
const Header: React.FC<HeaderProps> = ({
|
||||
title,
|
||||
showButton = true,
|
||||
onClick
|
||||
}) => (
|
||||
<DataNavigation
|
||||
Header={
|
||||
<div className="flex items-center text-[#4ab01a] gap-1">
|
||||
<svg
|
||||
className="icon"
|
||||
viewBox="0 0 1025 1024"
|
||||
version="1.1"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
p-id="6235"
|
||||
width="18"
|
||||
height="18">
|
||||
<path
|
||||
d="M980.34571 1.143792c-4.850903 0-9.824354 0.888481-14.797806 2.930966L229.773215 299.724504H20.428686c-11.233669 0-20.424853 9.446494-20.424853 21.180572V702.584302c0 11.74429 9.191184 21.180572 20.424853 21.180573h129.820365c-4.728353 14.808018-7.271248 30.51473-7.271248 46.46654 0 84.119757 68.678568 152.543014 153.176184 152.543014 70.721053 0 130.330986-47.998404 147.93721-112.847312l521.569043 209.59984c4.983664 1.919936 9.957116 2.930966 14.808019 2.930967 21.568645 0 40.839493-18.127057 40.839493-42.371358V43.525362C1021.195415 19.270849 1002.047116 1.143792 980.34571 1.143792zM296.153987 831.250663c-33.833769 0-61.274559-27.308028-61.274558-61.009035 0-14.297397 4.983664-27.951411 14.042086-38.807221l108.374269 43.525362c-2.553107 31.403211-28.972654 56.290895-61.141797 56.290894z m633.12959 74.550713L263.984844 638.501326l-16.462431-6.638077H91.915671V391.626129h155.606742l16.462431-6.638077 665.298733-267.30005v788.113374z m0 0"
|
||||
fill="#4ab01a"
|
||||
p-id="6236"></path>
|
||||
</svg>
|
||||
{title}
|
||||
</div>
|
||||
export const PlaygroundScene = () => {
|
||||
// 模拟数据
|
||||
const data = [
|
||||
{
|
||||
title: "绿色化工工艺项目",
|
||||
description:
|
||||
"基于生物基原料,采用repeal2.0可降解材料技术,开发新型环保材料。",
|
||||
demander: "奥赛康药业 供方:美国Propella公司"
|
||||
},
|
||||
{
|
||||
title: "智能农业解决方案",
|
||||
description: "利用物联网技术,实现精准农业管理,提高农作物产量。",
|
||||
demander: "奥赛康药业 供方:美国Propella公司"
|
||||
},
|
||||
{
|
||||
title: "新能源汽车电池技术",
|
||||
description: "研发高能量密度、长寿命的新型电池材料,推动电动汽车发展。",
|
||||
demander: "奥赛康药业 供方:美国Propella公司"
|
||||
},
|
||||
{
|
||||
title: "碳捕集与封存技术",
|
||||
description: "开发高效的碳捕集技术,减少工业排放,助力碳中和目标。",
|
||||
demander: "奥赛康药业 供方:美国Propella公司"
|
||||
}
|
||||
showButton={showButton}
|
||||
onClick={onClick}
|
||||
/>
|
||||
)
|
||||
]
|
||||
|
||||
type MainProps = {
|
||||
loading: boolean
|
||||
data: IodRegistryEntry[]
|
||||
truncate?: boolean
|
||||
}
|
||||
const Main: React.FC<MainProps> = ({ data, loading, truncate = true }) => (
|
||||
<div className="space-y-1.5 flex-1 overflow-y-auto">
|
||||
{data.map((item, index) => {
|
||||
return (
|
||||
<Card
|
||||
className="[&_.ant-card-body]:!p-2 !bg-[gb(248, 248, 248)] border !border-[#e9e9e9]"
|
||||
key={item.doId}>
|
||||
{loading ? (
|
||||
<Skeleton title={false} active />
|
||||
) : (
|
||||
<div className="flex flex-col gap-0.5">
|
||||
<h3
|
||||
className={`text-base font-medium mb-1 text-[#222222] break-all ${truncate ? "line-clamp-2" : ""}`}
|
||||
title={item.name}>
|
||||
{item.name}
|
||||
</h3>
|
||||
<p
|
||||
className={`text-sm text-[#383838] break-all ${truncate ? "line-clamp-2" : ""}`}
|
||||
title={item.doId}>
|
||||
数字对象标识:{item.doId}
|
||||
</p>
|
||||
<p
|
||||
className={`text-[#828282] text-xs break-all ${truncate ? "truncate" : ""}`}
|
||||
title={item.description}>
|
||||
{item.description}
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
</Card>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
)
|
||||
for (let i = 0; i < 10; i++) {
|
||||
data.push({
|
||||
title: "开发新型电池材料",
|
||||
description: "研发高能量密度、长寿命的新型电池材料,推动电动汽车发展。",
|
||||
demander: "奥赛康药业 供方:美国Propella公司"
|
||||
})
|
||||
}
|
||||
|
||||
type Props = {
|
||||
className?: string
|
||||
}
|
||||
export const PlaygroundScene: React.FC<Props> = ({ className }) => {
|
||||
const { iodLoading } = useMessageOption()
|
||||
const [open, setOpen] = useState(false)
|
||||
|
||||
const {
|
||||
setShowPlayground,
|
||||
setDetailHeader,
|
||||
setDetailMain,
|
||||
currentIodMessage
|
||||
} = useIodPlaygroundContext()
|
||||
const showDrawer = () => {
|
||||
setOpen(true)
|
||||
}
|
||||
|
||||
const data = useMemo<IodRegistryEntry[]>(() => {
|
||||
return currentIodMessage
|
||||
? currentIodMessage.scenario?.data ?? []
|
||||
: defaultData
|
||||
}, [currentIodMessage])
|
||||
|
||||
const title = useMemo(() => {
|
||||
return currentIodMessage ? "推荐场景" : "热点场景"
|
||||
}, [currentIodMessage])
|
||||
|
||||
const showMore = () => {
|
||||
setShowPlayground(false)
|
||||
setDetailHeader(
|
||||
<Header
|
||||
title={title}
|
||||
showButton={false}
|
||||
onClick={() => setShowPlayground(false)}
|
||||
/>
|
||||
)
|
||||
setDetailMain(<Main loading={iodLoading && Boolean(currentIodMessage)} data={data} truncate={false} />)
|
||||
const onClose = () => {
|
||||
setOpen(false)
|
||||
}
|
||||
|
||||
return (
|
||||
<Card className={`${className}`} hoverable>
|
||||
<div className="h-full flex flex-col gap-2 relative">
|
||||
<Card
|
||||
className="h-full [&_.ant-card-body]:h-full [&_.ant-card-body]:!p-[20px] overflow-y-hidden"
|
||||
hoverable>
|
||||
<div className="h-full flex flex-col relative">
|
||||
{/* 数据导航 */}
|
||||
<Header title={title} onClick={showMore} />
|
||||
<DataNavigation
|
||||
Header={
|
||||
<div className="flex items-center text-[#52c41a] gap-1">
|
||||
<svg
|
||||
className="icon"
|
||||
viewBox="0 0 1025 1024"
|
||||
version="1.1"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
p-id="6235"
|
||||
width="18"
|
||||
height="18">
|
||||
<path
|
||||
d="M980.34571 1.143792c-4.850903 0-9.824354 0.888481-14.797806 2.930966L229.773215 299.724504H20.428686c-11.233669 0-20.424853 9.446494-20.424853 21.180572V702.584302c0 11.74429 9.191184 21.180572 20.424853 21.180573h129.820365c-4.728353 14.808018-7.271248 30.51473-7.271248 46.46654 0 84.119757 68.678568 152.543014 153.176184 152.543014 70.721053 0 130.330986-47.998404 147.93721-112.847312l521.569043 209.59984c4.983664 1.919936 9.957116 2.930966 14.808019 2.930967 21.568645 0 40.839493-18.127057 40.839493-42.371358V43.525362C1021.195415 19.270849 1002.047116 1.143792 980.34571 1.143792zM296.153987 831.250663c-33.833769 0-61.274559-27.308028-61.274558-61.009035 0-14.297397 4.983664-27.951411 14.042086-38.807221l108.374269 43.525362c-2.553107 31.403211-28.972654 56.290895-61.141797 56.290894z m633.12959 74.550713L263.984844 638.501326l-16.462431-6.638077H91.915671V391.626129h155.606742l16.462431-6.638077 665.298733-267.30005v788.113374z m0 0"
|
||||
fill="#52c41a"
|
||||
p-id="6236"></path>
|
||||
</svg>
|
||||
相关场景
|
||||
</div>
|
||||
}
|
||||
onClick={showDrawer}
|
||||
/>
|
||||
|
||||
{/* 数据列表 */}
|
||||
<Main loading={iodLoading && Boolean(currentIodMessage)} data={data.slice(0, 3)} />
|
||||
{/* 场景列表 */}
|
||||
<div className="space-y-1.5 flex-1 overflow-y-auto">
|
||||
{data.slice(0, 3).map((item, index) => (
|
||||
<Card
|
||||
key={index}
|
||||
className="[&_.ant-card-body]:!p-2 !bg-[gb(248, 248, 248)] border !border-[#e9e9e9]">
|
||||
<div className="flex flex-col gap-0.5">
|
||||
<h3 className="text-base font-medium mb-1 text-[#222222] line-clamp-2">
|
||||
{item.title}
|
||||
</h3>
|
||||
<span className="text-sm text-[#383838] line-clamp-2">
|
||||
{item.description}
|
||||
</span>
|
||||
<p className="text-[#828282] text-xs truncate">
|
||||
{item.demander}
|
||||
</p>
|
||||
<p className="flex items-center gap-1.5">
|
||||
<span className="inline-block text-[#003AD4] bg-[#003ad430] h-5 leading-5 mt-1 text-xs rounded-full px-2.5">
|
||||
技术应用
|
||||
</span>
|
||||
<span className="inline-block text-[#00BF68] bg-[#00bf6830] h-5 leading-5 mt-1 text-xs rounded-full px-2.5">
|
||||
制造业
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
</Card>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 抽屉 */}
|
||||
<Drawer
|
||||
title="相关场景"
|
||||
closable={{ "aria-label": "Close Button" }}
|
||||
onClose={onClose}
|
||||
open={open}
|
||||
width="33.33%">
|
||||
<div className="grid grid-cols-1 gap-3 overflow-y-auto">
|
||||
{data.slice(0, 3).map((item, index) => (
|
||||
<Card
|
||||
key={index}
|
||||
hoverable
|
||||
className="[&_.ant-card-body]:!p-2 !bg-[gb(248, 248, 248)] border !border-[#e9e9e9]">
|
||||
<div className="flex flex-col gap-0.5">
|
||||
<h3 className="text-base font-medium mb-1 text-[#222222]">
|
||||
{item.title}
|
||||
</h3>
|
||||
<span className="text-sm text-[#383838]">
|
||||
{item.description}
|
||||
</span>
|
||||
<p className="text-[#828282] text-xs">{item.demander}</p>
|
||||
<p className="flex items-center gap-1.5">
|
||||
<span className="inline-block text-[#003AD4] bg-[#003ad430] h-5 leading-5 mt-1 text-xs rounded-full px-2.5">
|
||||
技术应用
|
||||
</span>
|
||||
<span className="inline-block text-[#00BF68] bg-[#00bf6830] h-5 leading-5 mt-1 text-xs rounded-full px-2.5">
|
||||
制造业
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
</Card>
|
||||
))}
|
||||
</div>
|
||||
</Drawer>
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
|
@ -1,158 +1,141 @@
|
||||
import React, { useMemo } from "react"
|
||||
import React, { useState } from "react"
|
||||
import { DataNavigation } from "@/components/Common/DataNavigation.tsx"
|
||||
import { Card, Skeleton } from "antd"
|
||||
import { IodRegistryEntry } from "@/types/iod.ts"
|
||||
import { useIodPlaygroundContext } from "@/components/Option/Playground/PlaygroundIod.tsx"
|
||||
import { Card, Drawer, List } from "antd"
|
||||
|
||||
const defaultData: IodRegistryEntry[] = [
|
||||
{
|
||||
name: "北京大学",
|
||||
description:
|
||||
"北大是常为新的,改进的运动的先锋,要使中国向着好的,往上的道路走。",
|
||||
doId: "12100000400002259P"
|
||||
},
|
||||
{
|
||||
name: "长三角先进材料研究院",
|
||||
description: "由江苏省人民政府联合中国科学院、中国钢研科技集团和中国",
|
||||
doId: "91320507MAEKWL5Y2L"
|
||||
},
|
||||
{
|
||||
name: "伊利诺伊大学香槟分校(UIUC)",
|
||||
description: "创建于1867年,坐落于伊利诺伊州双子城厄巴纳–香槟市,",
|
||||
doId: "bdware.org/uiuc"
|
||||
}
|
||||
]
|
||||
|
||||
type HeaderProps = {
|
||||
title: string
|
||||
showButton?: boolean
|
||||
onClick?: () => void
|
||||
}
|
||||
const Header: React.FC<HeaderProps> = ({
|
||||
title,
|
||||
showButton = true,
|
||||
onClick
|
||||
}) => (
|
||||
<DataNavigation
|
||||
Header={
|
||||
<div className="flex items-center text-[#BE0BAC] gap-1">
|
||||
<svg
|
||||
className="icon"
|
||||
viewBox="0 0 1024 1024"
|
||||
version="1.1"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
p-id="7272"
|
||||
width="18"
|
||||
height="18">
|
||||
<path
|
||||
d="M824.2 699.9c-25.4-25.4-54.7-45.7-86.4-60.4C783.1 602.8 812 546.8 812 484c0-110.8-92.4-201.7-203.2-200-109.1 1.7-197 90.6-197 200 0 62.8 29 118.8 74.2 155.5-31.7 14.7-60.9 34.9-86.4 60.4C345 754.6 314 826.8 312 903.8c-0.1 4.5 3.5 8.2 8 8.2h56c4.3 0 7.9-3.4 8-7.7 1.9-58 25.4-112.3 66.7-153.5C493.8 707.7 551.1 684 612 684c60.9 0 118.2 23.7 161.3 66.8C814.5 792 838 846.3 840 904.3c0.1 4.3 3.7 7.7 8 7.7h56c4.5 0 8.1-3.7 8-8.2-2-77-33-149.2-87.8-203.9zM612 612c-34.2 0-66.4-13.3-90.5-37.5-24.5-24.5-37.9-57.1-37.5-91.8 0.3-32.8 13.4-64.5 36.3-88 24-24.6 56.1-38.3 90.4-38.7 33.9-0.3 66.8 12.9 91 36.6 24.8 24.3 38.4 56.8 38.4 91.4 0 34.2-13.3 66.3-37.5 90.5-24.2 24.2-56.4 37.5-90.6 37.5z"
|
||||
p-id="7273"
|
||||
fill="#BE0BAC"></path>
|
||||
<path
|
||||
d="M361.5 510.4c-0.9-8.7-1.4-17.5-1.4-26.4 0-15.9 1.5-31.4 4.3-46.5 0.7-3.6-1.2-7.3-4.5-8.8-13.6-6.1-26.1-14.5-36.9-25.1-25.8-25.2-39.7-59.3-38.7-95.4 0.9-32.1 13.8-62.6 36.3-85.6 24.7-25.3 57.9-39.1 93.2-38.7 31.9 0.3 62.7 12.6 86 34.4 7.9 7.4 14.7 15.6 20.4 24.4 2 3.1 5.9 4.4 9.3 3.2 17.6-6.1 36.2-10.4 55.3-12.4 5.6-0.6 8.8-6.6 6.3-11.6-32.5-64.3-98.9-108.7-175.7-109.9-110.9-1.7-203.3 89.2-203.3 199.9 0 62.8 28.9 118.8 74.2 155.5-31.8 14.7-61.1 35-86.5 60.4-54.8 54.7-85.8 126.9-87.8 204-0.1 4.5 3.5 8.2 8 8.2h56.1c4.3 0 7.9-3.4 8-7.7 1.9-58 25.4-112.3 66.7-153.5 29.4-29.4 65.4-49.8 104.7-59.7 3.9-1 6.5-4.7 6-8.7z"
|
||||
p-id="7274"
|
||||
fill="#BE0BAC"></path>
|
||||
</svg>
|
||||
{title}
|
||||
</div>
|
||||
export const PlaygroundTeam = () => {
|
||||
// 模拟数据
|
||||
const data = [
|
||||
{
|
||||
title: "绿色化工工艺项目",
|
||||
description:
|
||||
"基于生物基原料,采用repeal2.0可降解材料技术,开发新型环保材料。",
|
||||
demander: "奥赛康药业 供方:美国Propella公司"
|
||||
},
|
||||
{
|
||||
title: "智能农业解决方案",
|
||||
description: "利用物联网技术,实现精准农业管理,提高农作物产量。",
|
||||
demander: "奥赛康药业 供方:美国Propella公司"
|
||||
},
|
||||
{
|
||||
title: "新能源汽车电池技术",
|
||||
description: "研发高能量密度、长寿命的新型电池材料,推动电动汽车发展。",
|
||||
demander: "奥赛康药业 供方:美国Propella公司"
|
||||
},
|
||||
{
|
||||
title: "碳捕集与封存技术",
|
||||
description: "开发高效的碳捕集技术,减少工业排放,助力碳中和目标。",
|
||||
demander: "奥赛康药业 供方:美国Propella公司"
|
||||
}
|
||||
showButton={showButton}
|
||||
onClick={onClick}
|
||||
/>
|
||||
)
|
||||
]
|
||||
|
||||
type MainProps = {
|
||||
loading: boolean
|
||||
data: IodRegistryEntry[]
|
||||
truncate?: boolean
|
||||
// 水平展示三个还是按列展示(页面和详情展示不一样)
|
||||
flat?: boolean
|
||||
}
|
||||
const Main: React.FC<MainProps> = ({
|
||||
data,
|
||||
loading,
|
||||
truncate = true,
|
||||
flat = true
|
||||
}) => (
|
||||
<div
|
||||
className={`${flat ? "grid grid-cols-3 gap-3" : "space-y-1.5"} flex-1 overflow-y-auto`}>
|
||||
{data.map((item) => {
|
||||
return (
|
||||
<Card
|
||||
className="[&_.ant-card-body]:!p-2 !bg-[gb(248, 248, 248)] border !border-[#e9e9e9]"
|
||||
key={item.doId}>
|
||||
{loading ? (
|
||||
<Skeleton title={false} active />
|
||||
) : (
|
||||
<div className="flex flex-col gap-0.5">
|
||||
<h3
|
||||
className={`text-base font-medium mb-1 text-[#222222] break-all ${truncate ? "line-clamp-2" : ""}`}
|
||||
title={item.name}>
|
||||
{item.name}
|
||||
</h3>
|
||||
<p
|
||||
className={`text-sm text-[#383838] break-all ${truncate ? "line-clamp-2" : ""}`}
|
||||
title={item.doId}>
|
||||
数字对象标识:{item.doId}
|
||||
</p>
|
||||
<p
|
||||
className={`text-[#828282] text-xs break-all ${truncate ? "truncate" : ""}`}
|
||||
title={item.description}>
|
||||
{item.description}
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
</Card>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
)
|
||||
for (let i = 0; i < 10; i++) {
|
||||
data.push({
|
||||
title: "开发新型电池材料",
|
||||
description: "研发高能量密度、长寿命的新型电池材料,推动电动汽车发展。",
|
||||
demander: "奥赛康药业 供方:美国Propella公司"
|
||||
})
|
||||
}
|
||||
|
||||
type Props = {
|
||||
className?: string
|
||||
}
|
||||
export const PlaygroundTeam: React.FC<Props> = ({ className }) => {
|
||||
const { iodLoading } = useMessageOption()
|
||||
const [open, setOpen] = useState(false)
|
||||
|
||||
const {
|
||||
setShowPlayground,
|
||||
setDetailHeader,
|
||||
setDetailMain,
|
||||
currentIodMessage
|
||||
} = useIodPlaygroundContext()
|
||||
const showDrawer = () => {
|
||||
setOpen(true)
|
||||
}
|
||||
|
||||
const data = useMemo<IodRegistryEntry[]>(() => {
|
||||
return currentIodMessage
|
||||
? currentIodMessage.organization?.data ?? []
|
||||
: defaultData
|
||||
}, [currentIodMessage])
|
||||
|
||||
const title = useMemo(() => {
|
||||
return currentIodMessage ? "推荐团队" : "热点团队"
|
||||
}, [currentIodMessage])
|
||||
|
||||
const showMore = () => {
|
||||
setShowPlayground(false)
|
||||
setDetailHeader(
|
||||
<Header
|
||||
title={title}
|
||||
showButton={false}
|
||||
onClick={() => setShowPlayground(false)}
|
||||
/>
|
||||
)
|
||||
setDetailMain(
|
||||
<Main loading={iodLoading && Boolean(currentIodMessage)} data={data} truncate={false} flat={false} />
|
||||
)
|
||||
const onClose = () => {
|
||||
setOpen(false)
|
||||
}
|
||||
|
||||
return (
|
||||
<Card className={`${className}`} hoverable>
|
||||
<div className="h-full flex flex-col gap-2 relative">
|
||||
<Card
|
||||
className="h-full [&_.ant-card-body]:h-full [&_.ant-card-body]:!p-[20px] overflow-y-hidden"
|
||||
hoverable>
|
||||
<div className="h-full flex flex-col relative">
|
||||
{/* 数据导航 */}
|
||||
<Header title={title} onClick={showMore} />
|
||||
{/* 数据列表 */}
|
||||
<Main loading={iodLoading && Boolean(currentIodMessage)} data={data.slice(0, 3)} />
|
||||
<DataNavigation
|
||||
Header={
|
||||
<div className="flex items-center text-[#BE0BAC] gap-1">
|
||||
<svg
|
||||
className="icon"
|
||||
viewBox="0 0 1024 1024"
|
||||
version="1.1"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
p-id="7272"
|
||||
width="18"
|
||||
height="18">
|
||||
<path
|
||||
d="M824.2 699.9c-25.4-25.4-54.7-45.7-86.4-60.4C783.1 602.8 812 546.8 812 484c0-110.8-92.4-201.7-203.2-200-109.1 1.7-197 90.6-197 200 0 62.8 29 118.8 74.2 155.5-31.7 14.7-60.9 34.9-86.4 60.4C345 754.6 314 826.8 312 903.8c-0.1 4.5 3.5 8.2 8 8.2h56c4.3 0 7.9-3.4 8-7.7 1.9-58 25.4-112.3 66.7-153.5C493.8 707.7 551.1 684 612 684c60.9 0 118.2 23.7 161.3 66.8C814.5 792 838 846.3 840 904.3c0.1 4.3 3.7 7.7 8 7.7h56c4.5 0 8.1-3.7 8-8.2-2-77-33-149.2-87.8-203.9zM612 612c-34.2 0-66.4-13.3-90.5-37.5-24.5-24.5-37.9-57.1-37.5-91.8 0.3-32.8 13.4-64.5 36.3-88 24-24.6 56.1-38.3 90.4-38.7 33.9-0.3 66.8 12.9 91 36.6 24.8 24.3 38.4 56.8 38.4 91.4 0 34.2-13.3 66.3-37.5 90.5-24.2 24.2-56.4 37.5-90.6 37.5z"
|
||||
p-id="7273"
|
||||
fill="#BE0BAC"></path>
|
||||
<path
|
||||
d="M361.5 510.4c-0.9-8.7-1.4-17.5-1.4-26.4 0-15.9 1.5-31.4 4.3-46.5 0.7-3.6-1.2-7.3-4.5-8.8-13.6-6.1-26.1-14.5-36.9-25.1-25.8-25.2-39.7-59.3-38.7-95.4 0.9-32.1 13.8-62.6 36.3-85.6 24.7-25.3 57.9-39.1 93.2-38.7 31.9 0.3 62.7 12.6 86 34.4 7.9 7.4 14.7 15.6 20.4 24.4 2 3.1 5.9 4.4 9.3 3.2 17.6-6.1 36.2-10.4 55.3-12.4 5.6-0.6 8.8-6.6 6.3-11.6-32.5-64.3-98.9-108.7-175.7-109.9-110.9-1.7-203.3 89.2-203.3 199.9 0 62.8 28.9 118.8 74.2 155.5-31.8 14.7-61.1 35-86.5 60.4-54.8 54.7-85.8 126.9-87.8 204-0.1 4.5 3.5 8.2 8 8.2h56.1c4.3 0 7.9-3.4 8-7.7 1.9-58 25.4-112.3 66.7-153.5 29.4-29.4 65.4-49.8 104.7-59.7 3.9-1 6.5-4.7 6-8.7z"
|
||||
p-id="7274"
|
||||
fill="#BE0BAC"></path>
|
||||
</svg>
|
||||
相关团队
|
||||
</div>
|
||||
}
|
||||
onClick={showDrawer}
|
||||
/>
|
||||
|
||||
{/* 场景列表 */}
|
||||
<div className="grid grid-cols-3 gap-3 flex-1 overflow-y-auto">
|
||||
{data.slice(0, 5).map((item, index) => (
|
||||
<Card
|
||||
key={index}
|
||||
className="[&_.ant-card-body]:!p-2 !bg-[gb(248, 248, 248)] border !border-[#e9e9e9]">
|
||||
<div className="flex flex-col gap-0.5">
|
||||
<h3 className="text-base font-medium mb-1 text-[#222222] line-clamp-2">
|
||||
{item.title}
|
||||
</h3>
|
||||
<p className="text-[#828282] text-xs truncate">
|
||||
{item.description}
|
||||
</p>
|
||||
<p className="flex items-center gap-1.5">
|
||||
<span className="inline-block text-[#BE0BAC] bg-[#be0bac30] h-5 leading-5 mt-1 text-xs rounded-full px-2.5">
|
||||
晶体材料
|
||||
</span>
|
||||
<span className="inline-block text-[#EB1C1C] bg-[#eb1c1c30] h-5 leading-5 mt-1 text-xs rounded-full px-2.5">
|
||||
中国
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
</Card>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 抽屉 */}
|
||||
<Drawer
|
||||
title="相关团队"
|
||||
closable={{ "aria-label": "Close Button" }}
|
||||
onClose={onClose}
|
||||
open={open}
|
||||
width="33.33%">
|
||||
<div className="grid grid-cols-1 gap-3 overflow-y-auto">
|
||||
{data.slice(0, 5).map((item, index) => (
|
||||
<Card
|
||||
key={index}
|
||||
hoverable
|
||||
className="[&_.ant-card-body]:!p-2 !bg-[gb(248, 248, 248)] border !border-[#e9e9e9]">
|
||||
<div className="flex flex-col gap-0.5">
|
||||
<h3 className="text-base font-medium mb-1 text-[#222222]">
|
||||
{item.title}
|
||||
</h3>
|
||||
<p className="text-[#828282] text-xs">{item.description}</p>
|
||||
<p className="flex items-center gap-1.5">
|
||||
<span className="inline-block text-[#BE0BAC] bg-[#be0bac30] h-5 leading-5 mt-1 text-xs rounded-full px-2.5">
|
||||
晶体材料
|
||||
</span>
|
||||
<span className="inline-block text-[#EB1C1C] bg-[#eb1c1c30] h-5 leading-5 mt-1 text-xs rounded-full px-2.5">
|
||||
中国
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
</Card>
|
||||
))}
|
||||
</div>
|
||||
</Drawer>
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
|
@ -1,13 +1,15 @@
|
||||
import { Form, Image, Input, message, Modal } from "antd"
|
||||
import React from "react"
|
||||
import { Form, Image, Input, Modal, Tooltip, message } from "antd"
|
||||
import { Share } from "lucide-react"
|
||||
import { useState } from "react"
|
||||
import type { Message } from "~/store/option"
|
||||
import Markdown from "./Markdown"
|
||||
import React from "react"
|
||||
import { useMutation } from "@tanstack/react-query"
|
||||
import { getPageShareUrl } from "~/services/ollama"
|
||||
import { cleanUrl } from "~/libs/clean-url"
|
||||
import { getTitleById, getUserId, saveWebshare } from "@/db"
|
||||
import { useTranslation } from "react-i18next"
|
||||
import fetcher from "@/libs/fetcher"
|
||||
import { Message } from "@/types/message.ts"
|
||||
|
||||
type Props = {
|
||||
messages: Message[]
|
||||
|
@ -1,21 +0,0 @@
|
||||
import React from "react"
|
||||
|
||||
export const BatteryIcon = React.forwardRef<
|
||||
SVGSVGElement,
|
||||
React.SVGProps<SVGSVGElement>
|
||||
>((props, ref) => {
|
||||
return (
|
||||
<svg
|
||||
className="icon"
|
||||
viewBox="0 0 1024 1024"
|
||||
version="1.1"
|
||||
fill="currentColor"
|
||||
fillRule="evenodd"
|
||||
ref={ref}
|
||||
{...props}>
|
||||
<path
|
||||
d="M604.16 112.64c13.568-35.0208-5.9904-57.7024-24.1152-80.8448H452.7616C436.2752 56.32 419.84 80.4864 434.176 112.64zM194.56 888.3712a213.4528 213.4528 0 0 0 21.504 2.304c195.7888 0 391.5776 0 587.3152 0.6656 25.1904 0 29.4912-10.24 29.3888-32.1024-0.6144-202.0864-3.1232-633.7024-3.1232-633.7024H194.56zM597.3504 307.712l7.1168 4.2496c-25.6 73.1136-50.8928 146.2272-78.4384 225.28h139.0592L437.9648 824.32l-8.6016-2.7648c17.92-71.0144 35.84-142.0288 54.9888-217.7536l-134.656-5.7856zM192.6656 926.72c-4.096 41.8304 14.7456 64.4096 54.3744 64.8192 66.56 0.6656 133.12 0 200.0384 0 105.8304 0 211.6608 0.3072 317.44 0 44.6464 0 69.6832-25.856 64.512-64.8704zM777.4208 141.0048c-20.992-1.6896-42.24-0.4096-63.3856-0.4096H257.4848c-41.3696 0-57.9584 12.3904-66.0992 49.3568h641.2288c-6.5024-32.5632-26.368-46.592-55.1936-48.9472z"
|
||||
p-id="50919"></path>
|
||||
</svg>
|
||||
)
|
||||
})
|
@ -1,26 +0,0 @@
|
||||
import React from "react"
|
||||
|
||||
export const BellIcon = React.forwardRef<
|
||||
SVGSVGElement,
|
||||
React.SVGProps<SVGSVGElement>
|
||||
>((props, ref) => {
|
||||
return (
|
||||
<svg
|
||||
className="icon"
|
||||
viewBox="0 0 1024 1024"
|
||||
version="1.1"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
p-id="76534"
|
||||
fill="currentColor"
|
||||
fillRule="evenodd"
|
||||
ref={ref}
|
||||
{...props}>
|
||||
<path
|
||||
d="M593.861938 788.582269 424.670537 788.582269c-9.444093 0-18.437931 3.931542-24.695448 10.902304-6.313799 6.970762-9.441023 16.32378-8.547677 25.675776 2.860141 29.191856 16.32378 56.238862 38.009685 76.018348 21.772886 20.016893 50.161447 31.0379 79.889515 31.0379 29.696346 0 58.084906-11.022031 79.830163-30.977525 21.714558-19.839861 35.178197-46.885843 38.068014-76.255755 0.595564-9.473769-2.534729-18.707061-8.638751-25.498744C612.299869 792.513812 603.306031 788.582269 593.861938 788.582269zM555.020304 863.825974c-25.082258 22.877033-66.604954 22.817682-91.567485 0.060375-7.596002-6.970762-13.404288-15.429411-17.157775-24.723078l125.82266 0C568.394916 848.51629 562.643935 856.914564 555.020304 863.825974z"
|
||||
p-id="76535"></path>
|
||||
<path
|
||||
d="M818.608631 648.343271l-62.763462-82.927711 0-36.22197 0-13.046131L755.845169 410.432767c0-70.745251-24.215518-136.337131-68.182892-184.682209-26.003234-28.625968-57.310264-49.715285-93.055372-62.821791-3.306302-18.944468-12.720719-36.251645-26.926256-49.207725-32.050973-29.251208-85.104283-29.251208-117.095905 0-14.356986 13.046131-23.77038 30.382984-26.986631 49.2681-35.71441 13.046131-67.022463 34.135448-93.025697 62.791092-43.937698 48.434106-68.183915 114.025986-68.183915 184.652534l0.179079 154.686035-62.315254 82.45085c-8.757454 9.353019-13.582343 21.506826-13.582343 34.256198l0 40.331567c0 27.643594 22.460548 50.042743 50.042743 50.042743l544.812313 0c27.610848 0 50.011021-22.400173 50.011021-50.042743l0-40.331567C831.535035 669.075455 826.739822 656.921647 818.608631 648.343271zM535.776008 149.881612c-7.387247-0.655939-19.301602-1.906419-26.569122-1.906419-7.29822 0-19.689435 1.251503-27.048029 1.906419C494.578724 129.627313 526.542716 133.379777 535.776008 149.881612zM237.426992 722.156394l-0.119727-40.034808 62.315254-82.449827c8.698103-9.354042 13.524015-21.447475 13.524015-34.256198L313.146535 410.432767c0-58.056254 19.540032-111.553679 54.986335-150.634766 17.574261-19.361977 38.307468-34.374902 61.540611-44.681642 48.851615-21.745257 110.302175-21.745257 159.096485 0 23.321148 10.425444 43.99398 25.438369 61.538565 44.681642 35.449373 39.081087 54.958706 92.578512 54.958706 150.634766l0 105.715717 0 13.046131 0 36.22197c0 12.867052 4.825912 25.081235 12.95608 33.539884l62.791092 82.868359 0.508583 39.795355L237.426992 722.156394z"
|
||||
p-id="76536"></path>
|
||||
</svg>
|
||||
)
|
||||
})
|
@ -1,26 +0,0 @@
|
||||
import React from "react"
|
||||
|
||||
export const CheckIcon = React.forwardRef<
|
||||
SVGSVGElement,
|
||||
React.SVGProps<SVGSVGElement>
|
||||
>((props, ref) => {
|
||||
return (
|
||||
<svg
|
||||
className="icon"
|
||||
viewBox="0 0 1024 1024"
|
||||
version="1.1"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
p-id="41530"
|
||||
fill="currentColor"
|
||||
fillRule="evenodd"
|
||||
ref={ref}
|
||||
{...props}>
|
||||
<path
|
||||
d="M334.935114 642.334328l-185.247485-227.447917c-16.445757-20.169324-13.342784-49.957864 6.826541-66.713918L571.071355 10.569036c20.169324-16.445757 49.957864-13.342784 66.713919 6.826541l185.247484 227.447916c16.445757 20.169324 13.342784 49.957864-6.82654 66.713919L401.649033 649.160868c-20.479621 16.445757-50.268162 13.342784-66.713919-6.82654zM189.71598 693.843679L39.53209 509.216788c-14.273676-17.376648-11.481-43.131324 5.895648-57.404999l5.585352-4.654459c17.376648-14.273676 43.131324-11.481 57.404999 5.895648l150.494188 184.62689c14.273676 17.376648 11.481 43.131324-5.895649 57.405l-5.585351 4.654459c-17.686946 14.273676-43.441621 11.481-57.715297-5.895648zM877.024488 1024H275.668331a44.372513 44.372513 0 1 1 0-88.745026h601.356157a44.372513 44.372513 0 1 1 0 88.745026z"
|
||||
p-id="41531"></path>
|
||||
<path
|
||||
d="M564.555112 345.06952l-77.264026-94.950972c-16.445757-20.169324-13.342784-49.957864 6.82654-66.713919l199.521161-162.595782c20.169324-16.445757 49.957864-13.342784 66.713918 6.82654l77.264026 94.950972c16.445757 20.169324 13.342784 49.957864-6.82654 66.713919l-199.521161 162.595782c-20.169324 16.445757-50.268162 13.342784-66.713918-6.82654zM646.163301 1020.58673l-94.950973-79.746405c51.509351-61.438864 77.574324-137.771999 72.919865-215.346322-4.344162-76.022837-37.85627-143.977945-94.330378-191.143134l79.746405-94.950972c82.849378 69.506594 131.87635 168.491431 138.392593 278.957268 6.205946 109.224648-29.78854 216.587512-101.777512 302.229565z"
|
||||
p-id="41532"></path>
|
||||
</svg>
|
||||
)
|
||||
})
|
@ -1,24 +0,0 @@
|
||||
import React from "react"
|
||||
|
||||
export const CollectIcon = React.forwardRef<
|
||||
SVGSVGElement,
|
||||
React.SVGProps<SVGSVGElement>
|
||||
>((props, ref) => {
|
||||
return (
|
||||
<svg
|
||||
className="icon"
|
||||
viewBox="0 0 1024 1024"
|
||||
version="1.1"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
p-id="73631"
|
||||
fill="currentColor"
|
||||
fillRule="evenodd"
|
||||
ref={ref}
|
||||
{...props}
|
||||
>
|
||||
<path
|
||||
d="M536.934 860.314c-26.828-14.797-70.502-14.695-97.177 0L251.238 964.096c-53.606 29.542-88.78 2.56-78.54-59.802l35.993-219.801c5.12-31.335-8.448-74.752-30.054-96.768L26.163 431.975c-43.417-44.289-29.696-87.655 30.003-96.769l210.74-32c30.003-4.608 65.28-31.539 78.643-59.852l94.259-199.936c26.829-56.884 70.451-56.679 97.126 0l94.208 199.987c13.466 28.467 48.896 55.296 78.695 59.853l210.739 32.05c60.006 9.114 73.216 52.583 30.054 96.718L798.106 587.674c-21.71 22.17-35.124 65.69-30.055 96.819l35.994 219.801c10.24 62.567-25.14 89.19-78.541 59.802l-188.57-103.782z"
|
||||
p-id="73632"></path>
|
||||
</svg>
|
||||
)
|
||||
})
|
@ -1,24 +0,0 @@
|
||||
import React from "react"
|
||||
|
||||
export const DataProjectIcon = React.forwardRef<
|
||||
SVGSVGElement,
|
||||
React.SVGProps<SVGSVGElement>
|
||||
>((props, ref) => {
|
||||
return (
|
||||
<svg
|
||||
className="icon"
|
||||
viewBox="0 0 1024 1024"
|
||||
version="1.1"
|
||||
fill="currentColor"
|
||||
fillRule="evenodd"
|
||||
ref={ref}
|
||||
{...props}>
|
||||
<path
|
||||
d="M669.538462 315.076923a185.659077 185.659077 0 0 1 122.171076 325.474462 354.500923 354.500923 0 0 1 232.093539 321.378461l0.196923 11.421539c0 27.963077-22.685538 50.648615-50.688 50.648615H365.764923a50.688 50.688 0 0 1-50.412308-45.489231L315.076923 973.312a354.579692 354.579692 0 0 1 232.290462-332.8A185.659077 185.659077 0 0 1 669.538462 315.076923z m-263.404308 161.910154a267.815385 267.815385 0 0 0-1.024 23.748923l0.196923 11.027692c1.378462 32.846769 8.782769 64.630154 21.464615 93.971693l2.087385 4.489846v2.756923l-1.220923 0.866461A433.742769 433.742769 0 0 0 249.619692 866.461538H126.936615A87.512615 87.512615 0 0 1 39.384615 778.948923v-214.449231c0-48.324923 39.187692-87.512615 87.512616-87.512615h279.236923zM341.346462 0c48.324923 0 87.512615 39.187692 87.512615 87.512615V389.513846H126.897231A87.512615 87.512615 0 0 1 39.384615 301.961846V87.512615C39.384615 39.187692 78.572308 0 126.897231 0h214.449231z m476.947692 0C866.697846 0 905.846154 39.187692 905.846154 87.512615v294.4A264.428308 264.428308 0 0 0 516.332308 285.144615V87.512615C516.371692 39.187692 555.559385 0 603.884308 0h214.44923z"
|
||||
p-id="11293"></path>
|
||||
<path
|
||||
d="M24.024615 24.615385L23.630769 22.646154l0.393846 1.969231zM24.024615 1001.353846l-0.393846-1.969231 0.393846 1.969231zM1000.763077 24.615385l-0.393846-1.969231 0.393846 1.969231zM1000.763077 1001.353846l-0.393846-1.969231 0.393846 1.969231z"
|
||||
p-id="11294"></path>
|
||||
</svg>
|
||||
)
|
||||
})
|
@ -1,27 +0,0 @@
|
||||
import React from "react"
|
||||
|
||||
export const DatasetIcon = React.forwardRef<
|
||||
SVGSVGElement,
|
||||
React.SVGProps<SVGSVGElement>
|
||||
>((props, ref) => {
|
||||
return (
|
||||
<svg
|
||||
className="icon"
|
||||
viewBox="0 0 1153 1024"
|
||||
version="1.1"
|
||||
fill="currentColor"
|
||||
fillRule="evenodd"
|
||||
ref={ref}
|
||||
{...props}>
|
||||
<path
|
||||
d="M849.992624 307.054752c-56.549976 0-109.613995 0.871489-162.581182 0a39.701182 39.701182 0 0 0-40.282175 23.239716c-15.29948 27.403499-31.857778 54.226005-49.190733 80.370686A36.118392 36.118392 0 0 1 576.054468 426.061466C416.378251 441.167281 257.670355 439.908463 104.966052 380.35669a323.419385 323.419385 0 0 1-45.123782-22.852387C19.850591 334.167754-2.420804 300.857494 0.871489 252.344586a735.92435 735.92435 0 0 0 0-77.465721A98.478298 98.478298 0 0 1 46.285768 87.148936c51.030544-36.312057 109.80766-53.257683 169.940426-65.071206C384.810969-11.329362 552.427423-8.811726 716.55792 44.252293a419.767376 419.767376 0 0 1 93.927186 48.416076c28.274988 18.01078 39.894846 46.963593 39.313854 81.04851-0.387329 41.444161 0.193664 83.178818 0.193664 133.337873zM724.594988 957.766809c-52.967187 10.554704-105.740709 22.755556-159.095224 31.373617a981.587518 981.587518 0 0 1-293.595083 0 625.535697 625.535697 0 0 1-193.664303-55.581655c-52.289362-25.951017-82.791489-63.618723-77.465721-125.881797 3.195461-40.088511 0.580993-80.56435 0.580993-122.976832 58.099291 78.724539 144.279905 96.832151 229.879527 113.487281 111.16331 21.399905 223.391773 28.274988 335.717069 5.713097 14.137494-2.808132 21.496738 2.517636 28.178156 15.202648 16.267801 30.59896 35.634232 59.454941 51.417872 90.150733a58.099291 58.099291 0 0 0 50.159055 34.762742 189.50052 189.50052 0 0 1 27.113002 6.29409zM1.161986 397.689645c25.66052 44.54279 61.972577 65.071206 101.092766 81.242175A723.820331 723.820331 0 0 0 358.27896 531.027518c65.749031 1.936643 131.691726-5.616265 197.537588-7.456076a35.5374 35.5374 0 0 1 26.338346 13.556501 751.514326 751.514326 0 0 1 45.414279 79.692861c3.58279 7.262411 0 18.301277-1.258818 27.500331 0 2.808132-3.970118 5.035272-5.422601 7.843404-19.36643 38.732861-49.481229 56.646809-94.701844 60.32643-142.343262 11.426194-281.491064 1.936643-416.37825-47.350922a320.804917 320.804917 0 0 1-43.574469-20.818912C21.980898 619.725768-2.808132 584.575697 0.580993 530.737021c2.7113-41.734657 0.580993-83.856643 0.580993-133.047376zM1017.899574 477.57617c0-32.148274-0.774657-59.551773 0.580993-87.148936 0-6.100426 7.746572-12.104019 12.685012-17.429787 11.135697-11.910355 13.846998-26.822506 1.258818-35.731064-7.64974-5.4226-30.59896-4.454279-33.213428 0.580993-15.202648 29.049645-45.607943 34.375414-68.653995 50.546383a312.864681 312.864681 0 0 1-30.017967 17.139291c-9.683215-15.29948-19.36643-29.72747-27.306667-45.123783a21.399905 21.399905 0 0 1 1.646147-17.429787c18.591773-33.794421 37.474043-67.782506 58.099291-100.511773a32.729267 32.729267 0 0 1 22.561891-13.556501c37.086714-1.35565 74.27026-1.452482 111.260142 0a35.731064 35.731064 0 0 1 24.789031 14.137494c20.818913 31.567281 40.572671 64.006052 58.873948 96.832151a35.150071 35.150071 0 0 1 0 27.984492c-15.008983 29.049645-33.213428 57.227801-48.416076 86.567943-8.908558 17.429787-20.334752 25.176359-39.894846 23.046052a405.242553 405.242553 0 0 0-44.252294 0.096832zM900.539007 842.439716c28.37182 16.267801 55.000662 30.986288 80.951679 46.866762 4.551111 2.808132 5.906761 10.457872 9.683215 14.912151a76.594232 76.594232 0 0 0 24.014373 22.271395 25.370024 25.370024 0 0 0 20.915745-12.781844 72.430449 72.430449 0 0 0-8.811726-31.179953c-2.323972-6.197258-9.102222-11.523026-9.683215-17.429787-0.968322-29.049645 0-58.099291 0-90.44123 19.36643 0 38.055035-0.677825 56.162648 0a22.271395 22.271395 0 0 1 14.331158 10.264208c20.141087 33.891253 39.991678 67.782506 58.099291 102.738913a29.049645 29.049645 0 0 1-0.580993 23.917541c-17.720284 33.31026-36.312057 66.330024-56.25948 98.478298a32.148274 32.148274 0 0 1-22.658723 12.491348q-55.484823 2.033475-111.16331 0a32.148274 32.148274 0 0 1-22.368227-12.685012q-30.405296-47.931915-57.324633-97.994137a34.084917 34.084917 0 0 1 0-25.854185A401.272435 401.272435 0 0 1 900.539007 842.439716z"
|
||||
p-id="6804"></path>
|
||||
<path
|
||||
d="M875.362648 419.96104c-26.628842 15.493144-51.32104 30.308463-76.691064 43.961797-4.551111 2.420804-12.200851-1.646147-17.817116 0-10.748369 2.517636-24.692199 4.06695-30.308463 11.329362-4.357447 5.713097 1.258818 19.36643 3.776454 29.630638 1.742979 7.165579 8.037069 13.750165 8.327565 20.72208 0.968322 29.049645 0 58.099291 0 90.92539-19.36643 0-37.667707 0.968322-55.678487-0.580993a25.176359 25.176359 0 0 1-15.008984-12.781844c-19.36643-34.278582-38.732861-68.653995-56.549976-103.900898a36.118392 36.118392 0 0 1 0.774657-28.37182c16.267801-31.373617 33.600757-62.359905 52.870355-91.990544a40.669504 40.669504 0 0 1 27.790827-16.267801c32.051442-2.033475 64.393381 0 96.832151-1.258818 20.334752-1.065154 33.794421 4.551111 41.928322 23.820709a381.22818 381.22818 0 0 0 19.753759 34.762742zM763.037352 639.092199c0 33.503924 0.774657 62.55357 0 91.40955 0 7.64974-8.424397 14.621655-10.36104 22.561892s-6.003593 22.658723-1.452482 27.984491 18.591773 7.262411 29.049645 9.00539a242.080378 242.080378 0 0 1 32.826099 3.292294 134.790355 134.790355 0 0 1 31.664114 17.332955c10.845201 7.359243 30.695792 18.785437 29.049645 23.530212a193.664303 193.664303 0 0 1-27.113002 50.352719c-2.130307 3.195461-10.264208 3.292293-15.589976 3.292293-38.732861 0-77.465721 1.065154-116.198582 0a34.375414 34.375414 0 0 1-23.723877-14.524822c-19.36643-30.695792-37.474043-62.069409-53.838676-94.314516a38.732861 38.732861 0 0 1 0-30.405295c14.427991-29.049645 32.632435-55.581655 46.866761-84.340804 9.683215-19.36643 21.303073-28.274988 43.090307-25.079527A246.050496 246.050496 0 0 0 763.037352 639.092199zM1017.899574 752.095319c0-31.373617-0.580993-58.680284 0-85.890118a30.017967 30.017967 0 0 1 9.683216-17.139291c15.589976-14.912151 15.686809-28.178156 1.452482-42.993475a40.088511 40.088511 0 0 1-11.038865-23.433381c-1.452482-25.466856-0.580993-51.127376-0.580993-79.983357 20.72208 0 38.732861-1.161986 57.130969 0.677825a29.630638 29.630638 0 0 1 17.236123 14.137495c19.36643 31.276785 39.410686 62.747234 56.937305 95.282836a38.055035 38.055035 0 0 1 0 30.211632c-15.202648 30.114799-33.891253 58.099291-49.384397 88.504586-8.037069 15.589976-17.623452 22.94922-35.5374 21.012577a450.075839 450.075839 0 0 0-45.89844-0.387329z"
|
||||
p-id="6805"></path>
|
||||
<path
|
||||
d="M875.45948 551.943262c-32.535603 68.84766-35.924728 70.881135-103.51357 62.650402 0-31.276785-1.161986-63.328227 1.258818-95.089172 0-4.744775 20.334752-14.234326 26.435177-11.329362 25.951017 11.813522 50.159054 27.984492 75.819575 43.768132zM879.816927 697.966147c-27.693995 16.461466-54.03234 33.213428-81.532672 47.641418-14.427991 7.64974-25.66052 0.580993-26.338345-16.074137-1.161986-24.885863 0-49.771726 0-74.754421a19.947423 19.947423 0 0 1 4.066951-13.266005c10.554704-10.264208 71.074799 0 78.724539 12.58818s15.783641 27.693995 25.079527 43.864965zM907.510922 834.693144c39.410686-73.398771 16.945626-60.035934 102.15792-58.099291 0 31.470449 0.580993 63.134563-0.968322 94.701844 0 3.292293-15.493144 10.457872-20.044255 8.230733-27.209835-13.750165-53.354515-29.340142-81.145343-44.833286zM909.060236 416.378251c25.079527-14.912151 48.997069-29.533806 73.398771-43.283972 15.686809-8.908558 25.757352 0 26.725674 14.427991 1.936643 29.049645 0.580993 57.518298 0.580993 85.599621-56.356312 14.331158-71.55896 5.809929-100.705438-56.74364zM1009.765674 504.882837v87.148936c0 14.234326-9.683215 18.688605-21.399906 12.200851-25.854184-14.234326-51.127376-29.824303-76.206903-44.542789 14.331158-54.613333 29.921135-63.618723 97.606809-54.806998zM904.218629 568.114232c26.628842 15.880473 52.095697 31.083121 77.465721 46.479432 11.329362 6.778251 12.394515 14.718487 0.677825 21.884066-25.273191 15.589976-50.836879 30.695792-75.819575 45.704776-42.025154-43.090307-42.509314-58.680284-2.323971-114.068274zM1009.765674 749.965012c-64.780709 7.359243-66.233191 6.487754-99.252955-55.097494 26.628842-15.29948 53.160851-31.276785 80.56435-45.704776 10.845201-5.616265 18.688605-0.580993 18.688605 12.975509zM883.883877 709.973333c39.798014 60.616927 38.732861 46.479433-1.258818 110.098156L788.213712 764.973995zM791.31234 488.421371c7.359243-5.809929 10.264208-8.618061 13.556502-10.651536 24.789031-14.718487 49.674894-29.049645 74.27026-43.671301 36.796217 38.151868 37.086714 54.322837 0.580993 106.999527z"
|
||||
p-id="6806"></path>
|
||||
</svg>
|
||||
)
|
||||
})
|
@ -1,33 +0,0 @@
|
||||
import React from "react"
|
||||
|
||||
export const IodIcon = React.forwardRef<
|
||||
SVGSVGElement,
|
||||
React.SVGProps<SVGSVGElement>
|
||||
>((props, ref) => {
|
||||
return (
|
||||
<svg
|
||||
className="icon"
|
||||
viewBox="0 0 1088 1024"
|
||||
version="1.1"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="currentColor"
|
||||
fillRule="evenodd"
|
||||
p-id="32289"
|
||||
{...props}
|
||||
ref={ref}
|
||||
>
|
||||
<path
|
||||
d="M853.333333 458.666667h21.333334v21.333333h-21.333334zM680.533333 217.6h21.333334v21.333333h-21.333334zM740.266667 264.533333h21.333333v21.333334h-21.333333zM629.333333 177.066667h21.333334v21.333333h-21.333334zM398.933333 177.066667h21.333334v21.333333h-21.333334zM343.466667 217.6h21.333333v21.333333h-21.333333zM285.866667 264.533333h21.333333v21.333334h-21.333333zM174.933333 458.666667h21.333334v21.333333h-21.333334zM174.933333 520.533333h21.333334v21.333334h-21.333334zM174.933333 576h21.333334v21.333333h-21.333334zM292.266667 759.466667h21.333333v21.333333h-21.333333zM347.733333 800h21.333334v21.333333h-21.333334zM403.2 846.933333h21.333333v21.333334h-21.333333zM629.333333 846.933333h21.333334v21.333334h-21.333334zM454.4 514.133333h21.333333v21.333334h-21.333333zM512 514.133333h21.333333v21.333334h-21.333333zM567.466667 514.133333h21.333333v21.333334h-21.333333zM680.533333 800h21.333334v21.333333h-21.333334zM740.266667 759.466667h21.333333v21.333333h-21.333333zM853.333333 520.533333h21.333334v21.333334h-21.333334zM853.333333 576h21.333334v21.333333h-21.333334zM509.866667 189.866667h21.333333v147.2h-21.333333zM509.866667 708.266667h21.333333v147.2h-21.333333zM225.92 672.170667l127.488-73.6 10.666667 18.474666-127.488 73.6zM223.509333 375.125333l10.666667-18.474666 127.466667 73.6-10.666667 18.474666zM677.312 422.186667l132.309333-64.554667 9.344 19.2-132.309333 64.512zM684.544 618.517333l10.986667-18.282666 126.186666 75.797333-10.986666 18.304z"
|
||||
p-id="32290"></path>
|
||||
<path
|
||||
d="M520.533333 590.933333c-93.866667 0-189.866667-23.466667-189.866666-66.133333s96-66.133333 189.866666-66.133333 189.866667 23.466667 189.866667 66.133333-93.866667 66.133333-189.866667 66.133333z m0-110.933333c-102.4 0-168.533333 27.733333-168.533333 44.8s66.133333 44.8 168.533333 44.8c102.4 0 168.533333-27.733333 168.533334-44.8s-64-44.8-168.533334-44.8z"
|
||||
p-id="32291"></path>
|
||||
<path
|
||||
d="M520.533333 714.666667c-36.266667 0-57.6-68.266667-64-130.133334l21.333334-2.133333c8.533333 76.8 29.866667 110.933333 42.666666 110.933333 12.8 0 34.133333-34.133333 42.666667-113.066666l21.333333 2.133333c-6.4 64-25.6 132.266667-64 132.266667z m44.8-243.2c-8.533333-78.933333-29.866667-115.2-42.666666-115.2-12.8 0-34.133333 36.266667-42.666667 113.066666l-21.333333-2.133333c6.4-64 27.733333-132.266667 64-132.266667 38.4 0 57.6 70.4 64 134.4l-21.333334 2.133334zM520.533333 209.066667c-36.266667 0-66.133333-29.866667-66.133333-66.133334 0-36.266667 29.866667-66.133333 66.133333-66.133333 36.266667 0 66.133333 29.866667 66.133334 66.133333 2.133333 36.266667-27.733333 66.133333-66.133334 66.133334z m0-113.066667c-25.6 0-44.8 21.333333-44.8 44.8 0 25.6 21.333333 44.8 44.8 44.8 25.6 0 44.8-21.333333 44.8-44.8 2.133333-23.466667-19.2-44.8-44.8-44.8zM857.6 407.466667c-36.266667 0-66.133333-29.866667-66.133333-66.133334 0-36.266667 29.866667-66.133333 66.133333-66.133333s66.133333 29.866667 66.133333 66.133333c2.133333 36.266667-27.733333 66.133333-66.133333 66.133334z m0-113.066667c-25.6 0-44.8 21.333333-44.8 44.8 0 25.6 21.333333 44.8 44.8 44.8s44.8-21.333333 44.8-44.8c2.133333-23.466667-19.2-44.8-44.8-44.8zM857.6 776.533333c-36.266667 0-66.133333-29.866667-66.133333-66.133333 0-36.266667 29.866667-66.133333 66.133333-66.133333s66.133333 29.866667 66.133333 66.133333c2.133333 34.133333-27.733333 66.133333-66.133333 66.133333z m0-113.066666c-25.6 0-44.8 21.333333-44.8 44.8s21.333333 44.8 44.8 44.8 44.8-21.333333 44.8-44.8-19.2-44.8-44.8-44.8zM520.533333 974.933333c-36.266667 0-66.133333-29.866667-66.133333-66.133333 0-36.266667 29.866667-66.133333 66.133333-66.133333 36.266667 0 66.133333 29.866667 66.133334 66.133333 2.133333 36.266667-27.733333 66.133333-66.133334 66.133333z m0-113.066666c-25.6 0-44.8 21.333333-44.8 44.8s21.333333 44.8 44.8 44.8c25.6 0 44.8-21.333333 44.8-44.8s-19.2-44.8-44.8-44.8zM183.466667 407.466667c-36.266667 0-66.133333-29.866667-66.133334-66.133334 0-36.266667 29.866667-66.133333 66.133334-66.133333 36.266667 0 66.133333 29.866667 66.133333 66.133333 2.133333 36.266667-27.733333 66.133333-66.133333 66.133334z m0-113.066667c-25.6 0-44.8 21.333333-44.8 44.8 0 25.6 21.333333 44.8 44.8 44.8 25.6 0 44.8-21.333333 44.8-44.8 2.133333-23.466667-19.2-44.8-44.8-44.8zM183.466667 776.533333c-36.266667 0-66.133333-29.866667-66.133334-66.133333 0-36.266667 29.866667-66.133333 66.133334-66.133333 36.266667 0 66.133333 29.866667 66.133333 66.133333 2.133333 34.133333-27.733333 66.133333-66.133333 66.133333z m0-113.066666c-25.6 0-44.8 21.333333-44.8 44.8s21.333333 44.8 44.8 44.8c25.6 0 44.8-21.333333 44.8-44.8s-19.2-44.8-44.8-44.8z"
|
||||
p-id="32292"></path>
|
||||
<path
|
||||
d="M514.133333 731.733333c-117.333333 0-215.466667-96-215.466666-215.466666 0-117.333333 96-215.466667 215.466666-215.466667 117.333333 0 215.466667 96 215.466667 215.466667s-96 215.466667-215.466667 215.466666z m0-386.133333c-93.866667 0-172.8 76.8-172.8 172.8s76.8 172.8 172.8 172.8c93.866667 0 172.8-76.8 172.8-172.8s-76.8-172.8-172.8-172.8z"
|
||||
p-id="32293"></path>
|
||||
</svg>
|
||||
)
|
||||
})
|
@ -1,22 +0,0 @@
|
||||
import React from "react"
|
||||
|
||||
export const MedicineBottleFillIcon = React.forwardRef<
|
||||
SVGSVGElement,
|
||||
React.SVGProps<SVGSVGElement>
|
||||
>((props, ref) => {
|
||||
return (
|
||||
<svg
|
||||
className="icon"
|
||||
viewBox="0 0 1024 1024"
|
||||
version="1.1"
|
||||
p-id="25925"
|
||||
fill="currentColor"
|
||||
fillRule="evenodd"
|
||||
ref={ref}
|
||||
{...props}>
|
||||
<path
|
||||
d="M725.333333 213.333333v85.333334a128 128 0 0 1 128 128v469.333333a42.666667 42.666667 0 0 1-42.666666 42.666667H213.333333a42.666667 42.666667 0 0 1-42.666666-42.666667V426.666667a128 128 0 0 1 128-128V213.333333h426.666666z m-170.666666 256h-85.333334v85.333334H384v85.333333h85.290667L469.333333 725.333333h85.333334l-0.042667-85.333333H640v-85.333333h-85.333333v-85.333334z m256-384v85.333334H213.333333V85.333333h597.333334z"
|
||||
p-id="25926"></path>
|
||||
</svg>
|
||||
)
|
||||
})
|
@ -1,26 +0,0 @@
|
||||
import React from "react"
|
||||
|
||||
export const NSDCIcon = React.forwardRef<
|
||||
SVGSVGElement,
|
||||
React.SVGProps<SVGSVGElement>
|
||||
>((props, ref) => {
|
||||
return (
|
||||
<svg
|
||||
className="icon"
|
||||
viewBox="0 0 1024 1024"
|
||||
version="1.1"
|
||||
fill="currentColor"
|
||||
fillRule="evenodd"
|
||||
ref={ref}
|
||||
{...props}>
|
||||
<path
|
||||
d="M482.233219 569.858314a74.216 74.216 0 1 0 56.802467-137.133287 74.216 74.216 0 1 0-56.802467 137.133287Z"
|
||||
fill=""
|
||||
p-id="7898"></path>
|
||||
<path
|
||||
d="M952.472 447.816c-25.08-25.224-59-45.952-99.736-63.784-81.616-35.808-191.784-59.872-317.32-66.536a1387.864 1387.864 0 0 0-49-1.16c28.56-38.272 56.824-72.624 83.496-101.04 52.328-55.376 100.896-84.224 123.216-85.088 9.712-0.288 15.224 1.448 19.856 4.352 9.568 6.376 22.176 21.456 21.456 47.256-0.584 19.856-8.984 58.128-19.568 81.76l67.696 30.152c15.656-34.648 25.08-75.528 26.096-109.88 1.304-50.88-23.776-91.032-55.232-111.472-18.992-12.176-41.312-17.104-63.056-16.384-62.912 2.464-116.984 47.544-174.384 108.432-40.592 42.912-81.904 96.112-122.64 154.24-28.264 2.176-55.952 5.072-82.336 9.28-13.48-45.52-22.472-86.544-23.048-115.096-0.432-25.656 2.176-46.824 6.52-60.304 4.496-13.624 8.984-17.688 12.464-19.424 2.752-1.448 5.656-2.32 10-2.608 20.008-1.592 60.88 6.96 100.312 43.776l50.592-54.216C421.32 76.584 370.88 57.88 328.984 56a233.76 233.76 0 0 0-17.544 0.288l-0.144 0.144a103.392 103.392 0 0 0-38.128 10.44v0.144c-25.512 12.904-41.024 37.256-49.288 62.624-8.264 25.512-10.584 53.632-10.144 84.368 0.728 38.128 9.568 82.624 22.616 129.016-22.32 5.656-43.488 12.032-63.056 19.136-39.576 14.496-73.208 31.744-99.008 53.632-25.952 21.888-46.096 50.88-46.096 85.528 0 39.864 21.6 72.912 47.544 95.672 26.096 22.904 57.256 38.416 87.408 50.304l26.96-69.288c-25.08-9.712-49.432-22.76-65.376-36.672-15.8-13.92-22.328-25.8-22.328-40.008 0-5.8 3.624-15.368 19.568-28.848 15.944-13.336 42.76-28.12 76.832-40.592 17.976-6.52 38.272-12.464 59.872-17.688 9.424 25.8 19.856 51.464 30.584 76.688-57.84 123.216-76.832 241.216-75.528 316.736 0.872 50.736 19.424 95.96 56.968 120.32 24.352 15.656 55.232 23.48 87.408 19.136 32.04-4.352 64.656-19.424 99.736-44.504l-42.912-60.304c-28.704 20.44-51.168 29.136-66.976 31.312-15.8 2.032-25.656-0.576-36.816-7.976-11.888-7.68-22.616-24.936-23.192-59.288-0.872-50.448 10.584-133.512 43.632-223.968 6.088 11.888 12.032 23.632 17.976 34.648h-0.144c39.576 73.352 99.008 161.2 161.2 228.168 31.024 33.488 62.48 61.752 95.096 80.6 31.6 18.12 69.144 27.832 103.792 12.176 30.584-10.584 49.576-37.832 58.856-67.264 9.568-30.728 12.032-66.68 9.424-107.416-5.512-81.32-33.048-181.344-86.104-279.488l-65.232 35.08c47.984 89.152 72.624 180.912 77.264 249.336 2.32 34.352-0.728 62.768-6.232 80.16-5.36 17.392-10.872 20.152-11.6 20.44l-3.48 0.872-3.04 1.592c-4.208 2.176-14.784 2.752-36.528-9.856-21.888-12.608-49.72-36.528-77.696-66.68C509.04 734.256 451.2 649.312 414.96 581.904c-14.352-26.672-29.136-57.552-43.488-90.312a694.256 694.256 0 0 1 33.632-57.84c9.28-14.496 18.848-28.416 28.416-42.472 10.872-0.288 21.6-1.304 32.616-1.304 21.312 0 43.056 0.584 65.376 1.736 118.288 6.088 221.496 29.568 291.512 60.304 34.936 15.224 61.32 32.472 76.832 48.128 15.656 15.656 19.568 27.104 18.992 36.384 0 2.464-4.352 13.048-22.032 24.936-17.832 11.888-46.384 23.336-80.888 29.568l12.904 73.064c42.76-7.68 80.016-21.456 109.152-41.024 29.28-19.568 53.2-46.68 55.088-82.776 1.728-35.224-15.52-67.408-40.6-92.48z m-615.936-43.784a34.84 34.84 0 0 1-1.592-4.2c1.448-0.144 2.896-0.288 4.208-0.432-0.88 1.592-1.752 3.04-2.616 4.632z"
|
||||
fill=""
|
||||
p-id="7899"></path>
|
||||
</svg>
|
||||
)
|
||||
})
|
@ -1,23 +0,0 @@
|
||||
import React from "react"
|
||||
|
||||
export const NewBottleIcon = React.forwardRef<
|
||||
SVGSVGElement,
|
||||
React.SVGProps<SVGSVGElement>
|
||||
>((props, ref) => {
|
||||
return (
|
||||
<svg
|
||||
className="icon"
|
||||
viewBox="0 0 1024 1024"
|
||||
version="1.1"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
p-id="40222"
|
||||
fill="currentColor"
|
||||
fillRule="evenodd"
|
||||
ref={ref}
|
||||
{...props}>
|
||||
<path
|
||||
d="M848.818342 511.548501l-319.661376 308.373898c-14.899471 16.705467-35.216931 27.541446-56.888888 30.70194 16.705467-17.608466 29.798942-38.828924 37.474426-61.40388l175.633157-164.345679-105.199294-105.650794L717.883598 397.770723c4.96649-4.514991 8.126984-10.835979 8.126984-17.608466s-3.160494-13.093474-8.126984-17.608465c-9.029982-10.38448-24.832451-11.738977-35.216931-2.708995L542.250441 478.589065l-30.70194-30.70194v-30.70194L632.098765 295.731922s92.557319-92.557319 199.562611 13.544974c107.45679 106.102293 16.253968 201.820106 16.253968 201.820106h0.902998z m-339.075838 74.948853v-74.948853l30.70194-30.70194 38.828925 38.828924-69.530865 66.821869z m-200.465608 294.828925C216.719577 881.326279 139.964727 819.470899 139.964727 758.067019v-492.134038c0-61.40388 80.818342-123.259259 169.312169-123.25926S478.589065 204.077601 478.589065 265.932981v492.134038c0 61.40388-76.75485 123.259259-169.312169 123.25926zM447.887125 263.223986C425.763668 206.335097 370.229277 169.312169 309.276896 170.666667c-60.952381-1.354497-116.938272 35.216931-139.061728 92.557319v246.067019h61.40388v215.816579c-1.354497 11.738977 4.514991 23.026455 14.447971 29.347442 9.932981 6.320988 23.026455 6.320988 32.959436 0s15.802469-17.608466 14.447972-29.347442v-213.559083h153.961199l0.451499-248.324515z m-184.663139 30.70194c8.126984 0 16.253968 3.160494 22.123457 9.029982 5.869489 5.869489 9.029982 13.996473 9.029982 22.123457v184.663139H232.522046V326.433862c0-8.126984 2.708995-16.253968 9.029982-22.123456 5.869489-5.869489 13.996473-9.029982 22.123457-9.029983l-0.451499-1.354497z m0 0"
|
||||
p-id="40223"></path>
|
||||
</svg>
|
||||
)
|
||||
})
|
@ -1,23 +0,0 @@
|
||||
import React from "react"
|
||||
|
||||
export const NotCollectIcon = React.forwardRef<
|
||||
SVGSVGElement,
|
||||
React.SVGProps<SVGSVGElement>
|
||||
>((props, ref) => {
|
||||
return (
|
||||
<svg
|
||||
className="icon"
|
||||
viewBox="0 0 1059 1024"
|
||||
version="1.1"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
p-id="73488"
|
||||
fill="currentColor"
|
||||
fillRule="evenodd"
|
||||
ref={ref}
|
||||
{...props}>
|
||||
<path
|
||||
d="M253.488042 1024c-16.9 0-33.2875-5.1125-47.6125-15.3625-26.625-18.425-39.425-49.6625-34.3125-81.925l40.9625-251.9c1.5375-10.2375-1.5375-20.475-8.7-27.65L28.213042 466.4375c-22.0125-22.525-29.1875-55.3-19.45-84.9875 9.725-29.7 35.325-51.2 66.05-55.8125l237.575-36.35c10.75-1.5375 19.4625-8.1875 24.0625-17.925L441.388042 48.125c13.825-29.7 42.5-48.125 75.2625-48.125s61.4375 18.4375 75.2625 48.125l104.45 223.2375c4.6125 9.725 13.825 16.375 24.0625 17.925L958.000542 325.625a82.355 82.355 0 0 1 66.05 55.8125c10.2375 29.7 2.5625 62.4625-19.45 84.9875l-175.625 180.7375c-7.1625 7.175-10.2375 17.925-8.7 27.65l40.9625 251.9c5.125 31.75-8.1875 63.4875-34.3 81.925-26.1125 18.4375-59.9 20.4875-88.0625 4.6125l-206.85-114.6875c-9.725-5.1125-20.9875-5.1125-30.7125 0l-207.3625 115.2c-12.8125 6.65-26.6375 10.2375-40.4625 10.2375zM516.650542 51.2c-12.8 0-23.55 7.1625-29.1875 18.4375L383.525542 292.875c-11.775 25.0875-35.325 43.0125-62.975 47.1l-237.575 36.35c-12.2875 2.05-21.5 9.7375-25.6 21.5-4.1 11.775-1.025 24.0625 7.675 32.775L240.688042 611.325c18.4375 18.95 26.625 45.5625 22.525 71.675L222.250542 934.9125c-2.05 12.8 3.075 24.575 13.3125 31.7375 10.2375 7.175 23.0375 7.6875 33.7875 1.5375l207.3625-115.2c25.0875-13.825 55.3-13.825 80.3875 0l207.3625 115.2c10.75 6.1375 23.55 5.625 33.8-1.5375 10.2375-7.1625 15.3625-18.95 13.3125-31.7375L770.625542 683.0125c-4.1-26.1125 4.1-52.7375 22.525-71.675l175.625-180.7375c8.7-8.7 11.2625-20.9875 7.675-32.775-4.0875-11.775-13.3125-19.9625-25.6-21.5l-237.5625-36.35c-27.65-4.0875-51.2-22.0125-62.975-47.1L545.838042 69.6375c-5.625-11.2625-16.375-18.4375-29.1875-18.4375z m0 0"
|
||||
p-id="73489"></path>
|
||||
</svg>
|
||||
)
|
||||
})
|
@ -1,24 +0,0 @@
|
||||
import React from "react"
|
||||
|
||||
export const ResearchInstitutesIcon = React.forwardRef<
|
||||
SVGSVGElement,
|
||||
React.SVGProps<SVGSVGElement>
|
||||
>((props, ref) => {
|
||||
return (
|
||||
<svg
|
||||
className="icon"
|
||||
viewBox="0 0 1024 1024"
|
||||
version="1.1"
|
||||
fill="currentColor"
|
||||
fillRule="evenodd"
|
||||
ref={ref}
|
||||
{...props}>
|
||||
<path
|
||||
d="M977.997713 356.640616l-433.994529-149.255836c-27.084774-9.776045-21.395041-9.833246-48.245647-0.11619L59.169291 355.289237c-26.854181 9.717056-26.682578 25.531403 0.402196 35.336048l103.741547 35.223434c-45.948661 44.509693-48.937424 90.77296-49.513011 144.425909-17.825328 6.844482-30.363118 24.094223-30.363118 44.2505 0 18.515317 10.580437 34.531656 26.051577 42.325321-7.30388 54.571743-28.409339 116.85135-90.284962 190.658788 30.650912 23.692027 46.408058 31.600094 70.100085 39.479561 86.488232-37.150399 75.964996-135.85824 69.234916-234.076295 11.905002-8.626658 19.611078-22.601629 19.611078-38.387376 0-16.933346-8.912664-31.744885-22.195858-40.139163 1.494382-52.587576 12.938199-99.657023 52.27297-130.107731 0.344995-0.832993 1.206588-1.52477 2.93335-2.24336l298.340069-120.53189c11.098823-4.458119 23.634826 0.920582 28.062557 12.019405l0.402196 0.949183c4.431306 11.070222-0.918794 23.634826-12.017617 28.062557l-252.164391 100.808198 225.655204 76.540584c27.027572 9.802858 21.389678 9.861846 48.188446 0.141215L978.34092 392.007052c26.857756-9.747444 26.684365-25.561791-0.400408-35.337836L977.997713 356.640616zM977.997713 356.640616"
|
||||
p-id="5109"></path>
|
||||
<path
|
||||
d="M498.801714 597.128809l-273.092884-92.610549 0 69.665713c14.260977 13.056177 22.140444 31.802086 22.140444 52.676953 0 18.74591-6.554901 35.797233-17.653724 48.563829 3.621552 10.925431 9.890447 21.622058 18.976502 24.959391 158.946079 87.866423 378.616606 86.88864 555.332599-8.885851 13.109803-10.898618 23.288043-24.412405 23.288043-37.493607l0-153.370748-280.570155 96.611059c-26.798768 9.690243-21.334264 9.659855-48.363624-0.11619L498.801714 597.128809zM498.801714 597.128809"
|
||||
p-id="5110"></path>
|
||||
</svg>
|
||||
)
|
||||
})
|
@ -1,21 +0,0 @@
|
||||
import React from "react"
|
||||
|
||||
export const ResearchPaperIcon = React.forwardRef<
|
||||
SVGSVGElement,
|
||||
React.SVGProps<SVGSVGElement>
|
||||
>((props, ref) => {
|
||||
return (
|
||||
<svg
|
||||
className="icon"
|
||||
viewBox="0 0 1024 1024"
|
||||
version="1.1"
|
||||
fill="currentColor"
|
||||
fillRule="evenodd"
|
||||
ref={ref}
|
||||
{...props}>
|
||||
<path
|
||||
d="M715.648 196.928V0H308.352v118.144h220.544l11.904 11.52 203.648 196.992 11.904 11.52v528.32H960V236.288h-244.352v-39.36zM756.352 0v196.928H960L756.352 0zM471.296 157.568v236.288h244.352V1024H64V157.568h407.296zM512 354.496h203.648L512 157.568v196.928z m-136.064 47.424H163.328c-6.4 0-11.52 8-11.52 17.792 0 9.792 5.12 17.792 11.52 17.792h212.608c6.4 0 11.52-8 11.52-17.792 0-9.792-5.12-17.792-11.52-17.792z m100.352 189.888H168.512c-9.216 0-16.704 7.936-16.704 17.728 0 9.856 7.488 17.792 16.704 17.792h307.776c9.216 0 16.64-7.936 16.64-17.792 0-9.792-7.424-17.728-16.64-17.728z m-302.336 225.344H581.76c12.224 0 22.144-7.936 22.144-17.728 0-9.856-9.92-17.792-22.144-17.792H173.952c-12.224 0-22.144 7.936-22.144 17.792 0 9.792 9.92 17.728 22.144 17.728z"
|
||||
p-id="10242"></path>
|
||||
</svg>
|
||||
)
|
||||
})
|
@ -1,22 +0,0 @@
|
||||
import React from "react"
|
||||
|
||||
export const SettingIcon = React.forwardRef<
|
||||
SVGSVGElement,
|
||||
React.SVGProps<SVGSVGElement>
|
||||
>((props, ref) => {
|
||||
return (
|
||||
<svg
|
||||
className="icon"
|
||||
viewBox="0 0 1084 1024"
|
||||
version="1.1"
|
||||
p-id="10420"
|
||||
fill="currentColor"
|
||||
fillRule="evenodd"
|
||||
ref={ref}
|
||||
{...props}>
|
||||
<path
|
||||
d="M1072.147851 406.226367c-6.331285-33.456782-26.762037-55.073399-52.047135-55.073399-0.323417 0-0.651455 0.003081-0.830105 0.009241l-4.655674 0c-73.124722 0-132.618162-59.491899-132.618162-132.618162 0-23.731152 11.447443-50.336101 11.546009-50.565574 13.104573-29.498767 3.023185-65.672257-23.427755-84.127081l-1.601687-1.127342-134.400039-74.661726-1.700252-0.745401c-8.753836-3.805547-18.334698-5.735272-28.479231-5.735272-20.789593 0-41.235746 8.344174-54.683758 22.306575-14.741683 15.216028-65.622973 58.649474-104.721083 58.649474-39.450789 0-90.633935-44.286652-105.438762-59.784516-13.518857-14.247316-34.128258-22.753199-55.127302-22.753199-9.945862 0-19.354234 1.861961-27.958682 5.531982l-1.746455 0.74078-139.141957 76.431283-1.643269 1.139662c-26.537186 18.437884-36.675557 54.579032-23.584845 84.062398 0.115506 0.264895 11.579891 26.725075 11.579891 50.634877 0 73.126262-59.491899 132.618162-132.618162 132.618162l-4.581749 0c-0.318797-0.00616-0.636055-0.01078-0.951772-0.01078-25.260456 0-45.672728 21.618157-52.002472 55.0811-0.462025 2.453354-11.313456 60.622322-11.313456 106.117939 0 45.494078 10.85143 103.659965 11.314996 106.119479 6.334365 33.458322 26.758957 55.076479 52.036353 55.076479 0.320337 0 0.651455-0.00616 0.842426-0.012321l4.655674 0c73.126262 0 132.618162 59.491899 132.618162 132.616622 0 23.760413-11.444363 50.333021-11.546009 50.565574-13.093793 29.474125-3.041666 65.646075 23.395414 84.151722l1.569346 1.093459 131.838879 73.726895 1.675611 0.7377c8.750757 3.84251 18.305437 5.790715 28.397607 5.790715 21.082208 0 41.676209-8.706094 55.0888-23.290689 18.724339-20.347588 69.527086-62.362616 107.04815-62.362616 40.625872 0 92.72537 47.100385 107.759669 63.583903 13.441852 14.831008 34.176001 23.689571 55.470741 23.695731l0.00616 0c9.895039 0 19.27877-1.883523 27.893999-5.598205l1.711034-0.73924 136.659342-75.531873 1.617088-1.128882c26.492523-18.456365 36.601633-54.600594 23.538642-84.016195-0.115506-0.267974-11.595291-27.082374-11.595291-50.67646 0-73.124722 59.49344-132.616622 132.618162-132.616622l4.517066-0.00154c0.300316 0.00616 0.599092 0.009241 0.899409 0.009241 25.331299-0.00154 45.785153-21.619697 52.107197-55.054918 0.112426-0.589852 11.325776-59.507301 11.325776-106.14104C1083.464388 466.640776 1072.609877 408.67356 1072.147851 406.226367zM377.486862 945.656142l-115.32764-64.487932c5.082277-13.052211 15.437801-43.51815 15.437801-75.017486 0-109.382917-84.176364-199.816642-192.587488-208.134635-2.647404-15.427021-8.873963-54.967133-8.873963-85.667166 0-30.65691 6.223479-70.232445 8.869343-85.671786 108.415744-8.311832 192.592108-98.745557 192.592108-208.134635 0-31.416171-10.300081-61.797405-15.371577-74.854236l122.721583-67.40331c0.003081 0 0.00462 0.00154 0.007701 0.00154 4.423121 4.518606 22.121764 22.080182 46.558275 39.493911 39.929754 28.46229 77.952885 42.894416 113.014434 42.894416 34.716571 0 72.437845-14.151831 112.115025-42.06431 24.282503-17.07953 41.896442-34.302288 46.308782-38.74543 0.009241-0.00154 0.018481-0.00462 0.026182-0.00616l118.301542 65.726159c-5.077657 13.055291-15.416239 43.499669-15.416239 74.958962 0 109.389077 84.174824 199.822802 192.590568 208.134635 2.645865 15.462442 8.872423 55.107281 8.872423 85.671786 0 30.687711-6.223479 70.241685-8.869343 85.673326C890.042174 606.334084 805.86427 696.767809 805.86427 806.158426c0 31.450053 10.317022 61.851309 15.393138 74.903519l-119.783103 66.198965c-5.168521-5.490399-22.603811-23.363073-46.740005-41.288109-40.701336-30.224145-79.662378-45.549521-115.800446-45.549521-35.79155 0-74.458435 15.038919-114.927219 44.694774C400.22004 922.554885 382.666163 940.255068 377.486862 945.656142zM731.271848 511.646647c0-105.803762-86.081448-191.88059-191.888289-191.88059-105.803762 0-191.88059 86.076827-191.88059 191.88059 0 105.803762 86.076827 191.882129 191.88059 191.882129C645.19194 703.528777 731.271848 617.450409 731.271848 511.646647zM539.383558 395.903184c63.825696 0 115.751164 51.922387 115.751164 115.743463 0 63.825696-51.925468 115.751164-115.751164 115.751164-63.821076 0-115.743463-51.925468-115.743463-115.751164C423.640095 447.824031 475.562482 395.903184 539.383558 395.903184z"
|
||||
p-id="10421"></path>
|
||||
</svg>
|
||||
)
|
||||
})
|
@ -1,23 +0,0 @@
|
||||
import React from "react"
|
||||
|
||||
export const ShareIcon = React.forwardRef<
|
||||
SVGSVGElement,
|
||||
React.SVGProps<SVGSVGElement>
|
||||
>((props, ref) => {
|
||||
return (
|
||||
<svg
|
||||
className="icon"
|
||||
viewBox="0 0 1024 1024"
|
||||
version="1.1"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
p-id="75461"
|
||||
fill="currentColor"
|
||||
fillRule="evenodd"
|
||||
ref={ref}
|
||||
{...props}>
|
||||
<path
|
||||
d="M1009.777778 503.466667l-443.733334-455.111111c-5.688889-5.688889-11.377778 0-11.377777 5.688888v267.377778C8.533333 409.6 2.844444 918.755556 17.066667 932.977778c0 0 45.511111-48.355556 164.977777-113.777778 85.333333-48.355556 224.711111-85.333333 369.777778-102.4v261.688889c0 8.533333 11.377778 11.377778 14.222222 5.688889l443.733334-480.711111z m-398.222222 358.4v-199.111111l-36.977778-2.844445c-221.866667 8.533333-378.311111 73.955556-497.777778 156.444445 76.8-275.911111 267.377778-403.911111 466.488889-438.044445l68.266667-2.844444v-199.111111l312.888888 312.888888s8.533333 5.688889 8.533334 14.222223-8.533333 14.222222-8.533334 14.222222l-312.888888 344.177778z"
|
||||
p-id="75462"></path>
|
||||
</svg>
|
||||
)
|
||||
})
|
@ -1,23 +0,0 @@
|
||||
import React from "react"
|
||||
|
||||
export const Ship1Icon = React.forwardRef<
|
||||
SVGSVGElement,
|
||||
React.SVGProps<SVGSVGElement>
|
||||
>((props, ref) => {
|
||||
return (
|
||||
<svg
|
||||
className="icon"
|
||||
viewBox="0 0 1024 1024"
|
||||
version="1.1"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
p-id="42511"
|
||||
fill="currentColor"
|
||||
fillRule="evenodd"
|
||||
ref={ref}
|
||||
{...props}>
|
||||
<path
|
||||
d="M532.48 241.777778h-28.444444a151.608889 151.608889 0 0 0-48.924445 8.533333l-142.222222 76.231111V199.111111a42.382222 42.382222 0 0 1 42.382222-42.097778H455.111111V102.115556a42.666667 42.666667 0 0 1 43.52-42.382223H540.444444a42.666667 42.666667 0 0 1 42.382223 42.382223v59.164444h101.546666a42.666667 42.666667 0 0 1 42.382223 42.382222v131.413334l-156.728889-85.333334a209.351111 209.351111 0 0 0-38.115556-8.533333z m309.191111 461.653333a768 768 0 0 0-220.16 85.333333c-113.777778 63.715556-118.613333 50.915556-207.644444 8.533334a961.422222 961.422222 0 0 0-203.093334-76.231111L151.608889 631.466667c-63.715556-80.497778-76.231111-119.466667-42.382222-139.946667L455.111111 309.475556a127.431111 127.431111 0 0 1 122.88 0l347.022222 182.044444c21.333333 17.066667 38.115556 55.182222-25.315555 139.946667z m122.595556 186.311111a42.097778 42.097778 0 0 1-42.382223 42.097778c-8.248889 0-12.515556 0-16.782222-3.982222a199.111111 199.111111 0 0 0-105.813333-34.133334c-63.431111 0-143.928889 59.448889-143.928889 59.448889S612.977778 995.555556 512 995.555556a220.728889 220.728889 0 0 1-143.928889-42.382223s-89.031111-63.431111-143.928889-59.448889a129.706667 129.706667 0 0 0-106.097778 34.133334 25.884444 25.884444 0 0 1-16.782222 3.982222 42.097778 42.097778 0 0 1-42.382222-42.097778 46.933333 46.933333 0 0 1 21.048889-42.382222s119.182222-136.248889 304.355555 0c0 0 59.448889 42.382222 101.546667 42.382222h42.382222a216.462222 216.462222 0 0 0 101.546667-42.382222c50.915556-33.848889 178.062222-118.613333 304.924444 0a47.502222 47.502222 0 0 1 28.444445 42.382222z m0 0"
|
||||
p-id="42512"></path>
|
||||
</svg>
|
||||
)
|
||||
})
|
@ -1,24 +0,0 @@
|
||||
import React from "react"
|
||||
|
||||
export const TalentPoolIcon = React.forwardRef<
|
||||
SVGSVGElement,
|
||||
React.SVGProps<SVGSVGElement>
|
||||
>((props, ref) => {
|
||||
return (
|
||||
<svg
|
||||
className="icon"
|
||||
viewBox="0 0 1024 1024"
|
||||
version="1.1"
|
||||
fill="currentColor"
|
||||
fillRule="evenodd"
|
||||
ref={ref}
|
||||
{...props}>
|
||||
<path
|
||||
d="M194.7 296.1H571v50.6H194.7zM679.7 294.1c-15 1.9-26.2 13.1-26.2 28.1v1.9c1.8 15 13.1 26.2 28.1 26.2 15-1.9 26.2-13.2 26.2-28.1v-1.9c-1.9-14.9-13.2-26.2-28.1-26.2z"
|
||||
p-id="19241"></path>
|
||||
<path
|
||||
d="M932.4 86.4c-18.7-15-43.1-22.5-67.4-22.5H279c-26.2 0-50.5 7.5-67.4 22.5-18.7 16.9-29.9 39.4-29.9 61.9v41H163c-26.2 0-50.5 7.5-67.4 22.4C75 230.5 63.8 253 63.8 277.4v598c0 24.3 11.2 46.8 29.9 61.8s43.1 22.5 67.4 22.5h584.2c26.2 0 50.6-7.5 67.4-22.5 18.7-16.8 31.8-39.3 31.8-61.8v-30h16.9c26.2 0 50.5-7.5 67.4-22.5 18.7-16.8 30-39.4 30-61.8v-611c3.6-24.4-7.6-46.8-26.4-63.7z m-345.5 711c-2.3 48.8-57.3 48.8-138.8 49-81.5-0.2-136.5-0.2-138.8-49-2.3-48.8 30-90.6 54.9-111 24.9-20.4 43-19.4 43-19.4l40.9-0.1 40.9 0.1s18.2-0.9 43 19.4c24.9 20.4 57.2 62.2 54.9 111zM368.5 578.5c0-43.9 35.6-79.6 79.6-79.6s79.6 35.6 79.6 79.6S492 658 448.1 658s-79.6-35.6-79.6-79.5z m412.3-197.9H128.2v-86.3c0-18.8 2.5-40.8 40.7-40.8h575.2c14.2 0 36.7 15 36.7 33.8v93.3z m117.1 363.6c0 3.8-6.4 22.5-18.6 31-12.2 8.4-30.1 6.5-32 6.5h-2.9V277.3c0-34.3-12.2-86.2-102.8-86.2H242.3v-30c0-3.8 0-7.5 1.9-11.3 1.9-3.8 3.8-5.6 7.5-9.4 9.4-9.4 22.5-15 39.3-15h548.2c26.2 0 58.7 15 58.7 33.7v585.1z"
|
||||
p-id="19242"></path>
|
||||
</svg>
|
||||
)
|
||||
})
|
@ -1,22 +0,0 @@
|
||||
import React from "react"
|
||||
|
||||
export const TechCompanyIcon = React.forwardRef<
|
||||
SVGSVGElement,
|
||||
React.SVGProps<SVGSVGElement>
|
||||
>((props, ref) => {
|
||||
return (
|
||||
<svg
|
||||
className="icon"
|
||||
viewBox="0 0 1024 1024"
|
||||
version="1.1"
|
||||
fill="currentColor"
|
||||
fillRule="evenodd"
|
||||
ref={ref}
|
||||
{...props}
|
||||
>
|
||||
<path
|
||||
d="M968 905.6h-48.64V357.376c0-36.992-28.608-66.816-63.808-66.816h-128v615.168h-48V135.68c0-36.864-28.544-66.816-63.872-66.816H168.448c-35.328 0-63.872 29.952-63.872 66.816v769.92H56a24.32 24.32 0 0 0-24 24.64 24.32 24.32 0 0 0 24 24.64h576.128v0.128h287.168v-0.128h48.64a24.32 24.32 0 0 0 24.064-24.64 24.32 24.32 0 0 0-24-24.64zM440.192 265.92h95.808v73.856H440.192V265.856z m0 196.864h95.808v73.856H440.192V462.72z m0 196.928h95.808v73.856H440.192v-73.856z m-192-393.792h96v73.856h-96V265.856z m0 196.864h96v73.856h-96V462.72z m0 196.928h96v73.856h-96v-73.856z"
|
||||
p-id="9056"></path>
|
||||
</svg>
|
||||
)
|
||||
})
|
@ -1,53 +1,27 @@
|
||||
import React, { useMemo, useState } from "react"
|
||||
import { useOptionLayoutContext } from "@/components/Layouts/Layout.tsx"
|
||||
import React, { useContext } from "react"
|
||||
import { HistoryContext } from "@/components/Layouts/Layout.tsx"
|
||||
import { PanelLeftIcon } from "lucide-react"
|
||||
import { Button, Tooltip } from "antd"
|
||||
import { Button } from "antd"
|
||||
import { PlusOutlined } from "@ant-design/icons"
|
||||
import { useMessageOption } from "@/hooks/useMessageOption.tsx"
|
||||
import { useTranslation } from "react-i18next"
|
||||
import { NavLink, useLocation } from "react-router-dom"
|
||||
import logo from "@/assets/logo.png"
|
||||
import { BellIcon } from "@/components/Icons/Bell.tsx"
|
||||
import { ShareIcon } from "@/components/Icons/Share.tsx"
|
||||
import { NotCollectIcon } from "@/components/Icons/NotCollect.tsx"
|
||||
import { CollectIcon } from "@/components/Icons/Collect.tsx"
|
||||
import { SettingIcon } from "@/components/Icons/Setting.tsx"
|
||||
|
||||
type Props = {}
|
||||
|
||||
export const Header: React.FC<Props> = ({}) => {
|
||||
const location = useLocation()
|
||||
|
||||
const { showOptionSidebar, setShowOptionSidebar } = useOptionLayoutContext()
|
||||
|
||||
const showLeft = useMemo<boolean>(() => {
|
||||
console.log(location.pathname)
|
||||
if (location.pathname.includes("/settings")) {
|
||||
return true
|
||||
}
|
||||
return showOptionSidebar
|
||||
}, [location.pathname, showOptionSidebar])
|
||||
export const Header = () => {
|
||||
const { show, setShow } = useContext(HistoryContext)
|
||||
|
||||
const { t } = useTranslation(["option", "common", "settings"])
|
||||
|
||||
const { clearChat } = useMessageOption()
|
||||
|
||||
// 是否隐藏logo
|
||||
const hideLogo = useMemo(() => {
|
||||
return localStorage.getItem("hideLogo") === "true"
|
||||
}, [])
|
||||
|
||||
const [collect, setCollect] = useState<boolean>(false)
|
||||
return (
|
||||
<div
|
||||
className={`h-[60px] absolute inset-0 pl-5 z-10 flex items-center transition-all duration-300 ease-in-out ${showOptionSidebar && !location.pathname.includes("/settings") ? "left-[300px]" : ""}`}>
|
||||
className={`w-full h-[60px] absolute inset-0 pl-5 z-10 flex items-center transition-all duration-300 ease-in-out ${show ? "left-[300px]" : ""}`}>
|
||||
{/*控制侧边栏显示隐藏与新建对话*/}
|
||||
{!showLeft && (
|
||||
{!show && (
|
||||
<div className="flex items-center gap-3">
|
||||
<button
|
||||
className="text-gray-500 dark:text-gray-400"
|
||||
onClick={() => {
|
||||
setShowOptionSidebar(!showOptionSidebar)
|
||||
setShow(!show)
|
||||
}}>
|
||||
<PanelLeftIcon className="w-6 h-6" />
|
||||
</button>
|
||||
@ -73,85 +47,21 @@ export const Header: React.FC<Props> = ({}) => {
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
{location.pathname.includes("/settings") && (
|
||||
<h2 className="text-xl font-bold text-zinc-700 dark:text-zinc-300 mr-3">
|
||||
<NavLink
|
||||
to="/"
|
||||
className="!text-gray-500 dark:text-gray-400 flex items-center gap-2 hover:text-gray-600 dark:hover:text-gray-300 transition-colors">
|
||||
{!hideLogo && <img src={logo} alt="logo" className="w-8" />}
|
||||
<p>
|
||||
<span className="text-[#d30100]">数联网</span>科创智能体
|
||||
</p>
|
||||
</NavLink>
|
||||
</h2>
|
||||
)}
|
||||
{/* 项目标题 */}
|
||||
<div
|
||||
className={`
|
||||
absolute left-1/2 transform -translate-x-1/2
|
||||
w-[600px] h-[60px] dark:bg-black
|
||||
w-[600px] h-[55px] bg-white dark:bg-black
|
||||
flex items-center justify-center
|
||||
transition-[top] drop-shadow
|
||||
${showOptionSidebar ? "-top-[60px]" : "-top-[2px] delay-200"}
|
||||
shadow-[0px_0px_5px_rgba(0,0,0,0.2)]
|
||||
rounded-b-[15px]
|
||||
transition-[top]
|
||||
${show ? "-top-[56px]" : "-top-[1px] delay-200"}
|
||||
`}>
|
||||
<svg
|
||||
className="icon"
|
||||
viewBox="0 0 8960 1024"
|
||||
version="1.1"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
p-id="9634"
|
||||
width="100%"
|
||||
height="55">
|
||||
<path
|
||||
d="M8960 0c-451.52 181.184-171.2 1024-992 1024H992C171.232 1024 451.392 181.184 0 0h8960z"
|
||||
fill="#ffffff"
|
||||
p-id="9635"></path>
|
||||
</svg>
|
||||
<h2 className="flex items-center gap-3 text-xl font-bold text-zinc-700 dark:text-zinc-300 mr-3 absolute left-1/2 transform -translate-x-1/2">
|
||||
{!hideLogo && <img src={logo} alt="logo" className="w-8" />}
|
||||
<p>
|
||||
<span className="text-[#d30100]">数联网</span>科创智能体
|
||||
</p>
|
||||
<h2 className="text-xl font-bold text-zinc-700 dark:text-zinc-300 mr-3">
|
||||
<span className="text-[#d30100]">数联网</span>科创智能体
|
||||
</h2>
|
||||
</div>
|
||||
{/*设置框*/}
|
||||
<div className="flex items-center gap-1 ml-auto pr-5">
|
||||
<Tooltip title="收藏">
|
||||
{collect ? (
|
||||
<Button
|
||||
color="default"
|
||||
variant="text"
|
||||
className="!px-[5px]"
|
||||
onClick={() => setCollect(false)}>
|
||||
<CollectIcon className="!text-gray-500 dark:text-gray-400 hover:text-gray-600 dark:hover:text-gray-300 transition-colors w-5 h-5 cursor-pointer" />
|
||||
</Button>
|
||||
) : (
|
||||
<Button
|
||||
color="default"
|
||||
variant="text"
|
||||
className="!px-[5px]"
|
||||
onClick={() => setCollect(true)}>
|
||||
<NotCollectIcon className="!text-gray-500 dark:text-gray-400 hover:text-gray-600 dark:hover:text-gray-300 transition-colors w-5 h-5 cursor-pointer" />
|
||||
</Button>
|
||||
)}
|
||||
</Tooltip>
|
||||
<Tooltip title="分享">
|
||||
<Button color="default" variant="text" className="!px-[5px]">
|
||||
<ShareIcon className="!text-gray-500 dark:text-gray-400 hover:text-gray-600 dark:hover:text-gray-300 transition-colors w-5 h-5 cursor-pointer" />
|
||||
</Button>
|
||||
</Tooltip>
|
||||
<Tooltip title="消息">
|
||||
<Button color="default" variant="text" className="!px-[5px]">
|
||||
<BellIcon className="!text-gray-500 dark:text-gray-400 hover:text-gray-600 dark:hover:text-gray-300 transition-colors w-5 h-5 cursor-pointer" />
|
||||
</Button>
|
||||
</Tooltip>
|
||||
|
||||
<Tooltip title={t("settings")}>
|
||||
<NavLink to="/settings">
|
||||
<SettingIcon className="!text-gray-500 dark:text-gray-400 hover:text-gray-600 dark:hover:text-gray-300 transition-colors w-5 h-5 cursor-pointer" />
|
||||
</NavLink>
|
||||
</Tooltip>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
@ -1,86 +1,47 @@
|
||||
import React, { useContext, useState } from "react"
|
||||
import React, { useCallback, useEffect, useState } from "react"
|
||||
|
||||
import { CurrentChatModelSettings } from "../Common/Settings/CurrentChatModelSettings"
|
||||
import { Header } from "./Header.tsx"
|
||||
import IodVideo from "@/components/Option/VideoPlayer"
|
||||
|
||||
interface OptionLayoutContextType {
|
||||
showOptionSidebar: boolean
|
||||
setShowOptionSidebar: (show: boolean) => void
|
||||
showVideo: boolean
|
||||
setShowVideo: (show: boolean) => void
|
||||
interface History {
|
||||
show: boolean
|
||||
setShow: (show: boolean) => void
|
||||
}
|
||||
|
||||
const OptionLayoutContext = React.createContext<OptionLayoutContextType>({
|
||||
showOptionSidebar: true,
|
||||
setShowOptionSidebar: () => {},
|
||||
showVideo: true,
|
||||
setShowVideo: () => {}
|
||||
export const HistoryContext = React.createContext<History>({
|
||||
show: true,
|
||||
setShow: () => {}
|
||||
})
|
||||
|
||||
// 创建自定义 hook 以便子组件使用
|
||||
export const useOptionLayoutContext = () => {
|
||||
const context = useContext(OptionLayoutContext)
|
||||
if (context === undefined) {
|
||||
throw new Error(
|
||||
"useOptionLayoutContext must be used within a OptionLayoutProvider"
|
||||
)
|
||||
}
|
||||
return context
|
||||
}
|
||||
|
||||
const OptionLayoutProvider = ({ children }: { children: React.ReactNode }) => {
|
||||
const [showHistory, setShowHistory] = useState(true)
|
||||
|
||||
const [showVideo, setShowVideo] = useState<boolean>(false)
|
||||
|
||||
return (
|
||||
<OptionLayoutContext.Provider
|
||||
value={{
|
||||
showOptionSidebar: showHistory,
|
||||
setShowOptionSidebar: setShowHistory,
|
||||
showVideo,
|
||||
setShowVideo
|
||||
}}>
|
||||
{children}
|
||||
</OptionLayoutContext.Provider>
|
||||
)
|
||||
}
|
||||
|
||||
const OptionLayoutMain: React.FC<{ children: React.ReactNode }> = ({
|
||||
children
|
||||
}) => {
|
||||
const { showVideo } = useOptionLayoutContext()
|
||||
|
||||
if (showVideo) {
|
||||
return <IodVideo />
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<Header />
|
||||
{children}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default function OptionLayout({
|
||||
children
|
||||
}: {
|
||||
children: React.ReactNode
|
||||
}) {
|
||||
const [showHistory, setShowHistory] = useState(true)
|
||||
const [openModelSettings, setOpenModelSettings] = useState(false)
|
||||
|
||||
const historyContextValue = {
|
||||
show: showHistory,
|
||||
setShow: setShowHistory
|
||||
}
|
||||
|
||||
const useToggle = useCallback(() => {
|
||||
setShowHistory(!showHistory)
|
||||
}, [showHistory])
|
||||
|
||||
return (
|
||||
<div className="flex h-full w-full">
|
||||
<main className="relative h-dvh w-full">
|
||||
{/*<div className="relative z-10 w-full">*/}
|
||||
{/*</div>*/}
|
||||
{/* <div className="relative flex h-full flex-col items-center"> */}
|
||||
<OptionLayoutProvider>
|
||||
<OptionLayoutMain>{children}</OptionLayoutMain>
|
||||
</OptionLayoutProvider>
|
||||
<HistoryContext.Provider value={historyContextValue}>
|
||||
<Header />
|
||||
{children}
|
||||
</HistoryContext.Provider>
|
||||
{/* </div> */}
|
||||
|
||||
<CurrentChatModelSettings
|
||||
open={openModelSettings}
|
||||
setOpen={setOpenModelSettings}
|
||||
|
@ -1,18 +1,17 @@
|
||||
import {
|
||||
BlocksIcon,
|
||||
BookIcon,
|
||||
BrainCircuitIcon,
|
||||
ChromeIcon,
|
||||
CombineIcon,
|
||||
CpuIcon,
|
||||
InfoIcon,
|
||||
OrbitIcon,
|
||||
ShareIcon
|
||||
ShareIcon,
|
||||
BlocksIcon,
|
||||
InfoIcon,
|
||||
CombineIcon,
|
||||
ChromeIcon,
|
||||
CpuIcon
|
||||
} from "lucide-react"
|
||||
import { useTranslation } from "react-i18next"
|
||||
import { Link, useLocation } from "react-router-dom"
|
||||
import { OllamaIcon } from "../Icons/Ollama"
|
||||
import { IodIcon } from "../Icons/Iod.tsx"
|
||||
import { BetaTag } from "../Common/Beta"
|
||||
|
||||
function classNames(...classes: string[]) {
|
||||
@ -83,12 +82,6 @@ export const SettingsLayout = ({ children }: { children: React.ReactNode }) => {
|
||||
icon={OllamaIcon}
|
||||
current={location.pathname}
|
||||
/>
|
||||
<LinkComponent
|
||||
href="/settings/iod"
|
||||
name={t("iodSettings.title")}
|
||||
icon={IodIcon}
|
||||
current={location.pathname}
|
||||
/>
|
||||
{import.meta.env.BROWSER === "chrome" && (
|
||||
<LinkComponent
|
||||
href="/settings/chrome"
|
||||
|
@ -1,10 +1,13 @@
|
||||
import React from "react"
|
||||
import React, { useContext } from "react"
|
||||
|
||||
import { Card } from "antd"
|
||||
|
||||
import { PlaygroundForm } from "./PlaygroundForm"
|
||||
import { PlaygroundChat } from "./PlaygroundChat"
|
||||
import { PlaygroundSidebar } from "./PlaygroundSidebar.tsx"
|
||||
import { useMessageOption } from "@/hooks/useMessageOption"
|
||||
import { webUIResumeLastChat } from "@/services/app"
|
||||
import { PlaygroundData } from '@/components/Common/Playground/Data.tsx'
|
||||
import { PlaygroundScene } from "@/components/Common/Playground/Scene.tsx"
|
||||
|
||||
import {
|
||||
formatToChatHistory,
|
||||
@ -16,7 +19,10 @@ import { getLastUsedChatSystemPrompt } from "@/services/model-settings"
|
||||
import { useStoreChatModelSettings } from "@/store/model"
|
||||
import { useSmartScroll } from "@/hooks/useSmartScroll"
|
||||
import { ChevronDown } from "lucide-react"
|
||||
import { PlaygroundIod } from "@/components/Option/Playground/PlaygroundIod.tsx"
|
||||
import { PlaygroundTeam } from "@/components/Common/Playground/Team.tsx"
|
||||
import { PlaygroundHistory } from "@/components/Common/Playground/History.tsx"
|
||||
import { PlaygroundIodRelevant } from "@/components/Common/Playground/IodRelevant.tsx"
|
||||
|
||||
|
||||
export const Playground = () => {
|
||||
const drop = React.useRef<HTMLDivElement>(null)
|
||||
@ -139,15 +145,14 @@ export const Playground = () => {
|
||||
className={`relative flex gap-3 h-full items-center ${
|
||||
dropState === "dragging" ? "bg-gray-100 dark:bg-gray-800" : ""
|
||||
} bg-white dark:bg-[#171717]`}>
|
||||
<PlaygroundSidebar />
|
||||
<div className="h-full flex-1 overflow-x-hidden prose-lg flex flex-col items-center [&>*]:max-w-[848px] pt-[60px]">
|
||||
<PlaygroundHistory />
|
||||
<div className="relative h-full flex-1 prose-lg flex flex-col items-center [&>*]:max-w-[848px] pt-[60px]">
|
||||
<div
|
||||
ref={containerRef}
|
||||
className="custom-scrollbar flex h-auto w-full flex-col items-center px-5">
|
||||
className="custom-scrollbar flex h-auto w-full flex-col items-center overflow-x-hidden overflow-y-auto px-5">
|
||||
<PlaygroundChat />
|
||||
</div>
|
||||
<div
|
||||
className={`${messages.length ? "absolute" : "relative"} bottom-0 w-full`}>
|
||||
<div className="relative bottom-0 w-full">
|
||||
{!isAtBottom && (
|
||||
<div className="absolute bottom-36 z-20 left-0 right-0 flex justify-center">
|
||||
<button
|
||||
@ -160,7 +165,23 @@ export const Playground = () => {
|
||||
<PlaygroundForm dropedFile={dropedFile} />
|
||||
</div>
|
||||
</div>
|
||||
<PlaygroundIod />
|
||||
{/*auto_530px_165px*/}
|
||||
{messages.length && (
|
||||
<div
|
||||
className="w-4/12 h-full grid grid-rows-10 gap-3 pt-16 pr-5 pb-0"
|
||||
style={{ paddingTop: "4rem" }}>
|
||||
<div className="w-full row-span-4">
|
||||
<PlaygroundIodRelevant />
|
||||
</div>
|
||||
<div className="w-full row-span-4 grid grid-cols-2 gap-3 custom-scrollbar">
|
||||
<PlaygroundData />
|
||||
<PlaygroundScene />
|
||||
</div>
|
||||
<div className="w-full row-span-2 pb-3">
|
||||
<PlaygroundTeam />
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
@ -11,8 +11,7 @@ export const PlaygroundChat = () => {
|
||||
regenerateLastMessage,
|
||||
isSearchingInternet,
|
||||
editMessage,
|
||||
ttsEnabled,
|
||||
setCurrentMessageId,
|
||||
ttsEnabled
|
||||
} = useMessageOption()
|
||||
const [isSourceOpen, setIsSourceOpen] = React.useState(false)
|
||||
const [source, setSource] = React.useState<any>(null)
|
||||
@ -28,7 +27,6 @@ export const PlaygroundChat = () => {
|
||||
{messages.map((message, index) => (
|
||||
<PlaygroundMessage
|
||||
key={index}
|
||||
id={message.id}
|
||||
isBot={message.isBot}
|
||||
message={message.message}
|
||||
name={message.name}
|
||||
@ -51,12 +49,10 @@ export const PlaygroundChat = () => {
|
||||
generationInfo={message?.generationInfo}
|
||||
isStreaming={streaming}
|
||||
reasoningTimeTaken={message?.reasoning_time_taken}
|
||||
setCurrentMessageId={setCurrentMessageId}
|
||||
iodSearch={message.iodSearch}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
{messages.length !== 0 && <div className="w-full pb-[157px]"></div>}
|
||||
{/*<div className="w-full pb-[0px]"></div>*/}
|
||||
<MessageSourcePopup
|
||||
open={isSourceOpen}
|
||||
setOpen={setIsSourceOpen}
|
||||
|
@ -1,3 +1,5 @@
|
||||
import { Card, Col, Row } from "antd"
|
||||
|
||||
import { useMessageOption } from "@/hooks/useMessageOption.tsx"
|
||||
import { useMutation, useQueryClient } from "@tanstack/react-query"
|
||||
import { qaPrompt } from "@/libs/playground.tsx"
|
||||
@ -21,20 +23,36 @@ export const PlaygroundEmpty = () => {
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="w-full pb-4 pt-[20%] grid grid-cols-3 gap-3">
|
||||
{qaPrompt.map((item, index) => (
|
||||
<div
|
||||
key={item.id}
|
||||
className="p-6 bg-gradient-to-br from-blue-50/90 via-indigo-50/90 to-purple-50/90 backdrop-blur-xl border border-white/60 shadow-xl rounded-2xl cursor-pointer hover:shadow-blue-200/40 hover:from-blue-100/90 hover:to-indigo-100/90 transition-all duration-500 hover:-translate-y-1"
|
||||
onClick={() => handleQuestion(item.title)}>
|
||||
<div className="flex items-center">
|
||||
<div className="text-blue-500 mr-2 w-10">{item.icon}</div>
|
||||
<div className="text-sm text-gray-800">
|
||||
{item.title}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
<div className="w-full p-4">
|
||||
{/* 标题区域 */}
|
||||
<div className="mb-4">
|
||||
<h2
|
||||
className="text-xl font-bold text-gray-800"
|
||||
style={{ lineHeight: "0" }}>
|
||||
数联网科创智能体
|
||||
</h2>
|
||||
<p className="text-sm text-gray-500">您好!请问有什么可以帮您?</p>
|
||||
</div>
|
||||
|
||||
{/* 卡片网格布局 */}
|
||||
<Row gutter={[16, 16]} className="w-full">
|
||||
{qaPrompt.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 w-10">{item.icon}</div>
|
||||
<div className="font-medium text-sm text-gray-800">
|
||||
{item.title}
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
</Col>
|
||||
))}
|
||||
</Row>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
@ -1,24 +1,17 @@
|
||||
import { useForm } from "@mantine/form"
|
||||
import { useMutation, useQueryClient } from "@tanstack/react-query"
|
||||
import React, { useMemo } from "react"
|
||||
import React from "react"
|
||||
import useDynamicTextareaSize from "~/hooks/useDynamicTextareaSize"
|
||||
import { toBase64 } from "~/libs/to-base64"
|
||||
import { useMessageOption } from "~/hooks/useMessageOption"
|
||||
import {
|
||||
Button,
|
||||
Checkbox,
|
||||
Dropdown,
|
||||
Image,
|
||||
MenuProps,
|
||||
Switch,
|
||||
Tooltip
|
||||
} from "antd"
|
||||
import { Checkbox, Dropdown, Switch, Tooltip } from "antd"
|
||||
import { Image } from "antd"
|
||||
import { useWebUI } from "~/store/webui"
|
||||
import { defaultEmbeddingModelForRag } from "~/services/ollama"
|
||||
import { ImageIcon, MicIcon, StopCircleIcon, X } from "lucide-react"
|
||||
import { getVariable } from "@/utils/select-variable"
|
||||
import { useTranslation } from "react-i18next"
|
||||
// import { KnowledgeSelect } from "../Knowledge/KnowledgeSelect"
|
||||
import { KnowledgeSelect } from "../Knowledge/KnowledgeSelect"
|
||||
import { useSpeechRecognition } from "@/hooks/useSpeechRecognition"
|
||||
import { PiGlobe, PiNetwork } from "react-icons/pi"
|
||||
import { handleChatInputKeyDown } from "@/utils/key-down"
|
||||
@ -213,47 +206,14 @@ export const PlaygroundForm = ({ dropedFile }: Props) => {
|
||||
}
|
||||
}
|
||||
|
||||
const iodSearchItems = useMemo<MenuProps["items"]>(() => {
|
||||
return [
|
||||
{
|
||||
key: 0,
|
||||
label: (
|
||||
<div
|
||||
onClick={() => {
|
||||
setIodSearch(true)
|
||||
}}>
|
||||
<p
|
||||
className={`${iodSearch ? "text-[#0057ff]" : "text-[#000000d9]"} flex items-center gap-1 mb-1`}>
|
||||
<PiNetwork className="h-5 w-5" />开
|
||||
</p>
|
||||
<p className="text-[#00000080]">输出带数联网的回答</p>
|
||||
</div>
|
||||
)
|
||||
},
|
||||
{
|
||||
key: 1,
|
||||
label: (
|
||||
<div
|
||||
onClick={() => {
|
||||
setIodSearch(false)
|
||||
}}>
|
||||
<p
|
||||
className={`${!iodSearch ? "text-[#0057ff]" : "text-[#000000d9]"} flex items-center gap-1 mb-1`}>
|
||||
<PiNetwork className="h-5 w-5" /> 关闭
|
||||
</p>
|
||||
<p className="text-[#00000080]">快速直接回答</p>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
]
|
||||
}, [iodSearch])
|
||||
|
||||
return (
|
||||
<div className="flex w-full flex-col items-center pt-1 px-5 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 flex w-full flex-row justify-center gap-2 lg:w-5/5">
|
||||
<div
|
||||
className={`shadow-xl relative w-full max-w-[65rem] p-1 rounded-xl bg-gradient-to-br from-white/90 via-blue-50/90 to-cyan-50/90 backdrop-blur-lg border border-blue-100/70 cursor-pointer hover:shadow-blue-100/60 transition-all duration-500`}>
|
||||
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 " : ""}
|
||||
`}>
|
||||
<div
|
||||
className={`border-b border-gray-200 dark:border-gray-600 relative ${
|
||||
form.values.image.length === 0 ? "hidden" : "block"
|
||||
@ -274,7 +234,8 @@ export const PlaygroundForm = ({ dropedFile }: Props) => {
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<div className={`flex bg-transparent `}>
|
||||
<div
|
||||
className={`flex bg-transparent `}>
|
||||
<form
|
||||
onSubmit={form.onSubmit(async (value) => {
|
||||
stopListening()
|
||||
@ -343,74 +304,60 @@ export const PlaygroundForm = ({ dropedFile }: Props) => {
|
||||
<div className="flex">
|
||||
{!selectedKnowledge && (
|
||||
<div>
|
||||
{/* 展示隐藏深度搜索*/}
|
||||
<Tooltip
|
||||
title={t("tooltip.searchInternet")}
|
||||
className="hidden">
|
||||
<div className="inline-flex items-center gap-2">
|
||||
<PiGlobe
|
||||
className={`h-5 w-5 dark:text-gray-300 `}
|
||||
/>
|
||||
<Switch
|
||||
value={webSearch}
|
||||
onChange={(e) => setWebSearch(e)}
|
||||
checkedChildren={t("form.webSearch.on")}
|
||||
unCheckedChildren={t("form.webSearch.off")}
|
||||
/>
|
||||
</div>
|
||||
</Tooltip>
|
||||
<Dropdown
|
||||
menu={{ items: iodSearchItems }}
|
||||
placement="bottom"
|
||||
trigger={["click"]}
|
||||
arrow>
|
||||
<Button
|
||||
color="default"
|
||||
variant="filled"
|
||||
size="large"
|
||||
className="w-full mt-4 hover:!bg-[#0057ff1a]"
|
||||
style={
|
||||
iodSearch
|
||||
? {
|
||||
color: "#0057ff",
|
||||
background: "#0057ff0f",
|
||||
border: "1px solid #0066ff26"
|
||||
}
|
||||
: {}
|
||||
}>
|
||||
<PiNetwork className="h-5 w-5" />
|
||||
数联网深度搜索{iodSearch ? ":开" : ""}
|
||||
</Button>
|
||||
</Dropdown>
|
||||
</div>
|
||||
<Tooltip title={t("tooltip.searchInternet")}>
|
||||
<div className="inline-flex items-center gap-2">
|
||||
<PiGlobe
|
||||
className={`h-5 w-5 dark:text-gray-300 `}
|
||||
/>
|
||||
<Switch
|
||||
value={webSearch}
|
||||
onChange={(e) => setWebSearch(e)}
|
||||
checkedChildren={t("form.webSearch.on")}
|
||||
unCheckedChildren={t("form.webSearch.off")}
|
||||
/>
|
||||
</div>
|
||||
</Tooltip>
|
||||
<Tooltip title={t("tooltip.searchIod")} className="ml-3">
|
||||
<div className="inline-flex items-center gap-2">
|
||||
<PiNetwork
|
||||
className={`h-5 w-5 dark:text-gray-300 `}
|
||||
/>
|
||||
<Switch
|
||||
value={iodSearch}
|
||||
onChange={(e) => setIodSearch(e)}
|
||||
checkedChildren={t("form.webSearch.on")}
|
||||
unCheckedChildren={t("form.webSearch.off")}
|
||||
/>
|
||||
</div>
|
||||
</Tooltip>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div className="flex !justify-end gap-1">
|
||||
<div className="flex !justify-end gap-3">
|
||||
{!selectedKnowledge && (
|
||||
<Tooltip title={t("tooltip.uploadImage")}>
|
||||
<Button
|
||||
color="default"
|
||||
variant="text"
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => {
|
||||
inputRef.current?.click()
|
||||
}}
|
||||
className={`!px-[5px] flex items-center justify-center dark:text-gray-300 ${
|
||||
className={`flex items-center justify-center dark:text-gray-300 ${
|
||||
chatMode === "rag" ? "hidden" : "block"
|
||||
}`}>
|
||||
<ImageIcon strokeWidth={1} className="h-5 w-5" />
|
||||
</Button>
|
||||
<ImageIcon className="h-5 w-5" />
|
||||
</button>
|
||||
</Tooltip>
|
||||
)}
|
||||
|
||||
{browserSupportsSpeechRecognition && (
|
||||
<Tooltip title={t("tooltip.speechToText")}>
|
||||
<Button
|
||||
color="default"
|
||||
variant="text"
|
||||
<button
|
||||
type="button"
|
||||
onClick={async () => {
|
||||
if (isListening) {
|
||||
stopSpeechRecognition()
|
||||
} else {
|
||||
console.log("开始语音识别,语言:", speechToTextLanguage);
|
||||
resetTranscript()
|
||||
startListening({
|
||||
continuous: true,
|
||||
@ -418,43 +365,40 @@ export const PlaygroundForm = ({ dropedFile }: Props) => {
|
||||
})
|
||||
}
|
||||
}}
|
||||
className={`flex items-center justify-center dark:text-gray-300 !px-[5px]`}>
|
||||
className={`flex items-center justify-center dark:text-gray-300`}>
|
||||
{!isListening ? (
|
||||
<MicIcon strokeWidth={1} className="h-5 w-5" />
|
||||
<MicIcon className="h-5 w-5" />
|
||||
) : (
|
||||
<div className="relative">
|
||||
<span className="animate-ping absolute inline-flex h-3 w-3 rounded-full bg-red-400 opacity-75"></span>
|
||||
<MicIcon
|
||||
strokeWidth={1}
|
||||
className="h-5 w-5"
|
||||
/>
|
||||
<MicIcon className="h-5 w-5" />
|
||||
</div>
|
||||
)}
|
||||
</Button>
|
||||
</button>
|
||||
</Tooltip>
|
||||
)}
|
||||
{/*<KnowledgeSelect />*/}
|
||||
<KnowledgeSelect />
|
||||
|
||||
{!isSending ? (
|
||||
<Dropdown.Button
|
||||
type="default"
|
||||
htmlType="submit"
|
||||
disabled={isSending}
|
||||
// icon={
|
||||
// <svg
|
||||
// xmlns="http://www.w3.org/2000/svg"
|
||||
// fill="none"
|
||||
// viewBox="0 0 24 24"
|
||||
// strokeWidth={1.5}
|
||||
// stroke="currentColor"
|
||||
// className="w-5 h-5">
|
||||
// <path
|
||||
// strokeLinecap="round"
|
||||
// strokeLinejoin="round"
|
||||
// d="m19.5 8.25-7.5 7.5-7.5-7.5"
|
||||
// />
|
||||
// </svg>
|
||||
// }
|
||||
className="!justify-end !w-auto"
|
||||
icon={
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
strokeWidth={1.5}
|
||||
stroke="currentColor"
|
||||
className="w-5 h-5">
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
d="m19.5 8.25-7.5 7.5-7.5-7.5"
|
||||
/>
|
||||
</svg>
|
||||
}
|
||||
menu={{
|
||||
items: [
|
||||
{
|
||||
@ -484,6 +428,20 @@ export const PlaygroundForm = ({ dropedFile }: Props) => {
|
||||
]
|
||||
}}>
|
||||
<div className="inline-flex gap-2">
|
||||
{sendWhenEnter ? (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth="2"
|
||||
className="h-5 w-5"
|
||||
viewBox="0 0 24 24">
|
||||
<path d="M9 10L4 15 9 20"></path>
|
||||
<path d="M20 4v7a4 4 0 01-4 4H4"></path>
|
||||
</svg>
|
||||
) : null}
|
||||
{t("common:submit")}
|
||||
</div>
|
||||
</Dropdown.Button>
|
||||
|
@ -1,160 +0,0 @@
|
||||
import React, { createContext, useContext, useMemo, useState } from "react"
|
||||
|
||||
import { AnimatePresence, motion } from "framer-motion"
|
||||
|
||||
import { PlaygroundIodRelevant } from "@/components/Common/Playground/IodRelevant.tsx"
|
||||
import { PlaygroundData } from "@/components/Common/Playground/Data.tsx"
|
||||
import { PlaygroundScene } from "@/components/Common/Playground/Scene.tsx"
|
||||
import { PlaygroundTeam } from "@/components/Common/Playground/Team.tsx"
|
||||
import { Card } from "antd"
|
||||
import { CloseOutlined } from "@ant-design/icons"
|
||||
import { useMessageOption } from "@/hooks/useMessageOption.tsx"
|
||||
import { AllIodRegistryEntry } from "@/types/iod.ts"
|
||||
|
||||
// 定义 Context 类型
|
||||
interface IodPlaygroundContextType {
|
||||
showPlayground: boolean
|
||||
setShowPlayground: React.Dispatch<React.SetStateAction<boolean>>
|
||||
detailHeader: React.ReactNode
|
||||
setDetailHeader: React.Dispatch<React.SetStateAction<React.ReactNode>>
|
||||
detailMain: React.ReactNode
|
||||
setDetailMain: React.Dispatch<React.SetStateAction<React.ReactNode>>
|
||||
currentIodMessage?: AllIodRegistryEntry
|
||||
}
|
||||
|
||||
// 创建 Context
|
||||
const PlaygroundContext = createContext<IodPlaygroundContextType | undefined>(
|
||||
undefined
|
||||
)
|
||||
|
||||
// 创建自定义 hook 以便子组件使用
|
||||
export const useIodPlaygroundContext = () => {
|
||||
const context = useContext(PlaygroundContext)
|
||||
if (context === undefined) {
|
||||
throw new Error(
|
||||
"usePlaygroundContext must be used within a PlaygroundProvider"
|
||||
)
|
||||
}
|
||||
return context
|
||||
}
|
||||
|
||||
const PlaygroundIodProvider: React.FC<{ children: React.ReactNode }> = ({
|
||||
children
|
||||
}) => {
|
||||
const { messages, iodLoading, currentMessageId } = useMessageOption()
|
||||
|
||||
const [showPlayground, setShowPlayground] = useState<boolean>(true)
|
||||
const [detailHeader, setDetailHeader] = useState(<></>)
|
||||
const [detailMain, setDetailMain] = useState(<></>)
|
||||
|
||||
const currentIodMessage = useMemo<AllIodRegistryEntry | undefined>(() => {
|
||||
// loading 返回 undefined是为了避免,数据不足三个的情况
|
||||
if (iodLoading || !messages.length) {
|
||||
return undefined
|
||||
}
|
||||
console.log(messages)
|
||||
console.log(currentMessageId)
|
||||
// 如果不存在currentMessageId默认返回最后一个message
|
||||
if (!currentMessageId) {
|
||||
const lastMessage = messages.at(-1)
|
||||
// 如果最后一次message没有开启数联网搜索,则返回undefined
|
||||
return lastMessage?.iodSearch ? lastMessage.iodSources : undefined
|
||||
}
|
||||
|
||||
const currentMessage = messages?.find(
|
||||
(message) => message.id === currentMessageId
|
||||
)
|
||||
return currentMessage?.iodSearch ? currentMessage.iodSources : undefined
|
||||
}, [currentMessageId, messages, iodLoading])
|
||||
|
||||
return (
|
||||
<PlaygroundContext.Provider
|
||||
value={{
|
||||
currentIodMessage,
|
||||
showPlayground,
|
||||
setShowPlayground,
|
||||
detailMain,
|
||||
setDetailMain,
|
||||
detailHeader,
|
||||
setDetailHeader
|
||||
}}>
|
||||
{children}
|
||||
</PlaygroundContext.Provider>
|
||||
)
|
||||
}
|
||||
|
||||
// 子组件使用修改card的默认样式
|
||||
const classNames =
|
||||
"h-full [&_.ant-card-body]:h-full [&_.ant-card-body]:!p-[20px] overflow-y-hidden !bg-[rgba(240,245,255,0.3)] backdrop-blur-sm border border-white/30 shadow-xl rounded-2xl"
|
||||
// 将原来的返回内容移到这个组件中
|
||||
const PlaygroundContent = () => {
|
||||
const { showPlayground, detailMain, detailHeader, setShowPlayground } =
|
||||
useIodPlaygroundContext()
|
||||
|
||||
return (
|
||||
<AnimatePresence mode="popLayout">
|
||||
{showPlayground ? (
|
||||
<motion.div
|
||||
key="playground"
|
||||
initial={{ x: "100%" }}
|
||||
animate={{ x: 0 }}
|
||||
exit={{ x: "100%" }}
|
||||
transition={{
|
||||
duration: 0.6,
|
||||
ease: "easeInOut"
|
||||
}}
|
||||
className="h-full grid grid-rows-12 gap-3">
|
||||
<div className="w-full row-span-5">
|
||||
<PlaygroundIodRelevant
|
||||
className={classNames
|
||||
.replace("!bg-[rgba(240,245,255,0.3)]", "")
|
||||
.replace("shadow-xl", "")}
|
||||
/>
|
||||
</div>
|
||||
<div className="w-full row-span-4 grid grid-cols-2 gap-3 custom-scrollbar">
|
||||
<PlaygroundData className={classNames} />
|
||||
<PlaygroundScene className={classNames} />
|
||||
</div>
|
||||
<div className="w-full row-span-3 pb-3">
|
||||
<PlaygroundTeam className={classNames} />
|
||||
</div>
|
||||
</motion.div>
|
||||
) : (
|
||||
<motion.div
|
||||
key="alternative"
|
||||
initial={{ x: "100%" }}
|
||||
animate={{ x: 0 }}
|
||||
exit={{ x: "100%" }}
|
||||
transition={{
|
||||
duration: 0.6,
|
||||
ease: "easeInOut"
|
||||
}}
|
||||
className="h-full pb-5">
|
||||
<Card className="h-full shadow-xl shadow-gray-500/20 [&_.ant-card-body]:h-full">
|
||||
<div className="flex flex-col h-full">
|
||||
<div className="pb-6 flex items-center justify-between">
|
||||
<div>{detailHeader}</div>
|
||||
<CloseOutlined
|
||||
size={30}
|
||||
className="hover:text-red-500 cursor-pointer transition-colors duration-200 text-xl"
|
||||
onClick={() => setShowPlayground(true)}
|
||||
/>
|
||||
</div>
|
||||
{detailMain}
|
||||
</div>
|
||||
</Card>
|
||||
</motion.div>
|
||||
)}
|
||||
</AnimatePresence>
|
||||
)
|
||||
}
|
||||
|
||||
export const PlaygroundIod = () => {
|
||||
return (
|
||||
<div className="w-[36%] h-full pt-16 pr-5 pb-0">
|
||||
<PlaygroundIodProvider>
|
||||
<PlaygroundContent />
|
||||
</PlaygroundIodProvider>
|
||||
</div>
|
||||
)
|
||||
}
|
25
src/components/Option/Playground/PlaygroundNewChat.tsx
Normal file
@ -0,0 +1,25 @@
|
||||
import { PencilIcon } from "lucide-react"
|
||||
import { useMessage } from "../../../hooks/useMessage"
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
export const PlaygroundNewChat = () => {
|
||||
const { setHistory, setMessages, setHistoryId } = useMessage()
|
||||
const { t } = useTranslation('optionChat')
|
||||
|
||||
const handleClick = () => {
|
||||
setHistoryId(null)
|
||||
setMessages([])
|
||||
setHistory([])
|
||||
}
|
||||
|
||||
return (
|
||||
<button
|
||||
onClick={handleClick}
|
||||
className="flex w-full border bg-transparent hover:bg-gray-200 dark:hover:bg-gray-800 text-gray-600 dark:text-gray-400 hover:text-gray-800 dark:hover:text-gray-200 focus:outline-none focus:ring-2 focus:ring-gray-500 focus:ring-offset-2 focus:ring-offset-gray-100 rounded-md p-2 dark:border-gray-800">
|
||||
<PencilIcon className="mx-3 h-5 w-5" aria-hidden="true" />
|
||||
<span className="inline-flex font-semibol text-white text-sm">
|
||||
{t('newChat')}
|
||||
</span>
|
||||
</button>
|
||||
)
|
||||
}
|
@ -1,33 +0,0 @@
|
||||
import { useTranslation } from "react-i18next"
|
||||
import TextArea from "antd/es/input/TextArea"
|
||||
import { IodDb } from "@/db/iod.ts"
|
||||
import { useState } from "react"
|
||||
|
||||
|
||||
export const IodApp = () => {
|
||||
const { t } = useTranslation("settings")
|
||||
|
||||
const db = IodDb.getInstance()
|
||||
|
||||
const [connection, setConnection] = useState(JSON.stringify(db.getIodConnection(), null, 2))
|
||||
|
||||
const setConnectValWrap = (val: string) => {
|
||||
db.insertIodConnection(JSON.parse(val))
|
||||
setConnection(val)
|
||||
}
|
||||
|
||||
return (
|
||||
<dl className="flex flex-col space-y-6 text-sm">
|
||||
<div>
|
||||
<h2 className="text-base font-semibold leading-7 text-gray-900 dark:text-white">
|
||||
{t("iodSettings.heading")}
|
||||
</h2>
|
||||
<div className="border border-b border-gray-200 dark:border-gray-600 mt-3"></div>
|
||||
</div>
|
||||
<div className="flex flex-col gap-3">
|
||||
<span className="text-gray-700 dark:text-neutral-50">连接配置</span>
|
||||
<TextArea rows={6} placeholder="请输入数联网连接配置" value={connection} onChange={(e) => setConnectValWrap(e.target.value)} />
|
||||
</div>
|
||||
</dl>
|
||||
)
|
||||
}
|
@ -1,21 +1,21 @@
|
||||
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"
|
||||
import {
|
||||
deleteByHistoryId,
|
||||
PageAssitDatabase,
|
||||
formatToChatHistory,
|
||||
formatToMessage,
|
||||
getPromptById,
|
||||
PageAssitDatabase,
|
||||
deleteByHistoryId,
|
||||
updateHistory,
|
||||
pinHistory,
|
||||
updateHistory
|
||||
getPromptById
|
||||
} from "@/db"
|
||||
import { Dropdown, Empty, Menu, Skeleton, Tooltip } from "antd"
|
||||
import { Empty, Skeleton, Dropdown, Menu, Tooltip } from "antd"
|
||||
import {
|
||||
BotIcon,
|
||||
MoreVertical,
|
||||
PencilIcon,
|
||||
Trash2,
|
||||
MoreVertical,
|
||||
PinIcon,
|
||||
PinOffIcon,
|
||||
Trash2
|
||||
BotIcon
|
||||
} from "lucide-react"
|
||||
import { useNavigate } from "react-router-dom"
|
||||
import { useTranslation } from "react-i18next"
|
||||
@ -24,7 +24,7 @@ import {
|
||||
getLastUsedChatSystemPrompt,
|
||||
lastUsedChatModelEnabled
|
||||
} from "@/services/model-settings"
|
||||
import { useMessageOption } from "@/hooks/useMessageOption.tsx"
|
||||
import { useState } from "react"
|
||||
|
||||
type Props = {
|
||||
onClose: () => void
|
||||
@ -34,7 +34,6 @@ type Props = {
|
||||
setSelectedModel: (model: string) => void
|
||||
setSelectedSystemPrompt: (prompt: string) => void
|
||||
setSystemPrompt: (prompt: string) => void
|
||||
stopStreamingRequest: () => void
|
||||
clearChat: () => void
|
||||
temporaryChat: boolean
|
||||
historyId: string
|
||||
@ -48,7 +47,6 @@ export const Sidebar = ({
|
||||
setHistoryId,
|
||||
setSelectedModel,
|
||||
setSelectedSystemPrompt,
|
||||
stopStreamingRequest,
|
||||
clearChat,
|
||||
historyId,
|
||||
setSystemPrompt,
|
||||
@ -58,8 +56,6 @@ export const Sidebar = ({
|
||||
const client = useQueryClient()
|
||||
const navigate = useNavigate()
|
||||
|
||||
const { setCurrentMessageId } = useMessageOption()
|
||||
|
||||
const { data: chatHistories, status } = useQuery({
|
||||
queryKey: ["fetchChatHistory"],
|
||||
queryFn: async () => {
|
||||
@ -145,41 +141,6 @@ export const Sidebar = ({
|
||||
}
|
||||
})
|
||||
|
||||
const handleHistoryClick = async (chat: any) => {
|
||||
const db = new PageAssitDatabase()
|
||||
const history = await db.getChatHistory(chat.id)
|
||||
setHistoryId(chat.id)
|
||||
setCurrentMessageId("")
|
||||
setHistory(formatToChatHistory(history))
|
||||
setMessages(formatToMessage(history))
|
||||
stopStreamingRequest()
|
||||
const isLastUsedChatModel =
|
||||
await lastUsedChatModelEnabled()
|
||||
if (isLastUsedChatModel) {
|
||||
const currentChatModel = await getLastUsedChatModel(
|
||||
chat.id
|
||||
)
|
||||
if (currentChatModel) {
|
||||
setSelectedModel(currentChatModel)
|
||||
}
|
||||
}
|
||||
const lastUsedPrompt =
|
||||
await getLastUsedChatSystemPrompt(chat.id)
|
||||
if (lastUsedPrompt) {
|
||||
if (lastUsedPrompt.prompt_id) {
|
||||
const prompt = await getPromptById(
|
||||
lastUsedPrompt.prompt_id
|
||||
)
|
||||
if (prompt) {
|
||||
setSelectedSystemPrompt(lastUsedPrompt.prompt_id)
|
||||
}
|
||||
}
|
||||
setSystemPrompt(lastUsedPrompt.prompt_content)
|
||||
}
|
||||
navigate("/")
|
||||
onClose()
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
className={`overflow-y-auto z-99 ${temporaryChat ? "pointer-events-none opacity-50" : ""}`}>
|
||||
@ -210,9 +171,10 @@ export const Sidebar = ({
|
||||
<div
|
||||
key={index}
|
||||
className={`
|
||||
flex py-2 px-2 items-center gap-3 relative rounded-md truncate group ease-in-out
|
||||
dark:hover:bg-[#2d2d2d] transition-colors duration-300
|
||||
${historyId === chat.id ? "text-white bg-[#2563eb] hover:bg-[#1d4ed8]" : "dark:text-gray-100 text-gray-800 hover:text-white hover:bg-[#2563eb]"}
|
||||
flex py-2 px-2 items-center gap-3 relative rounded-md truncate hover:pr-4 group transition-opacity duration-300 ease-in-out border
|
||||
hover:text-[#000000d9] hover:bg-[#f3f2ff] dark:hover:bg-[#2d2d2d] dark:border-gray-800
|
||||
hover:[&_.more-vertical]:text-[#000000d9]
|
||||
${historyId === chat.id ? 'text-[#000000d9] bg-[#f3f2ff] border-[#000000d9]' : 'dark:text-gray-100 text-gray-800'}
|
||||
`}>
|
||||
{chat?.message_source === "copilot" && (
|
||||
<Tooltip title={t("common:sidebarChat")} placement="top">
|
||||
@ -221,7 +183,38 @@ export const Sidebar = ({
|
||||
)}
|
||||
<button
|
||||
className="flex-1 overflow-hidden break-all text-start truncate w-full"
|
||||
onClick={() => handleHistoryClick(chat)}>
|
||||
onClick={async () => {
|
||||
const db = new PageAssitDatabase()
|
||||
const history = await db.getChatHistory(chat.id)
|
||||
setHistoryId(chat.id)
|
||||
setHistory(formatToChatHistory(history))
|
||||
setMessages(formatToMessage(history))
|
||||
const isLastUsedChatModel =
|
||||
await lastUsedChatModelEnabled()
|
||||
if (isLastUsedChatModel) {
|
||||
const currentChatModel = await getLastUsedChatModel(
|
||||
chat.id
|
||||
)
|
||||
if (currentChatModel) {
|
||||
setSelectedModel(currentChatModel)
|
||||
}
|
||||
}
|
||||
const lastUsedPrompt =
|
||||
await getLastUsedChatSystemPrompt(chat.id)
|
||||
if (lastUsedPrompt) {
|
||||
if (lastUsedPrompt.prompt_id) {
|
||||
const prompt = await getPromptById(
|
||||
lastUsedPrompt.prompt_id
|
||||
)
|
||||
if (prompt) {
|
||||
setSelectedSystemPrompt(lastUsedPrompt.prompt_id)
|
||||
}
|
||||
}
|
||||
setSystemPrompt(lastUsedPrompt.prompt_content)
|
||||
}
|
||||
navigate("/")
|
||||
onClose()
|
||||
}}>
|
||||
<span className="flex-grow truncate">{chat.title}</span>
|
||||
</button>
|
||||
<div className="flex items-center gap-2">
|
||||
@ -278,9 +271,7 @@ export const Sidebar = ({
|
||||
trigger={["click"]}
|
||||
placement="bottomRight">
|
||||
<button className="text-gray-500 dark:text-gray-400 opacity-80 hover:opacity-100">
|
||||
<MoreVertical
|
||||
className={`w-4 h-4 group-hover:text-white ${historyId === chat.id ? "text-white" : ""}`}
|
||||
/>
|
||||
<MoreVertical className={`group-hover:text-[#000000d9] w-4 h-4 more-vertical ${historyId === chat.id ? 'text-[#000000d9]' : ''}`} />
|
||||
</button>
|
||||
</Dropdown>
|
||||
</div>
|
||||
|
@ -1,419 +0,0 @@
|
||||
import React, { useEffect, useMemo, useRef, useState } from "react"
|
||||
import iodVideo from "@/public/video.mp4"
|
||||
import { useOptionLayoutContext } from "@/components/Layouts/Layout.tsx"
|
||||
import { createPortal } from "react-dom"
|
||||
import {
|
||||
ExpandOutlined,
|
||||
PauseCircleOutlined,
|
||||
PlayCircleOutlined
|
||||
} from "@ant-design/icons"
|
||||
import logo from "@/assets/logo.png"
|
||||
|
||||
const VideoPlayer = () => {
|
||||
const { setShowVideo } = useOptionLayoutContext()
|
||||
const videoRef = useRef<HTMLVideoElement>(null)
|
||||
const containerRef = useRef<HTMLDivElement>(null)
|
||||
const controlsTimerRef = useRef<NodeJS.Timeout | null>(null)
|
||||
const mouseMoveTimerRef = useRef<NodeJS.Timeout | null>(null)
|
||||
const isPlayingRef = useRef(false)
|
||||
|
||||
const [isPlaying, setIsPlaying] = useState(false)
|
||||
const [currentTime, setCurrentTime] = useState(0)
|
||||
const [duration, setDuration] = useState(0)
|
||||
const [volume, setVolume] = useState(1)
|
||||
const [isMuted, setIsMuted] = useState(false)
|
||||
const [showControls, setShowControls] = useState(false)
|
||||
const [isBuffering, setIsBuffering] = useState(false)
|
||||
|
||||
// 更新 isPlayingRef 当状态变化时
|
||||
useEffect(() => {
|
||||
isPlayingRef.current = isPlaying
|
||||
}, [isPlaying])
|
||||
|
||||
// 格式化时间
|
||||
const formatTime = (seconds: number) => {
|
||||
if (isNaN(seconds)) return "00:00"
|
||||
const minutes = Math.floor(seconds / 60)
|
||||
const secs = Math.floor(seconds % 60)
|
||||
return `${minutes.toString().padStart(2, "0")}:${secs.toString().padStart(2, "0")}`
|
||||
}
|
||||
|
||||
// 处理播放/暂停
|
||||
const togglePlayPause = () => {
|
||||
const video = videoRef.current
|
||||
if (!video) return
|
||||
|
||||
if (isPlaying) {
|
||||
video.pause()
|
||||
setIsPlaying(false)
|
||||
} else {
|
||||
video.play().catch((error) => {
|
||||
console.error("播放失败:", error)
|
||||
})
|
||||
setIsPlaying(true)
|
||||
}
|
||||
}
|
||||
|
||||
// 处理音量变化
|
||||
const handleVolumeChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
const newVolume = parseFloat(e.target.value)
|
||||
setVolume(newVolume)
|
||||
if (videoRef.current) {
|
||||
videoRef.current.volume = newVolume
|
||||
setIsMuted(newVolume === 0)
|
||||
}
|
||||
}
|
||||
|
||||
// 切换静音
|
||||
const toggleMute = () => {
|
||||
const newMuted = !isMuted
|
||||
setIsMuted(newMuted)
|
||||
if (videoRef.current) {
|
||||
videoRef.current.muted = newMuted
|
||||
}
|
||||
}
|
||||
|
||||
// 进度条点击处理
|
||||
const handleProgressClick = (e: React.MouseEvent<HTMLDivElement>) => {
|
||||
const progressBar = e.currentTarget
|
||||
const clickPosition = e.nativeEvent.offsetX
|
||||
const progressBarWidth = progressBar.offsetWidth
|
||||
if (duration > 0 && videoRef.current) {
|
||||
const newTime = (clickPosition / progressBarWidth) * duration
|
||||
videoRef.current.currentTime = newTime
|
||||
}
|
||||
}
|
||||
|
||||
// 全屏切换
|
||||
const toggleFullscreen = () => {
|
||||
const videoContainer = containerRef.current
|
||||
if (!videoContainer) return
|
||||
|
||||
if (!document.fullscreenElement) {
|
||||
if (videoContainer.requestFullscreen) {
|
||||
videoContainer.requestFullscreen().catch((err) => {
|
||||
console.error("全屏切换失败:", err)
|
||||
})
|
||||
}
|
||||
} else {
|
||||
if (document.exitFullscreen) {
|
||||
document.exitFullscreen()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const handleEnded = () => {
|
||||
setIsPlaying(false)
|
||||
setShowVideo(false)
|
||||
}
|
||||
|
||||
// 控制栏显示/隐藏 - 与原始HTML版本行为完全一致,添加防抖功能
|
||||
const handleMouseMove = (e: React.MouseEvent<HTMLDivElement>) => {
|
||||
// 清除之前的防抖定时器
|
||||
if (mouseMoveTimerRef.current) {
|
||||
clearTimeout(mouseMoveTimerRef.current)
|
||||
}
|
||||
|
||||
// 设置新的防抖定时器
|
||||
mouseMoveTimerRef.current = setTimeout(() => {
|
||||
const container = containerRef.current
|
||||
if (!container) return
|
||||
|
||||
const containerHeight = container.offsetHeight
|
||||
const mouseY = e.clientY - container.getBoundingClientRect().top
|
||||
console.log(mouseY > containerHeight - 150)
|
||||
// 如果鼠标在底部150px区域内
|
||||
if (mouseY > containerHeight - 150) {
|
||||
// 清除之前的隐藏定时器
|
||||
if (controlsTimerRef.current) {
|
||||
clearTimeout(controlsTimerRef.current)
|
||||
}
|
||||
// 立即显示控制器
|
||||
setShowControls(true)
|
||||
} else {
|
||||
// 鼠标离开底部区域,设置定时器隐藏控制器
|
||||
if (controlsTimerRef.current) {
|
||||
clearTimeout(controlsTimerRef.current)
|
||||
}
|
||||
controlsTimerRef.current = setTimeout(() => {
|
||||
setShowControls(false)
|
||||
}, 300) // 300ms后隐藏
|
||||
}
|
||||
}, 10) // 10ms 防抖延迟
|
||||
}
|
||||
|
||||
// 鼠标离开整个视频容器时立即隐藏控制器
|
||||
const handleMouseLeave = () => {
|
||||
// 清除防抖定时器
|
||||
if (mouseMoveTimerRef.current) {
|
||||
clearTimeout(mouseMoveTimerRef.current)
|
||||
}
|
||||
|
||||
// 清除控制栏隐藏定时器
|
||||
if (controlsTimerRef.current) {
|
||||
clearTimeout(controlsTimerRef.current)
|
||||
}
|
||||
|
||||
// 立即隐藏控制栏
|
||||
setShowControls(false)
|
||||
}
|
||||
|
||||
// 视频事件处理
|
||||
useEffect(() => {
|
||||
const video = videoRef.current
|
||||
if (!video) return
|
||||
|
||||
const handleLoadedMetadata = () => {
|
||||
setDuration(video.duration)
|
||||
}
|
||||
|
||||
const handleTimeUpdate = () => {
|
||||
setCurrentTime(video.currentTime)
|
||||
}
|
||||
|
||||
const handleWaiting = () => {
|
||||
setIsBuffering(true)
|
||||
}
|
||||
|
||||
const handlePlaying = () => {
|
||||
setIsBuffering(false)
|
||||
setIsPlaying(true)
|
||||
}
|
||||
|
||||
const handlePause = () => {
|
||||
if (!isBuffering) {
|
||||
setIsPlaying(false)
|
||||
}
|
||||
}
|
||||
|
||||
video.addEventListener("loadedmetadata", handleLoadedMetadata)
|
||||
video.addEventListener("timeupdate", handleTimeUpdate)
|
||||
video.addEventListener("waiting", handleWaiting)
|
||||
video.addEventListener("playing", handlePlaying)
|
||||
video.addEventListener("pause", handlePause)
|
||||
video.addEventListener("ended", handleEnded)
|
||||
|
||||
// 组件挂载时尝试播放视频
|
||||
const playVideo = async () => {
|
||||
try {
|
||||
await video.play()
|
||||
setIsPlaying(true)
|
||||
} catch (error) {
|
||||
console.error("自动播放失败:", error)
|
||||
}
|
||||
}
|
||||
|
||||
const timer = setTimeout(playVideo, 100)
|
||||
|
||||
return () => {
|
||||
video.removeEventListener("loadedmetadata", handleLoadedMetadata)
|
||||
video.removeEventListener("timeupdate", handleTimeUpdate)
|
||||
video.removeEventListener("waiting", handleWaiting)
|
||||
video.removeEventListener("playing", handlePlaying)
|
||||
video.removeEventListener("pause", handlePause)
|
||||
video.removeEventListener("ended", handleEnded)
|
||||
|
||||
// 清除所有定时器
|
||||
if (controlsTimerRef.current) {
|
||||
clearTimeout(controlsTimerRef.current)
|
||||
}
|
||||
if (mouseMoveTimerRef.current) {
|
||||
clearTimeout(mouseMoveTimerRef.current)
|
||||
}
|
||||
clearTimeout(timer)
|
||||
}
|
||||
}, [])
|
||||
|
||||
// 处理键盘事件
|
||||
const handleKeyDown = (e: KeyboardEvent) => {
|
||||
if (!videoRef.current) {
|
||||
return
|
||||
}
|
||||
|
||||
switch (e.code) {
|
||||
case "Space":
|
||||
e.preventDefault()
|
||||
const video = videoRef.current
|
||||
if (!video) return
|
||||
|
||||
if (isPlayingRef.current) {
|
||||
video.pause()
|
||||
setIsPlaying(false)
|
||||
} else {
|
||||
video.play().catch((error) => {
|
||||
console.error("播放失败:", error)
|
||||
})
|
||||
setIsPlaying(true)
|
||||
}
|
||||
break
|
||||
case "ArrowLeft":
|
||||
e.preventDefault()
|
||||
if (videoRef.current) {
|
||||
videoRef.current.currentTime = Math.max(
|
||||
0,
|
||||
videoRef.current.currentTime - 10
|
||||
)
|
||||
}
|
||||
break
|
||||
case "ArrowRight":
|
||||
e.preventDefault()
|
||||
if (videoRef.current && duration) {
|
||||
videoRef.current.currentTime = Math.min(
|
||||
duration,
|
||||
videoRef.current.currentTime + 10
|
||||
)
|
||||
}
|
||||
break
|
||||
case "ArrowUp":
|
||||
e.preventDefault()
|
||||
setVolume((prev) => {
|
||||
const newVolume = Math.min(1, prev + 0.1)
|
||||
if (videoRef.current) {
|
||||
videoRef.current.volume = newVolume
|
||||
setIsMuted(newVolume === 0)
|
||||
}
|
||||
return newVolume
|
||||
})
|
||||
break
|
||||
case "ArrowDown":
|
||||
e.preventDefault()
|
||||
setVolume((prev) => {
|
||||
const newVolume = Math.max(0, prev - 0.1)
|
||||
if (videoRef.current) {
|
||||
videoRef.current.volume = newVolume
|
||||
setIsMuted(newVolume === 0)
|
||||
}
|
||||
return newVolume
|
||||
})
|
||||
break
|
||||
case "KeyM":
|
||||
e.preventDefault()
|
||||
toggleMute()
|
||||
break
|
||||
case "KeyF":
|
||||
e.preventDefault()
|
||||
toggleFullscreen()
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// 键盘事件监听
|
||||
useEffect(() => {
|
||||
document.addEventListener("keydown", handleKeyDown)
|
||||
|
||||
if (containerRef.current) {
|
||||
containerRef.current.tabIndex = 0
|
||||
}
|
||||
|
||||
return () => {
|
||||
document.removeEventListener("keydown", handleKeyDown)
|
||||
}
|
||||
}, [duration])
|
||||
|
||||
// 计算进度条百分比
|
||||
const progressPercent = duration ? (currentTime / duration) * 100 : 0
|
||||
|
||||
// 是否隐藏logo
|
||||
const hideLogo = useMemo(() => {
|
||||
return localStorage.getItem("hideLogo") === "true"
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<div
|
||||
ref={containerRef}
|
||||
className="relative w-full h-screen bg-black flex justify-center items-center"
|
||||
onMouseMove={handleMouseMove}
|
||||
onMouseLeave={handleMouseLeave}>
|
||||
<video
|
||||
ref={videoRef}
|
||||
className="w-full h-full bg-black [&::-webkit-media-controls]:hidden [&::-webkit-media-controls-start-playback-button]:hidden"
|
||||
onClick={togglePlayPause}
|
||||
playsInline
|
||||
preload="auto">
|
||||
<source src={iodVideo} type="video/mp4" />
|
||||
您的浏览器不支持HTML5视频播放
|
||||
</video>
|
||||
|
||||
{/* 暂停时的遮罩层 */}
|
||||
{!isPlaying && !isBuffering && (
|
||||
<div
|
||||
className="absolute inset-0 bg-black bg-opacity-30 flex items-center justify-center cursor-pointer"
|
||||
onClick={togglePlayPause}>
|
||||
<PlayCircleOutlined className="text-white text-6xl opacity-80" />
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* 缓冲提示 */}
|
||||
{isBuffering && (
|
||||
<div className="absolute top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2 text-white text-sm bg-black bg-opacity-50 px-4 py-2 rounded">
|
||||
缓冲中...
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* 控制栏 - 使用与原始HTML相同的类名和行为 */}
|
||||
{createPortal(
|
||||
<div
|
||||
className={`fixed left-0 w-full bg-gradient-to-t from-black to-transparent p-4 transition-all duration-300 ease-in-out flex flex-col gap-2.5 z-50 ${showControls ? "bottom-0" : "-bottom-40"}`}>
|
||||
<div
|
||||
className="flex items-center justify-end gap-2 cursor-pointer"
|
||||
onClick={handleEnded}>
|
||||
{!hideLogo && <img src={logo} alt="logo" className="w-8" />}
|
||||
<h2 className="text-xl font-bold text-white dark:text-zinc-300 mr-3">
|
||||
<span className="text-[#d30100]">数联网</span>科创智能体
|
||||
</h2>
|
||||
</div>
|
||||
<div
|
||||
className="w-full h-1.5 bg-white bg-opacity-20 rounded cursor-pointer mb-2.5"
|
||||
onClick={handleProgressClick}>
|
||||
<div
|
||||
className="h-full bg-gradient-to-r from-orange-500 to-pink-600 rounded transition-all duration-100"
|
||||
style={{ width: `${progressPercent}%` }}></div>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center gap-4">
|
||||
<button
|
||||
className="bg-transparent border-none text-white text-lg cursor-pointer p-1 rounded-full w-12 h-12 flex items-center justify-center hover:bg-white hover:bg-opacity-20 transition-colors"
|
||||
onClick={togglePlayPause}>
|
||||
{isPlaying ? (
|
||||
<PauseCircleOutlined className="text-2xl" />
|
||||
) : (
|
||||
<PlayCircleOutlined className="text-2xl" />
|
||||
)}
|
||||
</button>
|
||||
|
||||
<span className="text-white text-sm min-w-[100px] text-center">
|
||||
<span>{formatTime(currentTime)}</span> /
|
||||
<span>{formatTime(duration)}</span>
|
||||
</span>
|
||||
|
||||
<div className="flex items-center ml-auto">
|
||||
<button
|
||||
className="bg-transparent border-none text-white text-2xl cursor-pointer p-1 rounded-full w-12 h-12 flex items-center justify-center hover:bg-white hover:bg-opacity-20 transition-colors"
|
||||
onClick={toggleMute}>
|
||||
{isMuted ? "🔇" : "🔊"}
|
||||
</button>
|
||||
<input
|
||||
type="range"
|
||||
min="0"
|
||||
max="1"
|
||||
step="0.1"
|
||||
value={isMuted ? 0 : volume}
|
||||
onChange={handleVolumeChange}
|
||||
className="w-20 h-1.5 ml-2.5"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<button
|
||||
className="bg-transparent border-none text-white text-lg cursor-pointer p-1 rounded-full w-12 h-12 flex items-center justify-center hover:bg-white hover:bg-opacity-20 transition-colors"
|
||||
onClick={toggleFullscreen}>
|
||||
<ExpandOutlined className="text-2xl" />
|
||||
</button>
|
||||
</div>
|
||||
</div>,
|
||||
document.body
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default VideoPlayer
|
@ -5,12 +5,6 @@ interface PageAssistContext {
|
||||
messages: Message[]
|
||||
setMessages: Dispatch<SetStateAction<Message[]>>
|
||||
|
||||
currentMessageId: string
|
||||
setCurrentMessageId: Dispatch<SetStateAction<string>>
|
||||
|
||||
iodLoading: boolean
|
||||
setIodLoading: Dispatch<SetStateAction<boolean>>
|
||||
|
||||
controller: AbortController | null
|
||||
setController: Dispatch<SetStateAction<AbortController>>
|
||||
|
||||
@ -22,12 +16,6 @@ export const PageAssistContext = createContext<PageAssistContext>({
|
||||
messages: [],
|
||||
setMessages: () => {},
|
||||
|
||||
currentMessageId: "",
|
||||
setCurrentMessageId: () => {},
|
||||
|
||||
iodLoading: false,
|
||||
setIodLoading: () => {},
|
||||
|
||||
controller: null,
|
||||
setController: () => {},
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { type ChatHistory as ChatHistoryType } from "~/store/option"
|
||||
import { AllIodRegistryEntry } from "@/types/iod.ts"
|
||||
import { type Message as MessageType } from "@/types/message.ts"
|
||||
import { getDefaultIodSources } from "@/libs/iod.ts"
|
||||
import {
|
||||
type ChatHistory as ChatHistoryType,
|
||||
type Message as MessageType
|
||||
} from "~/store/option"
|
||||
|
||||
type HistoryInfo = {
|
||||
id: string
|
||||
@ -30,7 +30,7 @@ type Message = {
|
||||
content: string
|
||||
images?: string[]
|
||||
webSources?: string[]
|
||||
iodSources?: AllIodRegistryEntry
|
||||
iodSources?: string[]
|
||||
search?: WebSearch
|
||||
createdAt: number
|
||||
reasoning_time_taken?: number
|
||||
@ -248,31 +248,38 @@ export const saveHistory = async (
|
||||
await db.addChatHistory(history)
|
||||
return history
|
||||
}
|
||||
export type HistoryMessage = {
|
||||
history_id: string
|
||||
name: string
|
||||
role: string
|
||||
content: string
|
||||
images: string[]
|
||||
iodSearch?: boolean
|
||||
webSearch?: boolean
|
||||
webSources?: any[]
|
||||
iodSources?: AllIodRegistryEntry
|
||||
createdAt?: number
|
||||
messageType?: string
|
||||
generationInfo?: any
|
||||
|
||||
export const saveMessage = async (
|
||||
history_id: string,
|
||||
name: string,
|
||||
role: string,
|
||||
content: string,
|
||||
images: string[],
|
||||
webSources?: any[],
|
||||
iodSources?: any[],
|
||||
time?: number,
|
||||
message_type?: string,
|
||||
generationInfo?: any,
|
||||
reasoning_time_taken?: number
|
||||
}
|
||||
export const saveMessage = async (msg: HistoryMessage): Promise<Message> => {
|
||||
) => {
|
||||
const id = generateID()
|
||||
let createdAt = Date.now()
|
||||
if (msg.createdAt) {
|
||||
createdAt += msg.createdAt
|
||||
if (time) {
|
||||
createdAt += time
|
||||
}
|
||||
const message = {
|
||||
...msg,
|
||||
id,
|
||||
history_id,
|
||||
name,
|
||||
role,
|
||||
content,
|
||||
images,
|
||||
createdAt,
|
||||
webSources,
|
||||
iodSources,
|
||||
messageType: message_type,
|
||||
generationInfo: generationInfo,
|
||||
reasoning_time_taken
|
||||
}
|
||||
const db = new PageAssitDatabase()
|
||||
await db.addMessage(message)
|
||||
@ -296,12 +303,11 @@ export const formatToMessage = (messages: MessageHistory): MessageType[] => {
|
||||
messages.sort((a, b) => a.createdAt - b.createdAt)
|
||||
return messages.map((message) => {
|
||||
return {
|
||||
...message,
|
||||
isBot: message.role === "assistant",
|
||||
message: message.content,
|
||||
name: message.name,
|
||||
webSources: message?.webSources || [],
|
||||
iodSources: message?.iodSources || getDefaultIodSources(),
|
||||
iodSources: message?.iodSources || [],
|
||||
images: message.images || [],
|
||||
generationInfo: message?.generationInfo,
|
||||
reasoning_time_taken: message?.reasoning_time_taken
|
||||
|
@ -1,75 +0,0 @@
|
||||
const iodConnection = "iodConnection-g3"
|
||||
|
||||
export const defaultIodConnectionConfig = {
|
||||
gatewayUrl: "tcp://reg01.public.internetofdata.cn:21037",
|
||||
registry: "data/Registry",
|
||||
localRepository: "data/Repository",
|
||||
doBrowser: "http://021.node.internetapi.cn:21030/SCIDE/SCManager"
|
||||
} as const
|
||||
|
||||
export type IodConnectionConfig = {
|
||||
gatewayUrl: string
|
||||
registry: string
|
||||
localRepository: string
|
||||
doBrowser: string
|
||||
}
|
||||
|
||||
export class IodDb {
|
||||
private static instance: IodDb
|
||||
private static iodConnectionConfig: IodConnectionConfig | null = null
|
||||
|
||||
// 单例模式
|
||||
static getInstance(): IodDb {
|
||||
if (!IodDb.instance) {
|
||||
IodDb.instance = new IodDb()
|
||||
}
|
||||
return IodDb.instance
|
||||
}
|
||||
|
||||
insertIodConnection(config: IodConnectionConfig): void {
|
||||
try {
|
||||
localStorage.setItem(iodConnection, JSON.stringify(config))
|
||||
IodDb.iodConnectionConfig = config
|
||||
} catch (error) {
|
||||
console.error('Failed to save IOD connection config:', error)
|
||||
throw new Error('Failed to save IOD connection configuration')
|
||||
}
|
||||
}
|
||||
|
||||
getIodConnection(): IodConnectionConfig {
|
||||
// 如果已经有缓存,直接返回
|
||||
if (IodDb.iodConnectionConfig) {
|
||||
return IodDb.iodConnectionConfig
|
||||
}
|
||||
|
||||
try {
|
||||
const val = localStorage.getItem(iodConnection)
|
||||
if (!val) {
|
||||
return defaultIodConnectionConfig
|
||||
}
|
||||
IodDb.iodConnectionConfig = JSON.parse(val)
|
||||
return IodDb.iodConnectionConfig
|
||||
} catch (error) {
|
||||
console.warn('Failed to parse IOD connection config, using default:', error)
|
||||
return defaultIodConnectionConfig
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// 添加清除配置的方法
|
||||
clearIodConnection(): void {
|
||||
try {
|
||||
localStorage.removeItem(iodConnection)
|
||||
IodDb.iodConnectionConfig = null
|
||||
} catch (error) {
|
||||
console.error('Failed to clear IOD connection config:', error)
|
||||
throw new Error('Failed to clear IOD connection configuration')
|
||||
}
|
||||
}
|
||||
|
||||
getIodConfig() {
|
||||
return {
|
||||
connection: this.getIodConnection(),
|
||||
}
|
||||
}
|
||||
}
|
@ -1,14 +1,8 @@
|
||||
import { HistoryMessage, saveHistory, saveMessage } from "@/db"
|
||||
import {
|
||||
setLastUsedChatModel,
|
||||
setLastUsedChatSystemPrompt
|
||||
} from "@/services/model-settings"
|
||||
import { saveHistory, saveMessage } from "@/db"
|
||||
import { setLastUsedChatModel, setLastUsedChatSystemPrompt } from "@/services/model-settings"
|
||||
import { generateTitle } from "@/services/title"
|
||||
import { ChatHistory } from "@/store/option"
|
||||
import { updateDialog } from "@/web/iod"
|
||||
import { AllIodRegistryEntry } from "@/types/iod.ts"
|
||||
import { getDefaultIodSources } from "@/libs/iod.ts"
|
||||
|
||||
export const saveMessageOnError = async ({
|
||||
e,
|
||||
history,
|
||||
@ -23,9 +17,7 @@ export const saveMessageOnError = async ({
|
||||
message_source = "web-ui",
|
||||
message_type,
|
||||
prompt_content,
|
||||
prompt_id,
|
||||
iodSearch,
|
||||
webSearch,
|
||||
prompt_id
|
||||
}: {
|
||||
e: any
|
||||
setHistory: (history: ChatHistory) => void
|
||||
@ -40,9 +32,7 @@ export const saveMessageOnError = async ({
|
||||
message_source?: "copilot" | "web-ui"
|
||||
message_type?: string
|
||||
prompt_id?: string
|
||||
prompt_content?: string,
|
||||
iodSearch?: boolean,
|
||||
webSearch?: boolean,
|
||||
prompt_content?: string
|
||||
}) => {
|
||||
if (
|
||||
e?.name === "AbortError" ||
|
||||
@ -63,63 +53,66 @@ export const saveMessageOnError = async ({
|
||||
}
|
||||
])
|
||||
|
||||
const defaultMessage: HistoryMessage = {
|
||||
history_id: historyId,
|
||||
name: selectedModel,
|
||||
role: "assistant",
|
||||
content: botMessage,
|
||||
webSources: [],
|
||||
iodSources: getDefaultIodSources(),
|
||||
messageType: message_type,
|
||||
iodSearch,
|
||||
webSearch,
|
||||
images: []
|
||||
}
|
||||
|
||||
if (historyId) {
|
||||
if (!isRegenerating) {
|
||||
await saveMessage({
|
||||
...JSON.parse(JSON.stringify(defaultMessage)),
|
||||
role: "user",
|
||||
content: userMessage,
|
||||
images: [image]
|
||||
})
|
||||
await saveMessage(
|
||||
historyId,
|
||||
selectedModel,
|
||||
"user",
|
||||
userMessage,
|
||||
[image],
|
||||
[],
|
||||
[],
|
||||
1,
|
||||
message_type
|
||||
)
|
||||
}
|
||||
await saveMessage({
|
||||
...JSON.parse(JSON.stringify(defaultMessage))
|
||||
})
|
||||
await saveMessage(
|
||||
historyId,
|
||||
selectedModel,
|
||||
"assistant",
|
||||
botMessage,
|
||||
[],
|
||||
[],
|
||||
[],
|
||||
2,
|
||||
message_type
|
||||
)
|
||||
await setLastUsedChatModel(historyId, selectedModel)
|
||||
if (prompt_id || prompt_content) {
|
||||
await setLastUsedChatSystemPrompt(historyId, {
|
||||
prompt_content,
|
||||
prompt_id
|
||||
})
|
||||
await setLastUsedChatSystemPrompt(historyId, { prompt_content, prompt_id })
|
||||
}
|
||||
} else {
|
||||
const title = await generateTitle(selectedModel, userMessage, userMessage)
|
||||
const newHistoryId = await saveHistory(title, false, message_source)
|
||||
if (!isRegenerating) {
|
||||
await saveMessage({
|
||||
...JSON.parse(JSON.stringify(defaultMessage)),
|
||||
history_id: newHistoryId.id,
|
||||
content: userMessage,
|
||||
role: "user",
|
||||
images: [image]
|
||||
})
|
||||
await saveMessage(
|
||||
newHistoryId.id,
|
||||
selectedModel,
|
||||
"user",
|
||||
userMessage,
|
||||
[image],
|
||||
[],
|
||||
[],
|
||||
1,
|
||||
message_type
|
||||
)
|
||||
}
|
||||
await saveMessage(
|
||||
{
|
||||
...JSON.parse(JSON.stringify(defaultMessage)),
|
||||
history_id: newHistoryId.id,
|
||||
},
|
||||
newHistoryId.id,
|
||||
selectedModel,
|
||||
"assistant",
|
||||
botMessage,
|
||||
[],
|
||||
[],
|
||||
[],
|
||||
2,
|
||||
message_type
|
||||
)
|
||||
setHistoryId(newHistoryId.id)
|
||||
await setLastUsedChatModel(newHistoryId.id, selectedModel)
|
||||
if (prompt_id || prompt_content) {
|
||||
await setLastUsedChatSystemPrompt(newHistoryId.id, {
|
||||
prompt_content,
|
||||
prompt_id
|
||||
})
|
||||
await setLastUsedChatSystemPrompt(newHistoryId.id, { prompt_content, prompt_id })
|
||||
}
|
||||
}
|
||||
|
||||
@ -137,13 +130,10 @@ export const saveMessageOnSuccess = async ({
|
||||
message,
|
||||
image,
|
||||
fullText,
|
||||
iodSearch,
|
||||
webSearch,
|
||||
webSources,
|
||||
iodSources,
|
||||
message_source = "web-ui",
|
||||
message_type,
|
||||
generationInfo,
|
||||
message_type, generationInfo,
|
||||
prompt_id,
|
||||
prompt_content,
|
||||
reasoning_time_taken = 0
|
||||
@ -155,87 +145,84 @@ export const saveMessageOnSuccess = async ({
|
||||
message: string
|
||||
image: string
|
||||
fullText: string
|
||||
iodSearch?: boolean
|
||||
webSearch?: boolean
|
||||
webSources: any[]
|
||||
iodSources: AllIodRegistryEntry
|
||||
message_source?: "copilot" | "web-ui"
|
||||
iodSources: any[]
|
||||
message_source?: "copilot" | "web-ui",
|
||||
message_type?: string
|
||||
generationInfo?: any
|
||||
prompt_id?: string
|
||||
prompt_content?: string
|
||||
reasoning_time_taken?: number
|
||||
}) => {
|
||||
var botMessage
|
||||
|
||||
const defaultMessage: HistoryMessage = {
|
||||
history_id: historyId,
|
||||
name: selectedModel,
|
||||
role: "assistant",
|
||||
content: fullText,
|
||||
webSources: webSources,
|
||||
iodSources: iodSources,
|
||||
messageType: message_type,
|
||||
images: [],
|
||||
iodSearch,
|
||||
webSearch,
|
||||
generationInfo,
|
||||
reasoning_time_taken,
|
||||
}
|
||||
var botMessage;
|
||||
if (historyId) {
|
||||
if (!isRegenerate) {
|
||||
await saveMessage(
|
||||
{
|
||||
...JSON.parse(JSON.stringify(defaultMessage)),
|
||||
role: "user",
|
||||
content: message,
|
||||
images: [image],
|
||||
webSources: [],
|
||||
iodSources: getDefaultIodSources(),
|
||||
},
|
||||
historyId,
|
||||
selectedModel,
|
||||
"user",
|
||||
message,
|
||||
[image],
|
||||
[],
|
||||
[],
|
||||
1,
|
||||
message_type,
|
||||
generationInfo,
|
||||
reasoning_time_taken
|
||||
)
|
||||
}
|
||||
botMessage = await saveMessage(
|
||||
{
|
||||
...JSON.parse(JSON.stringify(defaultMessage)),
|
||||
}
|
||||
historyId,
|
||||
selectedModel!,
|
||||
"assistant",
|
||||
fullText,
|
||||
[],
|
||||
webSources,
|
||||
iodSources,
|
||||
2,
|
||||
message_type,
|
||||
generationInfo,
|
||||
reasoning_time_taken
|
||||
)
|
||||
updateDialog(historyId, botMessage)
|
||||
await setLastUsedChatModel(historyId, selectedModel!)
|
||||
if (prompt_id || prompt_content) {
|
||||
await setLastUsedChatSystemPrompt(historyId, {
|
||||
prompt_content,
|
||||
prompt_id
|
||||
})
|
||||
await setLastUsedChatSystemPrompt(historyId, { prompt_content, prompt_id })
|
||||
}
|
||||
} else {
|
||||
const title = await generateTitle(selectedModel, message, message)
|
||||
const newHistoryId = await saveHistory(title, false, message_source)
|
||||
await saveMessage(
|
||||
{
|
||||
...JSON.parse(JSON.stringify(defaultMessage)),
|
||||
history_id: newHistoryId.id,
|
||||
role: "user",
|
||||
content: message,
|
||||
images: [image],
|
||||
webSources: [],
|
||||
iodSources: getDefaultIodSources(),
|
||||
},
|
||||
newHistoryId.id,
|
||||
selectedModel,
|
||||
"user",
|
||||
message,
|
||||
[image],
|
||||
[],
|
||||
[],
|
||||
1,
|
||||
message_type,
|
||||
generationInfo,
|
||||
reasoning_time_taken
|
||||
)
|
||||
botMessage = await saveMessage(
|
||||
{
|
||||
...JSON.parse(JSON.stringify(defaultMessage)),
|
||||
history_id: newHistoryId.id,
|
||||
}
|
||||
newHistoryId.id,
|
||||
selectedModel!,
|
||||
"assistant",
|
||||
fullText,
|
||||
[],
|
||||
webSources,
|
||||
iodSources,
|
||||
2,
|
||||
message_type,
|
||||
generationInfo,
|
||||
reasoning_time_taken
|
||||
)
|
||||
updateDialog(newHistoryId.id, botMessage)
|
||||
setHistoryId(newHistoryId.id)
|
||||
await setLastUsedChatModel(newHistoryId.id, selectedModel!)
|
||||
if (prompt_id || prompt_content) {
|
||||
await setLastUsedChatSystemPrompt(newHistoryId.id, {
|
||||
prompt_content,
|
||||
prompt_id
|
||||
})
|
||||
await setLastUsedChatSystemPrompt(newHistoryId.id, { prompt_content, prompt_id })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -21,15 +21,13 @@ const useDynamicTextareaSize = (
|
||||
const contentHeight = currentTextarea.scrollHeight;
|
||||
|
||||
if (maxHeight) {
|
||||
|
||||
|
||||
// Set max-height and adjust overflow behavior if maxHeight is provided
|
||||
currentTextarea.style.maxHeight = `${maxHeight}px`;
|
||||
currentTextarea.style.overflowY = contentHeight > maxHeight ? "scroll" : "hidden";
|
||||
currentTextarea.style.height = `${Math.min(contentHeight, maxHeight) < 60 ? 60 : Math.min(contentHeight, maxHeight)}px`;
|
||||
currentTextarea.style.fontWeight = "normal";
|
||||
currentTextarea.style.color = "#374151";
|
||||
currentTextarea.style.height = `${Math.min(contentHeight, maxHeight)}px`;
|
||||
} else {
|
||||
|
||||
|
||||
// Adjust height without max height constraint
|
||||
currentTextarea.style.height = `${contentHeight}px`;
|
||||
}
|
||||
@ -37,4 +35,4 @@ const useDynamicTextareaSize = (
|
||||
}, [textareaRef, textContent, maxHeight]);
|
||||
};
|
||||
|
||||
export default useDynamicTextareaSize;
|
||||
export default useDynamicTextareaSize;
|
@ -2,15 +2,16 @@ import React from "react"
|
||||
import { cleanUrl } from "~/libs/clean-url"
|
||||
import {
|
||||
defaultEmbeddingModelForRag,
|
||||
getOllamaURL,
|
||||
geWebSearchFollowUpPrompt,
|
||||
getOllamaURL,
|
||||
promptForRag,
|
||||
systemPromptForNonRag
|
||||
} from "~/services/ollama"
|
||||
import { useStoreMessageOption } from "~/store/option"
|
||||
import { useStoreMessageOption, type Message } from "~/store/option"
|
||||
import { useStoreMessage } from "~/store"
|
||||
import { SystemMessage } from "@langchain/core/messages"
|
||||
import { getDataFromCurrentTab } from "~/libs/get-html"
|
||||
import { MemoryVectorStore } from "langchain/vectorstores/memory"
|
||||
import { memoryEmbedding } from "@/utils/memory-embeddings"
|
||||
import { ChatHistory } from "@/store/option"
|
||||
import {
|
||||
@ -41,9 +42,6 @@ import {
|
||||
mergeReasoningContent,
|
||||
removeReasoning
|
||||
} from "@/libs/reasoning"
|
||||
import { AllIodRegistryEntry } from "@/types/iod.ts"
|
||||
import { getDefaultIodSources } from "@/libs/iod.ts"
|
||||
import { Message } from "@/types/message.ts"
|
||||
|
||||
export const useMessage = () => {
|
||||
const {
|
||||
@ -190,15 +188,15 @@ export const useMessage = () => {
|
||||
name: "You",
|
||||
message,
|
||||
webSources: [],
|
||||
iodSources: getDefaultIodSources(),
|
||||
iodSources: [],
|
||||
images: []
|
||||
},
|
||||
{
|
||||
isBot: true,
|
||||
name: selectedModel,
|
||||
message: "",
|
||||
message: "▋",
|
||||
webSources: [],
|
||||
iodSources: getDefaultIodSources(),
|
||||
iodSources: [],
|
||||
id: generateMessageId
|
||||
}
|
||||
]
|
||||
@ -210,7 +208,7 @@ export const useMessage = () => {
|
||||
name: selectedModel,
|
||||
message: "▋",
|
||||
webSources: [],
|
||||
iodSources: getDefaultIodSources(),
|
||||
iodSources: [],
|
||||
id: generateMessageId
|
||||
}
|
||||
]
|
||||
@ -244,7 +242,6 @@ export const useMessage = () => {
|
||||
}
|
||||
isAlreadyExistEmbedding = keepTrackOfEmbedding[websiteUrl]
|
||||
}
|
||||
|
||||
setMessages(newMessage)
|
||||
const ollamaUrl = await getOllamaURL()
|
||||
const embeddingModle = await defaultEmbeddingModelForRag()
|
||||
@ -351,7 +348,14 @@ export const useMessage = () => {
|
||||
metadata: Record<string, any>
|
||||
}[] = []
|
||||
// TODO: update type
|
||||
let iodSources: AllIodRegistryEntry = getDefaultIodSources()
|
||||
let iodSources: {
|
||||
name: any
|
||||
type: any
|
||||
mode: string
|
||||
url: string
|
||||
pageContent: string
|
||||
metadata: Record<string, any>
|
||||
}[] = []
|
||||
|
||||
if (chatWithWebsiteEmbedding) {
|
||||
const docs = await vectorstore.similaritySearch(query, 4)
|
||||
@ -508,7 +512,7 @@ export const useMessage = () => {
|
||||
content: fullText
|
||||
}
|
||||
])
|
||||
debugger
|
||||
|
||||
await saveMessageOnSuccess({
|
||||
historyId,
|
||||
setHistoryId,
|
||||
@ -619,7 +623,7 @@ export const useMessage = () => {
|
||||
name: "You",
|
||||
message,
|
||||
webSources: [],
|
||||
iodSources: getDefaultIodSources(),
|
||||
iodSources: [],
|
||||
images: []
|
||||
},
|
||||
{
|
||||
@ -627,7 +631,7 @@ export const useMessage = () => {
|
||||
name: selectedModel,
|
||||
message: "▋",
|
||||
webSources: [],
|
||||
iodSources: getDefaultIodSources(),
|
||||
iodSources: [],
|
||||
id: generateMessageId
|
||||
}
|
||||
]
|
||||
@ -639,7 +643,7 @@ export const useMessage = () => {
|
||||
name: selectedModel,
|
||||
message: "▋",
|
||||
webSources: [],
|
||||
iodSources: getDefaultIodSources(),
|
||||
iodSources: [],
|
||||
id: generateMessageId
|
||||
}
|
||||
]
|
||||
@ -803,7 +807,7 @@ export const useMessage = () => {
|
||||
image,
|
||||
fullText,
|
||||
webSources: [],
|
||||
iodSources: getDefaultIodSources(),
|
||||
iodSources: [],
|
||||
message_source: "copilot",
|
||||
generationInfo,
|
||||
reasoning_time_taken: timetaken
|
||||
@ -908,7 +912,7 @@ export const useMessage = () => {
|
||||
name: "You",
|
||||
message,
|
||||
webSources: [],
|
||||
iodSources: getDefaultIodSources(),
|
||||
iodSources: [],
|
||||
images: [image]
|
||||
},
|
||||
{
|
||||
@ -916,7 +920,7 @@ export const useMessage = () => {
|
||||
name: selectedModel,
|
||||
message: "▋",
|
||||
webSources: [],
|
||||
iodSources: getDefaultIodSources(),
|
||||
iodSources: [],
|
||||
id: generateMessageId
|
||||
}
|
||||
]
|
||||
@ -928,7 +932,7 @@ export const useMessage = () => {
|
||||
name: selectedModel,
|
||||
message: "▋",
|
||||
webSources: [],
|
||||
iodSources: getDefaultIodSources(),
|
||||
iodSources: [],
|
||||
id: generateMessageId
|
||||
}
|
||||
]
|
||||
@ -1097,7 +1101,7 @@ export const useMessage = () => {
|
||||
image,
|
||||
fullText,
|
||||
webSources: [],
|
||||
iodSources: getDefaultIodSources(),
|
||||
iodSources: [],
|
||||
message_source: "copilot",
|
||||
generationInfo,
|
||||
reasoning_time_taken: timetaken
|
||||
@ -1141,7 +1145,7 @@ export const useMessage = () => {
|
||||
isRegenerate: boolean,
|
||||
messages: Message[],
|
||||
history: ChatHistory,
|
||||
signal: AbortSignal
|
||||
signal: AbortSignal,
|
||||
) => {
|
||||
const url = await getOllamaURL()
|
||||
setStreaming(true)
|
||||
@ -1199,7 +1203,7 @@ export const useMessage = () => {
|
||||
name: "You",
|
||||
message,
|
||||
webSources: [],
|
||||
iodSources: getDefaultIodSources(),
|
||||
iodSources: [],
|
||||
images: [image]
|
||||
},
|
||||
{
|
||||
@ -1207,7 +1211,7 @@ export const useMessage = () => {
|
||||
name: selectedModel,
|
||||
message: "▋",
|
||||
webSources: [],
|
||||
iodSources: getDefaultIodSources(),
|
||||
iodSources: [],
|
||||
id: generateMessageId
|
||||
}
|
||||
]
|
||||
@ -1219,7 +1223,7 @@ export const useMessage = () => {
|
||||
name: selectedModel,
|
||||
message: "▋",
|
||||
webSources: [],
|
||||
iodSources: getDefaultIodSources(),
|
||||
iodSources: [],
|
||||
id: generateMessageId
|
||||
}
|
||||
]
|
||||
@ -1296,12 +1300,8 @@ export const useMessage = () => {
|
||||
query = removeReasoning(query)
|
||||
}
|
||||
|
||||
const { prompt, webSources, iodSources } = await getSystemPromptForWeb(
|
||||
query,
|
||||
[],
|
||||
webSearch,
|
||||
iodSearch
|
||||
)
|
||||
const { prompt, webSources, iodSources } =
|
||||
await getSystemPromptForWeb(query, [], webSearch, iodSearch)
|
||||
setIsSearchingInternet(false)
|
||||
|
||||
// message = message.trim().replaceAll("\n", " ")
|
||||
@ -1556,7 +1556,7 @@ export const useMessage = () => {
|
||||
name: "You",
|
||||
message,
|
||||
webSources: [],
|
||||
iodSources: getDefaultIodSources(),
|
||||
iodSources: [],
|
||||
images: [image],
|
||||
messageType: messageType
|
||||
},
|
||||
@ -1565,7 +1565,7 @@ export const useMessage = () => {
|
||||
name: selectedModel,
|
||||
message: "▋",
|
||||
webSources: [],
|
||||
iodSources: getDefaultIodSources(),
|
||||
iodSources: [],
|
||||
id: generateMessageId
|
||||
}
|
||||
]
|
||||
@ -1577,7 +1577,7 @@ export const useMessage = () => {
|
||||
name: selectedModel,
|
||||
message: "▋",
|
||||
webSources: [],
|
||||
iodSources: getDefaultIodSources(),
|
||||
iodSources: [],
|
||||
id: generateMessageId
|
||||
}
|
||||
]
|
||||
@ -1724,7 +1724,7 @@ export const useMessage = () => {
|
||||
image,
|
||||
fullText,
|
||||
webSources: [],
|
||||
iodSources: getDefaultIodSources(),
|
||||
iodSources: [],
|
||||
message_source: "copilot",
|
||||
message_type: messageType,
|
||||
generationInfo,
|
||||
@ -1811,7 +1811,7 @@ export const useMessage = () => {
|
||||
isRegenerate || false,
|
||||
messages,
|
||||
memory || history,
|
||||
signal
|
||||
signal,
|
||||
)
|
||||
} else {
|
||||
await normalChatMode(
|
||||
|
@ -2,14 +2,15 @@ import React from "react"
|
||||
import { cleanUrl } from "~/libs/clean-url"
|
||||
import {
|
||||
defaultEmbeddingModelForRag,
|
||||
getOllamaURL,
|
||||
geWebSearchFollowUpPrompt,
|
||||
geWebSearchKeywordsPrompt,
|
||||
getOllamaURL,
|
||||
promptForRag,
|
||||
systemPromptForNonRagOption
|
||||
} from "~/services/ollama"
|
||||
import type { ChatHistory, MeteringEntry } from "~/store/option"
|
||||
import { useStoreMessageOption } from "~/store/option"
|
||||
import type { ChatHistory, Message, MeteringEntry } from "~/store/option"
|
||||
import { SystemMessage } from "@langchain/core/messages"
|
||||
import { useStoreMessageOption } from "~/store/option"
|
||||
import {
|
||||
deleteChatForEdit,
|
||||
generateID,
|
||||
@ -46,21 +47,14 @@ import {
|
||||
mergeReasoningContent,
|
||||
removeReasoning
|
||||
} from "@/libs/reasoning"
|
||||
import { getDefaultIodSources } from "@/libs/iod.ts"
|
||||
import type { Message } from "@/types/message.ts"
|
||||
|
||||
export const useMessageOption = () => {
|
||||
const {
|
||||
controller: abortController,
|
||||
setController: setAbortController,
|
||||
iodLoading,
|
||||
setIodLoading,
|
||||
currentMessageId,
|
||||
setCurrentMessageId,
|
||||
messages,
|
||||
setMessages,
|
||||
setMessages
|
||||
} = usePageAssist()
|
||||
|
||||
const {
|
||||
history,
|
||||
setHistory,
|
||||
@ -119,8 +113,6 @@ export const useMessageOption = () => {
|
||||
setIsProcessing(false)
|
||||
setStreaming(false)
|
||||
currentChatModelSettings.reset()
|
||||
setIodLoading(false)
|
||||
setCurrentMessageId("")
|
||||
textareaRef?.current?.focus()
|
||||
if (defaultInternetSearchOn) {
|
||||
setWebSearch(true)
|
||||
@ -203,7 +195,6 @@ export const useMessageOption = () => {
|
||||
})
|
||||
let newMessage: Message[] = []
|
||||
let generateMessageId = generateID()
|
||||
setCurrentMessageId(generateMessageId)
|
||||
const meter: MeteringEntry = {
|
||||
id: generateMessageId,
|
||||
queryContent: message,
|
||||
@ -215,39 +206,36 @@ export const useMessageOption = () => {
|
||||
data: meter
|
||||
})
|
||||
|
||||
let defaultMessage: Message = {
|
||||
isBot: true,
|
||||
name: selectedModel,
|
||||
message,
|
||||
iodSearch,
|
||||
webSearch,
|
||||
webSources: [],
|
||||
iodSources: getDefaultIodSources(),
|
||||
images: [image]
|
||||
}
|
||||
|
||||
if (!isRegenerate) {
|
||||
newMessage = [
|
||||
...messages,
|
||||
{
|
||||
...JSON.parse(JSON.stringify(defaultMessage)),
|
||||
id: generateID(),
|
||||
isBot: false,
|
||||
name: "You",
|
||||
message,
|
||||
webSources: [],
|
||||
iodSources: [],
|
||||
images: [image]
|
||||
},
|
||||
{
|
||||
...JSON.parse(JSON.stringify(defaultMessage)),
|
||||
id: generateMessageId,
|
||||
message: "",
|
||||
isBot: true,
|
||||
name: selectedModel,
|
||||
message: "▋",
|
||||
webSources: [],
|
||||
iodSources: [],
|
||||
id: generateMessageId
|
||||
}
|
||||
]
|
||||
} else {
|
||||
newMessage = [
|
||||
...messages,
|
||||
{
|
||||
...JSON.parse(JSON.stringify(defaultMessage)),
|
||||
id: generateMessageId,
|
||||
message: " ",
|
||||
isBot: true,
|
||||
name: selectedModel,
|
||||
message: "▋",
|
||||
webSources: [],
|
||||
iodSources: [],
|
||||
id: generateMessageId
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -328,9 +316,7 @@ export const useMessageOption = () => {
|
||||
// Currently only IoD search use keywords
|
||||
if (iodSearch) {
|
||||
// Extract keywords
|
||||
console.log(
|
||||
"query:" + query + " --> " + JSON.stringify(tokenizeInput(query))
|
||||
)
|
||||
console.log("query:"+query+" --> "+JSON.stringify(tokenizeInput(query)));
|
||||
keywords = tokenizeInput(query)
|
||||
/*
|
||||
const questionPrompt = await geWebSearchKeywordsPrompt()
|
||||
@ -349,35 +335,15 @@ export const useMessageOption = () => {
|
||||
*/
|
||||
}
|
||||
|
||||
const {
|
||||
prompt,
|
||||
webSources,
|
||||
iodSources,
|
||||
iodSearchResults: iodData,
|
||||
iodTokenCount
|
||||
} = await getSystemPromptForWeb(query, keywords, webSearch, iodSearch)
|
||||
setIodLoading(false)
|
||||
const { prompt, webSources, iodSources, iodSearchResults: iodData, iodTokenCount } =
|
||||
await getSystemPromptForWeb(query, keywords, webSearch, iodSearch)
|
||||
console.log("prompt:\n" + prompt)
|
||||
setIsSearchingInternet(false)
|
||||
meter.prompt = prompt
|
||||
meter.iodKeywords = keywords
|
||||
meter.iodData = Object.values(iodData).flat()
|
||||
meter.iodData = iodData
|
||||
meter.iodTokenCount = iodTokenCount
|
||||
|
||||
|
||||
setMessages((prev) => {
|
||||
return prev.map((message) => {
|
||||
if (message.id === generateMessageId) {
|
||||
return {
|
||||
...message,
|
||||
webSources,
|
||||
iodSources,
|
||||
}
|
||||
}
|
||||
return message
|
||||
})
|
||||
})
|
||||
|
||||
// message = message.trim().replaceAll("\n", " ")
|
||||
|
||||
let humanMessage = await humanMessageFormatter({
|
||||
@ -529,8 +495,6 @@ export const useMessageOption = () => {
|
||||
message,
|
||||
image,
|
||||
fullText,
|
||||
iodSearch,
|
||||
webSearch,
|
||||
webSources,
|
||||
iodSources,
|
||||
generationInfo,
|
||||
@ -547,17 +511,20 @@ export const useMessageOption = () => {
|
||||
modelInputTokenCount: prompt.length,
|
||||
modelOutputTokenCount: fullText.length,
|
||||
model: ollama.modelName ?? ollama.model,
|
||||
relatedDataCount: Object.values(iodData).flat()?.length ?? 0,
|
||||
relatedDataCount: iodData?.length ?? 0,
|
||||
timeTaken: new Date().getTime() - chatStartTime.getTime(),
|
||||
date: chatStartTime.getTime(),
|
||||
cot,
|
||||
responseContent: content,
|
||||
modelResponseContent: fullText
|
||||
modelResponseContent: fullText,
|
||||
}
|
||||
const _meteringEntries = [currentMeteringEntry, ...meteringEntries]
|
||||
const _meteringEntries = [
|
||||
currentMeteringEntry,
|
||||
...meteringEntries,
|
||||
]
|
||||
setCurrentMeteringEntry({
|
||||
loading: false,
|
||||
data: currentMeteringEntry
|
||||
data: currentMeteringEntry,
|
||||
})
|
||||
setMeteringEntries(_meteringEntries)
|
||||
localStorage.setItem("meteringEntries", JSON.stringify(_meteringEntries))
|
||||
@ -572,9 +539,7 @@ export const useMessageOption = () => {
|
||||
setHistory,
|
||||
setHistoryId,
|
||||
userMessage: message,
|
||||
isRegenerating: isRegenerate,
|
||||
iodSearch,
|
||||
webSearch,
|
||||
isRegenerating: isRegenerate
|
||||
})
|
||||
|
||||
if (!errorSave) {
|
||||
@ -680,7 +645,6 @@ export const useMessageOption = () => {
|
||||
|
||||
let newMessage: Message[] = []
|
||||
let generateMessageId = generateID()
|
||||
setCurrentMessageId(generateMessageId)
|
||||
const meter: MeteringEntry = {
|
||||
id: generateMessageId,
|
||||
queryContent: message,
|
||||
@ -689,8 +653,9 @@ export const useMessageOption = () => {
|
||||
|
||||
setCurrentMeteringEntry({
|
||||
loading: true,
|
||||
data: meter
|
||||
data: meter,
|
||||
})
|
||||
|
||||
if (!isRegenerate) {
|
||||
newMessage = [
|
||||
...messages,
|
||||
@ -698,9 +663,8 @@ export const useMessageOption = () => {
|
||||
isBot: false,
|
||||
name: "You",
|
||||
message,
|
||||
id: generateID(),
|
||||
webSources: [],
|
||||
iodSources: getDefaultIodSources(),
|
||||
iodSources: [],
|
||||
images: [image]
|
||||
},
|
||||
{
|
||||
@ -708,7 +672,7 @@ export const useMessageOption = () => {
|
||||
name: selectedModel,
|
||||
message: "▋",
|
||||
webSources: [],
|
||||
iodSources: getDefaultIodSources(),
|
||||
iodSources: [],
|
||||
id: generateMessageId
|
||||
}
|
||||
]
|
||||
@ -720,7 +684,7 @@ export const useMessageOption = () => {
|
||||
name: selectedModel,
|
||||
message: "▋",
|
||||
webSources: [],
|
||||
iodSources: getDefaultIodSources(),
|
||||
iodSources: [],
|
||||
id: generateMessageId
|
||||
}
|
||||
]
|
||||
@ -898,6 +862,7 @@ export const useMessageOption = () => {
|
||||
content: fullText
|
||||
}
|
||||
])
|
||||
|
||||
await saveMessageOnSuccess({
|
||||
historyId,
|
||||
setHistoryId,
|
||||
@ -906,8 +871,6 @@ export const useMessageOption = () => {
|
||||
message,
|
||||
image,
|
||||
fullText,
|
||||
iodSearch,
|
||||
webSearch,
|
||||
source: [],
|
||||
generationInfo,
|
||||
prompt_content: promptContent,
|
||||
@ -920,24 +883,28 @@ export const useMessageOption = () => {
|
||||
setIsProcessing(false)
|
||||
setStreaming(false)
|
||||
|
||||
|
||||
// Save metering entry
|
||||
const { cot, content } = responseResolver(fullText)
|
||||
const currentMeteringEntry = {
|
||||
...meter,
|
||||
modelInputTokenCount: prompt? prompt.length : 0,
|
||||
modelOutputTokenCount: fullText? fullText.length : 0,
|
||||
modelInputTokenCount: prompt.length,
|
||||
modelOutputTokenCount: fullText.length,
|
||||
model: ollama.modelName ?? ollama.model,
|
||||
relatedDataCount: 0,
|
||||
relatedDataCount: 0,
|
||||
timeTaken: new Date().getTime() - chatStartTime.getTime(),
|
||||
date: chatStartTime.getTime(),
|
||||
cot,
|
||||
responseContent: content,
|
||||
modelResponseContent: fullText
|
||||
modelResponseContent: fullText,
|
||||
}
|
||||
const _meteringEntries = [currentMeteringEntry, ...meteringEntries]
|
||||
const _meteringEntries = [
|
||||
currentMeteringEntry,
|
||||
...meteringEntries,
|
||||
]
|
||||
setCurrentMeteringEntry({
|
||||
loading: false,
|
||||
data: currentMeteringEntry
|
||||
data: currentMeteringEntry,
|
||||
})
|
||||
setMeteringEntries(_meteringEntries)
|
||||
} catch (e) {
|
||||
@ -953,9 +920,7 @@ export const useMessageOption = () => {
|
||||
userMessage: message,
|
||||
isRegenerating: isRegenerate,
|
||||
prompt_content: promptContent,
|
||||
prompt_id: promptId,
|
||||
iodSearch,
|
||||
webSearch,
|
||||
prompt_id: promptId
|
||||
})
|
||||
|
||||
if (!errorSave) {
|
||||
@ -1031,7 +996,7 @@ export const useMessageOption = () => {
|
||||
name: "You",
|
||||
message,
|
||||
webSources: [],
|
||||
iodSources: getDefaultIodSources(),
|
||||
iodSources: [],
|
||||
images: []
|
||||
},
|
||||
{
|
||||
@ -1039,7 +1004,7 @@ export const useMessageOption = () => {
|
||||
name: selectedModel,
|
||||
message: "▋",
|
||||
webSources: [],
|
||||
iodSources: getDefaultIodSources(),
|
||||
iodSources: [],
|
||||
id: generateMessageId
|
||||
}
|
||||
]
|
||||
@ -1051,7 +1016,7 @@ export const useMessageOption = () => {
|
||||
name: selectedModel,
|
||||
message: "▋",
|
||||
webSources: [],
|
||||
iodSources: getDefaultIodSources(),
|
||||
iodSources: [],
|
||||
id: generateMessageId
|
||||
}
|
||||
]
|
||||
@ -1286,9 +1251,7 @@ export const useMessageOption = () => {
|
||||
fullText,
|
||||
source,
|
||||
generationInfo,
|
||||
reasoning_time_taken: timetaken,
|
||||
iodSearch,
|
||||
webSearch,
|
||||
reasoning_time_taken: timetaken
|
||||
})
|
||||
|
||||
setIsProcessing(false)
|
||||
@ -1304,9 +1267,7 @@ export const useMessageOption = () => {
|
||||
setHistory,
|
||||
setHistoryId,
|
||||
userMessage: message,
|
||||
isRegenerating: isRegenerate,
|
||||
iodSearch,
|
||||
webSearch,
|
||||
isRegenerating: isRegenerate
|
||||
})
|
||||
|
||||
if (!errorSave) {
|
||||
@ -1358,7 +1319,6 @@ export const useMessageOption = () => {
|
||||
)
|
||||
} else {
|
||||
if (webSearch || iodSearch) {
|
||||
setIodLoading(iodSearch)
|
||||
await searchChatMode(
|
||||
webSearch,
|
||||
iodSearch,
|
||||
@ -1475,10 +1435,6 @@ export const useMessageOption = () => {
|
||||
editMessage,
|
||||
messages,
|
||||
setMessages,
|
||||
iodLoading,
|
||||
currentMessageId,
|
||||
setIodLoading,
|
||||
setCurrentMessageId,
|
||||
onSubmit,
|
||||
setStreaming,
|
||||
streaming,
|
||||
|
@ -6,7 +6,6 @@ import {
|
||||
} from "@/db"
|
||||
import { exportKnowledge, importKnowledge } from "@/db/knowledge"
|
||||
import { exportVectors, importVectors } from "@/db/vector"
|
||||
import { IodDb } from "@/db/iod"
|
||||
import { message } from "antd"
|
||||
|
||||
export const exportPageAssistData = async () => {
|
||||
@ -14,14 +13,12 @@ export const exportPageAssistData = async () => {
|
||||
const chat = await exportChatHistory()
|
||||
const vector = await exportVectors()
|
||||
const prompts = await exportPrompts()
|
||||
const iod = IodDb.getInstance().getIodConfig()
|
||||
|
||||
const data = {
|
||||
knowledge,
|
||||
chat,
|
||||
vector,
|
||||
prompts,
|
||||
iod
|
||||
prompts
|
||||
}
|
||||
|
||||
const dataStr = JSON.stringify(data)
|
||||
@ -37,7 +34,6 @@ export const exportPageAssistData = async () => {
|
||||
}
|
||||
|
||||
export const importPageAssistData = async (file: File) => {
|
||||
debugger
|
||||
const reader = new FileReader()
|
||||
reader.onload = async () => {
|
||||
try {
|
||||
@ -59,10 +55,6 @@ export const importPageAssistData = async (file: File) => {
|
||||
await importPrompts(data.prompts)
|
||||
}
|
||||
|
||||
if(data?.iod) {
|
||||
IodDb.getInstance().insertIodConnection(data.iod.connection)
|
||||
}
|
||||
|
||||
message.success("Data imported successfully")
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
|
@ -1,18 +0,0 @@
|
||||
import { AllIodRegistryEntry } from "@/types/iod.ts"
|
||||
|
||||
export const getDefaultIodSources = (): AllIodRegistryEntry => {
|
||||
return {
|
||||
data: {
|
||||
data: [],
|
||||
total: 0
|
||||
},
|
||||
scenario: {
|
||||
data: [],
|
||||
total: 0
|
||||
},
|
||||
organization: {
|
||||
data: [],
|
||||
total: 0
|
||||
}
|
||||
}
|
||||
}
|
@ -1,91 +1,51 @@
|
||||
import { Avatar } from "antd"
|
||||
import { MedicineBottleFillIcon } from "@/components/Icons/MedicineBottleFill.tsx"
|
||||
import { CheckIcon } from "@/components/Icons/Check.tsx"
|
||||
import { NewBottleIcon } from "@/components/Icons/NewBottle.tsx"
|
||||
import { BatteryIcon } from "@/components/Icons/Battery.tsx"
|
||||
import { ShipIcon } from "@/components/Icons/Ship.tsx"
|
||||
import { Ship1Icon } from "@/components/Icons/Ship1.tsx"
|
||||
import RocketSvg from "@/assets/icons/rocket.svg"
|
||||
import BulbSvg from "@/assets/icons/bulb.svg"
|
||||
import EyeSvg from "@/assets/icons/eye.svg"
|
||||
import ASvg from "@/assets/icons/a.svg"
|
||||
import BSvg from "@/assets/icons/b.svg"
|
||||
import CSvg from "@/assets/icons/c.svg"
|
||||
import DSvg from "@/assets/icons/d.svg"
|
||||
import ESvg from "@/assets/icons/e.svg"
|
||||
import FSvg from "@/assets/icons/f.svg"
|
||||
|
||||
export const qaPrompt = [
|
||||
// {
|
||||
// title: "如何开发一个适合超大型城市的碳普惠方法学?",
|
||||
// icon: <img src={RocketSvg} alt="Rocket" className="w-10 my-0" />,
|
||||
// },
|
||||
// {
|
||||
// title: "如何开发一个零碳园区的数字化评价系统?",
|
||||
// icon: <img src={BulbSvg} alt="Rocket" className="w-10 my-0" />,
|
||||
// },
|
||||
// {
|
||||
// title: "如何开发一个碳定价预测系统?",
|
||||
// icon: <img src={EyeSvg} alt="Rocket" className="w-10 my-0" />,
|
||||
// },
|
||||
{
|
||||
title: "如何解决固态电池的成本和寿命难题?",
|
||||
icon: (
|
||||
<Avatar
|
||||
className="!bg-[#3581e3b3]"
|
||||
shape="square"
|
||||
size={40}
|
||||
icon={<BatteryIcon className="w-7" />}
|
||||
/>
|
||||
)
|
||||
title: "最近一年大型语言模型的技术进展有哪些?",
|
||||
icon: <img src={RocketSvg} alt="Rocket" className="w-full my-0" />,
|
||||
},
|
||||
{
|
||||
title: "如何解决船舶制造中的材料腐蚀难题?",
|
||||
icon: (
|
||||
<Avatar
|
||||
className="!bg-[#3581e3b3]"
|
||||
shape="square"
|
||||
size={40}
|
||||
icon={<ShipIcon className="w-7" />}
|
||||
/>
|
||||
)
|
||||
title: "生成式AI在企业中有哪些具体应用场景?",
|
||||
icon: <img src={BulbSvg} alt="Rocket" className="w-full my-0" />,
|
||||
},
|
||||
{
|
||||
title: "如何解决船舶制造中流体模拟和建模优化难题?",
|
||||
icon: (
|
||||
<Avatar
|
||||
className="!bg-[#3581e3b3]"
|
||||
shape="square"
|
||||
size={40}
|
||||
icon={<Ship1Icon className="w-7" />}
|
||||
/>
|
||||
)
|
||||
title: "多模态学习技术的最新研究方向是什么?",
|
||||
icon: <img src={EyeSvg} alt="Rocket" className="w-full my-0" />,
|
||||
},
|
||||
{
|
||||
title: "新药临床研究如何提升实验安全性?",
|
||||
icon: (
|
||||
<Avatar
|
||||
className="!bg-[#3581e3b3]"
|
||||
shape="square"
|
||||
size={40}
|
||||
icon={<MedicineBottleFillIcon className="w-7" />}
|
||||
/>
|
||||
)
|
||||
title: "当前AI芯片市场格局和未来三年发展趋势如何?",
|
||||
icon: <img src={ASvg} alt="Rocket" className="w-full my-0" />,
|
||||
},
|
||||
{
|
||||
title: "人工智能技术如何加速新药申报和审批?",
|
||||
icon: (
|
||||
<Avatar
|
||||
className="!bg-[#3581e3b3]"
|
||||
shape="square"
|
||||
size={40}
|
||||
icon={<CheckIcon className="w-7" />}
|
||||
/>
|
||||
)
|
||||
title: "主流深度学习框架性能与易用性对比分析?",
|
||||
icon: <img src={BSvg} alt="Rocket" className="w-full my-0" />,
|
||||
},
|
||||
{
|
||||
title: "如何研制与利妥昔单抗相似的新药?",
|
||||
icon: (
|
||||
<Avatar
|
||||
className="!bg-[#3581e3b3]"
|
||||
shape="square"
|
||||
size={40}
|
||||
icon={<NewBottleIcon className="w-7" />}
|
||||
/>
|
||||
)
|
||||
}
|
||||
title: "国内外AI伦理治理框架有哪些最佳实践?",
|
||||
icon: <img src={CSvg} alt="Rocket" className="w-full my-0" />,
|
||||
},
|
||||
{
|
||||
title: "大规模知识图谱构建与应用最新进展?",
|
||||
icon: <img src={DSvg} alt="Rocket" className="w-full my-0" />,
|
||||
},
|
||||
{
|
||||
title: "计算机视觉领域近期突破性技术有哪些?",
|
||||
icon: <img src={ESvg} alt="Rocket" className="w-full my-0" />,
|
||||
},
|
||||
{
|
||||
title: "量子计算对AI算法的影响与应用前景?",
|
||||
icon: <img src={FSvg} alt="Rocket" className="w-full my-0" />,
|
||||
},
|
||||
].map((item, index) => ({
|
||||
...item,
|
||||
id: index.toString()
|
||||
id: index.toString(),
|
||||
}))
|
||||
|
@ -7,7 +7,6 @@ import OptionOllamaSettings from "./options-settings-ollama"
|
||||
import OptionShare from "./option-settings-share"
|
||||
import OptionKnowledgeBase from "./option-settings-knowledge"
|
||||
import OptionAbout from "./option-settings-about"
|
||||
import OptionIodSettings from "./option-settings-iod"
|
||||
import SidepanelChat from "./sidepanel-chat"
|
||||
import SidepanelSettings from "./sidepanel-settings"
|
||||
import OptionRagSettings from "./option-rag"
|
||||
@ -29,7 +28,6 @@ export const OptionRoutingChrome = () => {
|
||||
<Route path="/settings/share" element={<OptionShare />} />
|
||||
<Route path="/settings/knowledge" element={<OptionKnowledgeBase />} />
|
||||
<Route path="/settings/rag" element={<OptionRagSettings />} />
|
||||
<Route path="/settings/iod" element={<OptionIodSettings />} />
|
||||
<Route path="/settings/about" element={<OptionAbout />} />
|
||||
<Route path="/metering" element={<OptionMetering />} />
|
||||
<Route path="/metering/list/:id" element={<MeteringListDetail />} />
|
||||
|
@ -15,7 +15,6 @@ const MeteringListDetail = lazy(() => import("./metering-list-detail"))
|
||||
const OptionShare = lazy(() => import("./option-settings-share"))
|
||||
const OptionKnowledgeBase = lazy(() => import("./option-settings-knowledge"))
|
||||
const OptionAbout = lazy(() => import("./option-settings-about"))
|
||||
const OptionIodSettings = lazy(() => import("./option-settings-iod"))
|
||||
const OptionRagSettings = lazy(() => import("./option-rag"))
|
||||
const OptionOpenAI = lazy(() => import("./option-settings-openai"))
|
||||
|
||||
@ -32,7 +31,6 @@ export const OptionRoutingFirefox = () => {
|
||||
<Route path="/settings/knowledge" element={<OptionKnowledgeBase />} />
|
||||
<Route path="/settings/about" element={<OptionAbout />} />
|
||||
<Route path="/settings/rag" element={<OptionRagSettings />} />
|
||||
<Route path="/settings/iod" element={<OptionIodSettings />} />
|
||||
<Route path="/metering" element={<OptionMetering />} />
|
||||
<Route path="/metering/list/:id" element={<MeteringListDetail />} />
|
||||
</Routes>
|
||||
|
@ -1,11 +1,10 @@
|
||||
import OptionLayout from "~/components/Layouts/Layout"
|
||||
import IodVideo from "@/components/Option/VideoPlayer/index.tsx"
|
||||
import { Playground } from "~/components/Option/Playground/Playground"
|
||||
|
||||
const OptionIndex = () => {
|
||||
return (
|
||||
<OptionLayout>
|
||||
<Playground />
|
||||
<Playground />
|
||||
</OptionLayout>
|
||||
)
|
||||
}
|
||||
|
@ -1,15 +0,0 @@
|
||||
import { SettingsLayout } from "~/components/Layouts/SettingsOptionLayout"
|
||||
import OptionLayout from "~/components/Layouts/Layout"
|
||||
import { IodApp } from "@/components/Option/Settings/iod"
|
||||
|
||||
const OptionAbout = () => {
|
||||
return (
|
||||
<OptionLayout>
|
||||
<SettingsLayout>
|
||||
<IodApp />
|
||||
</SettingsLayout>
|
||||
</OptionLayout>
|
||||
)
|
||||
}
|
||||
|
||||
export default OptionAbout
|
@ -21,26 +21,8 @@ const DEFAULT_RAG_QUESTION_PROMPT =
|
||||
|
||||
const DEFAUTL_RAG_SYSTEM_PROMPT = `You are a helpful AI assistant. Use the following pieces of context to answer the question at the end. If you don't know the answer, just say you don't know. DO NOT try to make up an answer. If the question is not related to the context, politely respond that you are tuned to only answer questions that are related to the context. {context} Question: {question} Helpful answer:`
|
||||
|
||||
const DEFAULT_WEBSEARCH_PROMPT = `你是一个中文AI助手,当前日期和时间是: {current_date_time}。在<数联网搜索结果> 中提供了来自数联网(Internet of Data)的搜索结果。
|
||||
每条搜索结果的格式如下:
|
||||
\`<result doId="{doId}" name="{title}" authors="{authors}" dataType="{paper,dataset or algorithm}" year="{year}" url="{url}" id="{id}">{abstract}</result>\`
|
||||
请在思考和回答中以如下形式展示搜索结果中与问题相关的 \`doId\` 和 \`name\` ,如果没有url则空着 :
|
||||
\`[数联网引用[id] doId: {doId} "{name}"]({url})\`
|
||||
例如:
|
||||
\`[数联网引用[1] doId: 10.48550/arXiv.1803.05591v2 "On the insufficiency of existing momentum schemes for Stochastic Optimization"](http://arxiv.org/pdf/1803.05591v2.pdf)\`
|
||||
|
||||
<数联网搜索结果>
|
||||
{iod_search_results}
|
||||
</数联网搜索结果>
|
||||
根据用户的问题,请筛选出<数联网搜索结果>中的相关信息,并结合<数联网搜索结果>中的数据、场景(项目)、人员团队等类型的相关信息,
|
||||
以举例的方式,在思考和回答中引用<数联网搜索结果>中的相关数据、场景和人员团队,例如回答中举例介绍一下如何利用这些数据、过往成功项目和这些人员团队及其相关经验来解决用户的问题。
|
||||
在<think>中,需要从问题的角度,罗列一下数联网搜索结果中与问题的相关的数据、场景(项目)、人员,注意罗列时使用数联网引用。
|
||||
在回答中从合理的角度开始展开,尝试按照初始验证、实验室验证、中试与熟化,公司设立与市场化的4个阶段来组织推进建议;
|
||||
注意在这4个阶段中引用相关的数据、场景和团队。
|
||||
最后,注意控制一下回答的长度。
|
||||
`
|
||||
|
||||
const DEFAULT_WEBSEARCH_PROMPT2 = `You are an AI assistant specialized in retrieving and analyzing academic papers from Neo4j graph database.
|
||||
const DEFAULT_WEBSEARCH_PROMPT = `You are an AI assistant specialized in retrieving and analyzing academic papers from Neo4j graph database.
|
||||
|
||||
Generate a response that how can user achieve his request based on provided search results. The current date and time are {current_date_time}.
|
||||
|
||||
@ -66,6 +48,7 @@ Use this information to generate a meaningful response that includes:
|
||||
<iod-search-results>
|
||||
{iod_search_results}
|
||||
</iod-search-results>
|
||||
|
||||
`
|
||||
|
||||
const DEFAULT_WEBSEARCH2_PROMPT = `You are an AI model who is expert at searching the web and answering user's queries.
|
||||
|
@ -18,10 +18,6 @@ export type ChatHistory = {
|
||||
type State = {
|
||||
messages: Message[]
|
||||
setMessages: (messages: Message[]) => void
|
||||
currentMessageId: string
|
||||
setCurrentMessageId: (messageId: string) => void
|
||||
iodLoading: boolean
|
||||
setIodLoading: (iodLoading: boolean) => void
|
||||
history: ChatHistory
|
||||
setHistory: (history: ChatHistory) => void
|
||||
streaming: boolean
|
||||
@ -57,10 +53,6 @@ type State = {
|
||||
export const useStoreMessage = create<State>((set) => ({
|
||||
messages: [],
|
||||
setMessages: (messages) => set({ messages }),
|
||||
currentMessageId: "",
|
||||
setCurrentMessageId: (currentMessageId) => set({ currentMessageId }),
|
||||
iodLoading: false,
|
||||
setIodLoading: (iodLoading) => set({ iodLoading }),
|
||||
history: [],
|
||||
setHistory: (history) => set({ history }),
|
||||
streaming: false,
|
||||
|
@ -1,6 +1,27 @@
|
||||
import { Knowledge } from "@/db/knowledge"
|
||||
import { create } from "zustand"
|
||||
import { Message } from "esbuild"
|
||||
|
||||
type WebSearch = {
|
||||
search_engine: string
|
||||
search_url: string
|
||||
search_query: string
|
||||
search_results: {
|
||||
title: string
|
||||
link: string
|
||||
}[]
|
||||
}
|
||||
export type Message = {
|
||||
isBot: boolean
|
||||
name: string
|
||||
message: string
|
||||
webSources: any[]
|
||||
iodSources: any[]
|
||||
images?: string[]
|
||||
search?: WebSearch
|
||||
reasoning_time_taken?: number
|
||||
id?: string
|
||||
messageType?: string
|
||||
}
|
||||
|
||||
export type ChatHistory = {
|
||||
role: "user" | "assistant" | "system"
|
||||
@ -14,11 +35,8 @@ type State = {
|
||||
setMessages: (messages: Message[]) => void
|
||||
history: ChatHistory
|
||||
setHistory: (history: ChatHistory) => void
|
||||
currentMeteringEntry: { data: MeteringEntry; loading: boolean }
|
||||
setCurrentMeteringEntry: (meteringEntry: {
|
||||
data: MeteringEntry
|
||||
loading: boolean
|
||||
}) => void
|
||||
currentMeteringEntry: {data: MeteringEntry, loading: boolean}
|
||||
setCurrentMeteringEntry: (meteringEntry: {data: MeteringEntry, loading: boolean}) => void
|
||||
meteringEntries: MeteringEntry[]
|
||||
setMeteringEntries: (meteringEntries: MeteringEntry[]) => void
|
||||
streaming: boolean
|
||||
@ -104,12 +122,9 @@ export const useStoreMessageOption = create<State>((set) => ({
|
||||
setMessages: (messages) => set({ messages }),
|
||||
history: [],
|
||||
setHistory: (history) => set({ history }),
|
||||
currentMeteringEntry: { data: {} as MeteringEntry, loading: false },
|
||||
setCurrentMeteringEntry: (currentMeteringEntry) =>
|
||||
set({ currentMeteringEntry }),
|
||||
meteringEntries: JSON.parse(
|
||||
localStorage.getItem("meteringEntries") || JSON.stringify([])
|
||||
),
|
||||
currentMeteringEntry: {data: {} as MeteringEntry, loading: false},
|
||||
setCurrentMeteringEntry: (currentMeteringEntry) => set({ currentMeteringEntry }),
|
||||
meteringEntries: JSON.parse(localStorage.getItem("meteringEntries") || JSON.stringify([])),
|
||||
setMeteringEntries: (meteringEntries) => set({ meteringEntries }),
|
||||
streaming: false,
|
||||
setStreaming: (streaming) => set({ streaming }),
|
||||
@ -132,7 +147,7 @@ export const useStoreMessageOption = create<State>((set) => ({
|
||||
setIsEmbedding: (isEmbedding) => set({ isEmbedding }),
|
||||
webSearch: false,
|
||||
setWebSearch: (webSearch) => set({ webSearch }),
|
||||
iodSearch: true,
|
||||
iodSearch: false,
|
||||
setIodSearch: (iodSearch) => set({ iodSearch }),
|
||||
isSearchingInternet: false,
|
||||
setIsSearchingInternet: (isSearchingInternet) => set({ isSearchingInternet }),
|
||||
|
@ -8,20 +8,4 @@ export type IodRegistryEntry = {
|
||||
data_space?: string
|
||||
data_type?:string
|
||||
traceId?:string
|
||||
fromRepo?: string
|
||||
}
|
||||
|
||||
export type AllIodRegistryEntry = {
|
||||
data: {
|
||||
data: IodRegistryEntry[]
|
||||
total: number
|
||||
}
|
||||
scenario: {
|
||||
data: IodRegistryEntry[]
|
||||
total: number
|
||||
}
|
||||
organization: {
|
||||
data: IodRegistryEntry[]
|
||||
total: number
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,3 @@
|
||||
import { AllIodRegistryEntry } from "@/types/iod.ts"
|
||||
|
||||
type WebSearch = {
|
||||
search_engine: string
|
||||
search_url: string
|
||||
@ -13,16 +11,12 @@ export type Message = {
|
||||
isBot: boolean
|
||||
name: string
|
||||
message: string
|
||||
webSearch?: boolean
|
||||
webSources: any[]
|
||||
iodSearch?: boolean
|
||||
iodSources: AllIodRegistryEntry
|
||||
iodSources: any[]
|
||||
images?: string[]
|
||||
search?: WebSearch
|
||||
messageType?: string
|
||||
id?: string
|
||||
generationInfo?: any
|
||||
reasoning_time_taken?: number
|
||||
}
|
||||
|
||||
export type Messages = Message[]
|
||||
}
|
528
src/web/iod.ts
@ -1,386 +1,275 @@
|
||||
import { cleanUrl } from "@/libs/clean-url"
|
||||
import { PageAssistHtmlLoader } from "@/loader/html"
|
||||
import { PageAssistPDFUrlLoader } from "@/loader/pdf-url"
|
||||
import { getOllamaURL } from "@/services/ollama"
|
||||
import { pageAssistEmbeddingModel } from "@/models/embedding"
|
||||
import { defaultEmbeddingModelForRag, getOllamaURL } from "@/services/ollama"
|
||||
|
||||
import {
|
||||
getIsSimpleInternetSearch,
|
||||
totalSearchResults
|
||||
} from "@/services/search"
|
||||
import { Document } from "@langchain/core/documents"
|
||||
import { AllIodRegistryEntry, IodRegistryEntry } from "~/types/iod"
|
||||
import { getPageAssistTextSplitter } from "@/utils/text-splitter"
|
||||
import { Document } from "@langchain/core/documents"
|
||||
import { MemoryVectorStore } from "langchain/vectorstores/memory"
|
||||
import type { IodRegistryEntry } from "~/types/iod"
|
||||
|
||||
|
||||
import { PageAssitDatabase } from "@/db"
|
||||
import exp from "constants"
|
||||
|
||||
import { enPOSTag, Segment, useDefault } from "segmentit"
|
||||
import { getDefaultIodSources } from "@/libs/iod.ts"
|
||||
import { IodDb } from "@/db/iod.ts"
|
||||
|
||||
const segment = useDefault(new Segment())
|
||||
import { Segment, useDefault, cnPOSTag, enPOSTag} from 'segmentit';
|
||||
const segment = useDefault(new Segment());
|
||||
export const tokenizeInput = function (input: string): string[] {
|
||||
const words = segment.doSegment(input, { simple: false })
|
||||
console.log(
|
||||
words.map(function (word) {
|
||||
return { w: word.w, p: enPOSTag(word.p) }
|
||||
})
|
||||
)
|
||||
return words.filter((word) => word.w.length > 1).map((word) => word.w)
|
||||
const words = segment.doSegment(input, { simple: false });
|
||||
console.log(words.map(function(word){return {w:word.w, p:enPOSTag(word.p)}}) );
|
||||
return words.filter(word =>( word.w.length > 1)).map(word=>word.w);
|
||||
}
|
||||
|
||||
function getIodConfig() {
|
||||
return IodDb.getInstance().getIodConnection()
|
||||
}
|
||||
export const iodConfigLocal = {
|
||||
gatewayUrl: "tcp://127.0.0.1:21036",
|
||||
registry: "bdware/Registry",
|
||||
localRepository: "bdtest.local/myrepo1",
|
||||
doBrowser: "http://127.0.0.1:21030/SCIDE/SCManager"
|
||||
//doipUrl = tcp://reg01.public.internetofdata.cn:21037
|
||||
export const iodConfig = {
|
||||
"gatewayUrl": "tcp://021.node.internetapi.cn:21052",
|
||||
"registry":"data/Registry",
|
||||
"localRepository":"data/Repository",
|
||||
"doBrowser":"http://021.node.internetapi.cn:21030/SCIDE/SCManager"
|
||||
}
|
||||
function inGrepList(str: string){
|
||||
return "什么|问题|需要|合适|设计|考虑|合作|精度|传感器|最新|研究|药物|如何|解决|中的|难题|成本|提升".indexOf(
|
||||
str
|
||||
) != -1
|
||||
return "什么|问题|需要|合适|设计|考虑|合作|精度|传感器|最新|研究|药物".indexOf(str)!=-1;
|
||||
}
|
||||
|
||||
export const makeSearchParamsWithDataType = function (
|
||||
count: number,
|
||||
keyword: string | string[],
|
||||
dataType: string
|
||||
) {
|
||||
const iodConfig = getIodConfig()
|
||||
const searchMode = []
|
||||
searchMode.push({ key: "data_type", type: "MUST", value: dataType })
|
||||
if (typeof keyword === "string") {
|
||||
export const makeRegSearchParams = function(count: number, keyword: string| string[]){
|
||||
const searchMode = [];
|
||||
if (typeof keyword === 'string') {
|
||||
// 如果 keyword 是字符串,则直接添加一个 searchMode 条目
|
||||
searchMode.push({
|
||||
key: "description",
|
||||
type: "MUST",
|
||||
value: keyword
|
||||
})
|
||||
});
|
||||
} else if (Array.isArray(keyword)) {
|
||||
// 如果 keyword 是数组,则为每个元素添加一个 searchMode 条目
|
||||
keyword.forEach((str) => {
|
||||
keyword.forEach(str => {
|
||||
if (!inGrepList(str))
|
||||
searchMode.push({
|
||||
key: "description",
|
||||
type: "SHOULD",
|
||||
value: str
|
||||
})
|
||||
})
|
||||
searchMode.push({
|
||||
key: "description",
|
||||
type: "SHOULD",
|
||||
value: str
|
||||
});
|
||||
});
|
||||
}
|
||||
return {
|
||||
action: "executeContract",
|
||||
contractID: "BDBrowser",
|
||||
operation: "sendRequestDirectly",
|
||||
arg: {
|
||||
id: iodConfig.registry,
|
||||
id: iodConfig.registry,
|
||||
//doipUrl:"tcp://127.0.0.1:21039",
|
||||
doipUrl: iodConfig.gatewayUrl,
|
||||
op: "Search",
|
||||
vars: {
|
||||
timeout: 15000
|
||||
vars:{
|
||||
timeout:15000
|
||||
},
|
||||
attributes: {
|
||||
offset: 0,
|
||||
count,
|
||||
bodyBase64Encoded: false,
|
||||
searchMode: searchMode
|
||||
searchMode:searchMode
|
||||
},
|
||||
body: ""
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const makeRegSearchParams = function (
|
||||
count: number,
|
||||
keyword: string | string[]
|
||||
) {
|
||||
const searchMode = []
|
||||
const iodConfig = getIodConfig()
|
||||
if (typeof keyword === "string") {
|
||||
// 如果 keyword 是字符串,则直接添加一个 searchMode 条目
|
||||
searchMode.push({
|
||||
key: "description",
|
||||
type: "MUST",
|
||||
value: keyword
|
||||
})
|
||||
} else if (Array.isArray(keyword)) {
|
||||
// 如果 keyword 是数组,则为每个元素添加一个 searchMode 条目
|
||||
keyword.forEach((str) => {
|
||||
if (!inGrepList(str))
|
||||
searchMode.push({
|
||||
key: "description",
|
||||
type: "SHOULD",
|
||||
value: str
|
||||
})
|
||||
})
|
||||
export const makeDOIPParams = (doId:string, op:string, attributes:Object, requestBody: string) => ({
|
||||
action: "executeContract",
|
||||
contractID: "BDBrowser",
|
||||
operation: "sendRequestDirectly",
|
||||
arg: {
|
||||
id: doId,
|
||||
doipUrl: iodConfig.gatewayUrl,
|
||||
op: op,
|
||||
attributes: attributes,
|
||||
body: requestBody
|
||||
}
|
||||
return {
|
||||
action: "executeContract",
|
||||
contractID: "BDBrowser",
|
||||
operation: "sendRequestDirectly",
|
||||
arg: {
|
||||
id: iodConfig.registry,
|
||||
//doipUrl:"tcp://127.0.0.1:21039",
|
||||
doipUrl: iodConfig.gatewayUrl,
|
||||
op: "Search",
|
||||
vars: {
|
||||
timeout: 15000
|
||||
},
|
||||
attributes: {
|
||||
offset: 0,
|
||||
count,
|
||||
bodyBase64Encoded: false,
|
||||
searchMode: searchMode
|
||||
},
|
||||
body: ""
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
export const makeDOIPParams = (
|
||||
doId: string,
|
||||
op: string,
|
||||
attributes: Object,
|
||||
requestBody: string
|
||||
) => {
|
||||
const iodConfig = getIodConfig()
|
||||
return {
|
||||
action: "executeContract",
|
||||
contractID: "BDBrowser",
|
||||
operation: "sendRequestDirectly",
|
||||
arg: {
|
||||
id: doId,
|
||||
doipUrl: iodConfig.gatewayUrl,
|
||||
op: op,
|
||||
attributes: attributes,
|
||||
body: requestBody
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const retrieveDoc = function (doId: string): Promise<Document> {
|
||||
const iodConfig = getIodConfig()
|
||||
console.log("retriveDoc:" + doId)
|
||||
const params = makeDOIPParams(
|
||||
doId,
|
||||
"Retrieve",
|
||||
{
|
||||
bodyBase64Encoded: false
|
||||
},
|
||||
""
|
||||
)
|
||||
export const retrieveDoc = function(doId: string) : Promise<Document> {
|
||||
console.log("retriveDoc:"+doId)
|
||||
const params = makeDOIPParams(doId,"Retrieve",{
|
||||
bodyBase64Encoded: false
|
||||
}, "");
|
||||
const abortController = new AbortController()
|
||||
setTimeout(() => abortController.abort(), 10000)
|
||||
return fetch(iodConfig.doBrowser, {
|
||||
method: "POST",
|
||||
body: JSON.stringify(params),
|
||||
signal: abortController.signal
|
||||
})
|
||||
.then((response) => {
|
||||
console.log("responseIn retrieveDoc:")
|
||||
console.log(response)
|
||||
return response.json()
|
||||
})
|
||||
.then((res) => {
|
||||
console.log("res:")
|
||||
console.log(res.result.body)
|
||||
//TODO
|
||||
method: "POST",
|
||||
body: JSON.stringify(params),
|
||||
signal: abortController.signal
|
||||
}).then((response) => {
|
||||
console.log("responseIn retrieveDoc:");
|
||||
console.log(response);
|
||||
return response.json()})
|
||||
.then((res) => {
|
||||
console.log("res:");
|
||||
console.log(res.result.body);
|
||||
//TODO
|
||||
return {
|
||||
metadata: { traceId: res.result.header.attributes?.traceId },
|
||||
pageContent: res.result.body
|
||||
metadata:{traceId:res.result.header.attributes?.traceId},
|
||||
pageContent:res.result.body
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
export const updateInLocalRepo = function (
|
||||
historyId: string,
|
||||
requestBody: Object
|
||||
): Promise<string> {
|
||||
const iodConfig = getIodConfig()
|
||||
const params = makeDOIPParams(
|
||||
iodConfig.localRepository,
|
||||
"Update",
|
||||
{
|
||||
aiDialogID: historyId,
|
||||
bodyBase64Encoded: false
|
||||
},
|
||||
JSON.stringify(requestBody)
|
||||
)
|
||||
export const updateInLocalRepo = function(historyId: string, requestBody: Object) : Promise<string> {
|
||||
const params = makeDOIPParams(iodConfig.localRepository,"Update",{
|
||||
"aiDialogID": historyId,
|
||||
bodyBase64Encoded: false
|
||||
}, JSON.stringify(requestBody));
|
||||
const abortController = new AbortController()
|
||||
setTimeout(() => abortController.abort(), 10000)
|
||||
return fetch(iodConfig.doBrowser, {
|
||||
method: "POST",
|
||||
body: JSON.stringify(params),
|
||||
signal: abortController.signal
|
||||
}).then((response) => response.json())
|
||||
.then((res) => {
|
||||
console.log("update dialog:"+JSON.stringify(res))
|
||||
return res.body;
|
||||
})
|
||||
.then((response) => response.json())
|
||||
.then((res) => {
|
||||
console.log("update dialog:" + JSON.stringify(res))
|
||||
return res.body
|
||||
})
|
||||
}
|
||||
export const updateDialog = async function (
|
||||
histroyId: string,
|
||||
botMessage: any
|
||||
): Promise<string> {
|
||||
export const updateDialog = async function(histroyId : string, botMessage: any): Promise<string> {
|
||||
//TODO @Nex confused by Message/MessageType in ./db/index.ts!
|
||||
const db = new PageAssitDatabase()
|
||||
const chatHistory = await db.getChatHistory(histroyId)
|
||||
var userMessage = null
|
||||
for (var i = 0; i < chatHistory.length; i++) {
|
||||
userMessage = chatHistory[i]
|
||||
if (userMessage.role == "user") break
|
||||
var userMessage = null;
|
||||
for (var i=0;i<chatHistory.length;i++){
|
||||
userMessage = chatHistory[i];
|
||||
if (userMessage.role=='user') break;
|
||||
}
|
||||
let updateBody: any = {}
|
||||
let updateBody:any = {};
|
||||
// !!!IMPORTANT!!! traceId = histroyId+"/"+userMessage.id;
|
||||
// Update traceId in retrieveDoc!
|
||||
updateBody.traceId = histroyId + "/" + userMessage.id
|
||||
updateBody.traceId = histroyId+"/"+userMessage.id;
|
||||
updateBody.question = {
|
||||
id: histroyId + "/" + userMessage.id,
|
||||
content: userMessage.content,
|
||||
tokenCount: userMessage.content.length
|
||||
"id": histroyId+"/"+userMessage.id,
|
||||
"content": userMessage.content,
|
||||
"tokenCount": userMessage.content.length
|
||||
}
|
||||
updateBody.answer = {
|
||||
id: histroyId + "/" + botMessage.id,
|
||||
content: botMessage.content,
|
||||
tokenCount: botMessage.content.length
|
||||
"id": histroyId+"/"+botMessage.id,
|
||||
"content": botMessage.content,
|
||||
"tokenCount": botMessage.content.length
|
||||
}
|
||||
//TODO set a correct model ID
|
||||
updateBody.model = { id: "bdware.ollama/" + userMessage.name }
|
||||
updateBody.model = {"id":"bdware.ollama/" + userMessage.name}
|
||||
|
||||
//TODO incorrect tokenCount calculated!!
|
||||
updateBody.webSources =
|
||||
botMessage.webSources?.map((r) => ({
|
||||
url: r.url,
|
||||
tokenCount: r.url.length,
|
||||
content: r.url,
|
||||
traceId: r?.traceId
|
||||
})) ?? []
|
||||
|
||||
updateBody.IoDSources =
|
||||
Object.values((botMessage?.iodSources ?? {}) as AllIodRegistryEntry).flatMap(iod => iod.data)?.map((r) => ({
|
||||
id: r.doId,
|
||||
tokenCount:
|
||||
r.content || r.description
|
||||
? calculateTokenCount(r.content || r.description)
|
||||
: 0,
|
||||
content: r.content || r.description,
|
||||
traceId: r?.traceId
|
||||
})) ?? []
|
||||
console.log("updateBody:")
|
||||
updateBody.webSources = botMessage.webSources?.map((r) => ({
|
||||
url: r.url,
|
||||
tokenCount: r.url.length,
|
||||
content: r.url,
|
||||
traceId: r?.traceId
|
||||
})) ?? [];
|
||||
updateBody.IoDSources = botMessage.iodSources?.map((r) => ({
|
||||
id: r.doId,
|
||||
tokenCount: (r.content || r.description)?calculateTokenCount((r.content || r.description)):0,
|
||||
content: r.content || r.description,
|
||||
traceId: r?.traceId
|
||||
})) ?? [];
|
||||
console.log("updateBody:");
|
||||
console.log(updateBody)
|
||||
return updateInLocalRepo(histroyId, updateBody)
|
||||
return updateInLocalRepo(histroyId,updateBody)
|
||||
}
|
||||
|
||||
export async function localIodSearch(
|
||||
query: string,
|
||||
keywords: string[]
|
||||
): Promise<AllIodRegistryEntry> {
|
||||
const iodConfig = getIodConfig()
|
||||
): Promise<IodRegistryEntry[]> {
|
||||
const TOTAL_SEARCH_RESULTS = await totalSearchResults()
|
||||
const abortController = new AbortController()
|
||||
setTimeout(() => abortController.abort(), 10000)
|
||||
const params = makeRegSearchParams(TOTAL_SEARCH_RESULTS, keywords)
|
||||
const dataParams = makeSearchParamsWithDataType(
|
||||
TOTAL_SEARCH_RESULTS,
|
||||
keywords,
|
||||
"data"
|
||||
)
|
||||
const scenarioParams = makeSearchParamsWithDataType(
|
||||
TOTAL_SEARCH_RESULTS,
|
||||
keywords,
|
||||
"scenario"
|
||||
)
|
||||
const orgParams = makeSearchParamsWithDataType(
|
||||
TOTAL_SEARCH_RESULTS,
|
||||
keywords,
|
||||
"organization"
|
||||
)
|
||||
|
||||
const abortController = new AbortController();
|
||||
setTimeout(() => abortController.abort(), 10000);
|
||||
const params = makeRegSearchParams(TOTAL_SEARCH_RESULTS, keywords);
|
||||
console.log('params------->',params)
|
||||
try {
|
||||
console.log("params------->", params)
|
||||
const requests = [
|
||||
fetch(iodConfig.doBrowser, {
|
||||
method: "POST",
|
||||
body: JSON.stringify(dataParams),
|
||||
signal: abortController.signal
|
||||
}),
|
||||
fetch(iodConfig.doBrowser, {
|
||||
method: "POST",
|
||||
body: JSON.stringify(scenarioParams),
|
||||
signal: abortController.signal
|
||||
}),
|
||||
fetch(iodConfig.doBrowser, {
|
||||
method: "POST",
|
||||
body: JSON.stringify(orgParams),
|
||||
signal: abortController.signal
|
||||
})
|
||||
]
|
||||
//TODO @Zhaoweijie, 这三类分别是数据、场景、团队的搜索请求。
|
||||
const responses = await Promise.all(requests)
|
||||
const results = await Promise.all(responses.map((res) => res.json()))
|
||||
const response = await fetch(iodConfig.doBrowser, {
|
||||
method: "POST",
|
||||
body: JSON.stringify(params),
|
||||
signal: abortController.signal
|
||||
});
|
||||
|
||||
const allResults: AllIodRegistryEntry = getDefaultIodSources()
|
||||
let i = 0
|
||||
for (const res of results) {
|
||||
// 检查顶层状态
|
||||
if (res.status !== "Success") {
|
||||
continue // 跳过失败的请求
|
||||
}
|
||||
let body
|
||||
try {
|
||||
body = JSON.parse(res.result.body)
|
||||
} catch (e) {
|
||||
console.warn("Failed to parse result.body as JSON", e)
|
||||
continue
|
||||
}
|
||||
|
||||
if (body.code !== 0) {
|
||||
continue
|
||||
}
|
||||
const entries: IodRegistryEntry[] = body.data?.results || []
|
||||
const prunedEntries: IodRegistryEntry[] = []
|
||||
const seenDoIds = new Set<string>()
|
||||
// 数据清洗:补全 url 和 doId
|
||||
for (const r of entries) {
|
||||
r.url = r.url || r.pdf_url
|
||||
// @ts-ignore
|
||||
r.doId = r.doId || r.doid
|
||||
if (seenDoIds.has(r.doId)) {
|
||||
continue
|
||||
}
|
||||
prunedEntries.push(r)
|
||||
}
|
||||
|
||||
// 数据
|
||||
if (i === 0) {
|
||||
allResults.data = {
|
||||
data: prunedEntries,
|
||||
total: body.data?.total ?? 0
|
||||
}
|
||||
}
|
||||
// 场景
|
||||
if (i === 1) {
|
||||
allResults.scenario = {
|
||||
data: prunedEntries,
|
||||
total: body.data?.total ?? 0
|
||||
}
|
||||
}
|
||||
// 团队
|
||||
if (i === 2) {
|
||||
allResults.organization = {
|
||||
data: prunedEntries,
|
||||
total: body.data?.total ?? 0
|
||||
}
|
||||
}
|
||||
i++
|
||||
const res = await response.json();
|
||||
if (res.status !== "Success") {
|
||||
return [];
|
||||
}
|
||||
|
||||
return allResults
|
||||
const body = JSON.parse(res.result.body);
|
||||
if (body.code !== 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
let results: IodRegistryEntry[] = body.data?.results || [];
|
||||
for (const r of results) {
|
||||
r.url = r.url || r.pdf_url;
|
||||
}
|
||||
for (const r of results) {
|
||||
r.doId = r.doId || r.doid;
|
||||
}
|
||||
|
||||
// results 根据 doId 去重
|
||||
const map = new Map<string, IodRegistryEntry>();
|
||||
for (const r of results) {
|
||||
map.set(r.doId, r);
|
||||
}
|
||||
return Array.from(map.values());
|
||||
} catch (e) {
|
||||
console.log(e)
|
||||
return getDefaultIodSources()
|
||||
console.log(e);
|
||||
return [];
|
||||
}
|
||||
/*
|
||||
const results = (
|
||||
await Promise.all(
|
||||
keywords.map(async (keyword) => {
|
||||
const abortController = new AbortController()
|
||||
setTimeout(() => abortController.abort(), 10000)
|
||||
//http://47.93.156.31:21033/SCIDE/SCManager
|
||||
|
||||
const params = makeRegSearchParams(TOTAL_SEARCH_RESULTS, keyword)
|
||||
return fetch(iodConfig.doBrowser, {
|
||||
method: "POST",
|
||||
body: JSON.stringify(params),
|
||||
signal: abortController.signal
|
||||
})
|
||||
.then((response) => response.json())
|
||||
.then((res) => {
|
||||
if (res.status !== "Success") {
|
||||
console.log(res)
|
||||
return []
|
||||
}
|
||||
const body = JSON.parse(res.result.body)
|
||||
if (body.code !== 0) {
|
||||
console.log(body)
|
||||
return []
|
||||
}
|
||||
const results: IodRegistryEntry[] = body.data?.results || []
|
||||
for (const r of results) {
|
||||
r.url = r.url || r.pdf_url
|
||||
}
|
||||
for (const r of results) {
|
||||
r.doId = r.doId || r.doid
|
||||
}
|
||||
return results
|
||||
})
|
||||
.catch((e) => {
|
||||
console.log(e)
|
||||
return []
|
||||
})
|
||||
})
|
||||
)
|
||||
).flat()
|
||||
|
||||
// results 根据 doId 去重
|
||||
const map = new Map<string, IodRegistryEntry>()
|
||||
for (const r of results) {
|
||||
map.set(r.doId, r)
|
||||
}
|
||||
return Array.from(map.values())
|
||||
*/
|
||||
}
|
||||
|
||||
const ARXIV_URL_PATTERN = /^https?:\/\/arxiv\.org\//
|
||||
@ -390,41 +279,27 @@ export const searchIod = async (query: string, keywords: string[]) => {
|
||||
const searchResults = await localIodSearch(query, keywords)
|
||||
|
||||
const isSimpleMode = await getIsSimpleInternetSearch()
|
||||
console.log(
|
||||
"searchMode:" +
|
||||
isSimpleMode +
|
||||
"\n kw:" +
|
||||
JSON.stringify(keywords) +
|
||||
"\n" +
|
||||
" ->searchResult:\n" +
|
||||
JSON.stringify(searchResults)
|
||||
)
|
||||
console.log("searchMode:"+isSimpleMode+"\n kw:"+JSON.stringify(keywords)+"\n"+" ->searchResult:\n"+JSON.stringify(searchResults))
|
||||
if (isSimpleMode) {
|
||||
await getOllamaURL()
|
||||
return searchResults
|
||||
}
|
||||
|
||||
const docs: Document<Record<string, any>>[] = []
|
||||
for (const result of Object.values(searchResults)
|
||||
.map((item) => item.data)
|
||||
.flat()) {
|
||||
const resMap = new Map<string, IodRegistryEntry>()
|
||||
for (const result of searchResults) {
|
||||
const url = result.url
|
||||
if (result.doId) {
|
||||
//TODO !!!!@Nex traceId should be the id of history/question!
|
||||
let docFromRetrieve = await retrieveDoc(result.doId)
|
||||
console.log(
|
||||
"doc from Retrieve:" +
|
||||
result.doId +
|
||||
" -->" +
|
||||
JSON.stringify(docFromRetrieve)
|
||||
)
|
||||
docs.push(docFromRetrieve)
|
||||
result.description = docFromRetrieve.pageContent
|
||||
result.traceId = docFromRetrieve.metadata?.traceId
|
||||
continue
|
||||
if (result.doId){
|
||||
//TODO !!!!@Nex traceId should be the id of history/question!
|
||||
let docFromRetrieve = await retrieveDoc(result.doId);
|
||||
console.log("doc from Retrieve:"+result.doId+" -->"+JSON.stringify(docFromRetrieve))
|
||||
docs.push(docFromRetrieve)
|
||||
result.description = docFromRetrieve.pageContent;
|
||||
result.traceId = docFromRetrieve.metadata?.traceId;
|
||||
continue;
|
||||
}
|
||||
if (!url) {
|
||||
continue
|
||||
continue;
|
||||
}
|
||||
|
||||
let htmlUrl = ""
|
||||
@ -522,7 +397,8 @@ export const searchIod = async (query: string, keywords: string[]) => {
|
||||
*/
|
||||
}
|
||||
|
||||
export const calculateTokenCount = function (str: string) {
|
||||
const byteArray = new TextEncoder().encode(str)
|
||||
return byteArray.length
|
||||
export const calculateTokenCount = function(str:string){
|
||||
const byteArray = new TextEncoder().encode(str);
|
||||
return byteArray.length;
|
||||
}
|
||||
|
||||
|
151
src/web/web.ts
@ -1,5 +1,4 @@
|
||||
import { defineConfig } from "wxt"
|
||||
import { defineRunnerConfig } from "wxt"
|
||||
import react from "@vitejs/plugin-react"
|
||||
import topLevelAwait from "vite-plugin-top-level-await"
|
||||
|
||||
@ -9,6 +8,7 @@ const chromeMV3Permissions = [
|
||||
"activeTab",
|
||||
"scripting",
|
||||
"declarativeNetRequest",
|
||||
"action",
|
||||
"unlimitedStorage",
|
||||
"contextMenus",
|
||||
"tts",
|
||||
@ -49,6 +49,7 @@ export default defineConfig({
|
||||
process.env.TARGET === "firefox" ? "entries-firefox" : "entries",
|
||||
srcDir: "src",
|
||||
outDir: "build",
|
||||
|
||||
manifest: {
|
||||
version: "1.5.0",
|
||||
name:
|
||||
|