Compare commits
No commits in common. "30aa0faaa17055e3f9e779419bb1a504d9abf194" and "ef0e315bdc544f2f76f4a43652a13a5bdb5bc9a4" have entirely different histories.
30aa0faaa1
...
ef0e315bdc
BIN
operation/1.png
Before Width: | Height: | Size: 659 KiB |
BIN
operation/2.png
Before Width: | Height: | Size: 763 KiB |
BIN
operation/3.png
Before Width: | Height: | Size: 941 KiB |
BIN
operation/4.png
Before Width: | Height: | Size: 610 KiB |
@ -1 +0,0 @@
|
||||
<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>
|
Before Width: | Height: | Size: 590 B |
@ -1 +0,0 @@
|
||||
<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>
|
Before Width: | Height: | Size: 601 B |
@ -1 +0,0 @@
|
||||
<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>
|
Before Width: | Height: | Size: 1.8 KiB |
@ -1 +0,0 @@
|
||||
<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>
|
Before Width: | Height: | Size: 1.4 KiB |
@ -1 +0,0 @@
|
||||
<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>
|
Before Width: | Height: | Size: 1.5 KiB |
@ -1 +0,0 @@
|
||||
<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>
|
Before Width: | Height: | Size: 1.7 KiB |
@ -1 +0,0 @@
|
||||
<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>
|
Before Width: | Height: | Size: 601 B |
@ -1 +0,0 @@
|
||||
<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>
|
Before Width: | Height: | Size: 2.2 KiB |
@ -1 +0,0 @@
|
||||
<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>
|
Before Width: | Height: | Size: 2.6 KiB |
@ -1,17 +1,16 @@
|
||||
{
|
||||
"projectTitle": "数联网科创智能体",
|
||||
"newChat": "新对话",
|
||||
"newChat": "新聊天",
|
||||
"selectAPrompt": "选择一个提示词",
|
||||
"githubRepository": "GitHub 仓库",
|
||||
"settings": "设置",
|
||||
"metering": "计量",
|
||||
"sidebarTitle": "对话历史",
|
||||
"sidebarTitle": "聊天历史",
|
||||
"error": "错误",
|
||||
"somethingWentWrong": "出现了错误",
|
||||
"validationSelectModel": "请选择一个模型以继续",
|
||||
"deleteHistoryConfirmation": "你确定要删除这个历史记录吗?",
|
||||
"editHistoryTitle": "输入一个新的标题",
|
||||
"temporaryChat": "临时对话",
|
||||
"temporaryChat": "临时聊天",
|
||||
"more": {
|
||||
"copy": {
|
||||
"group": "复制",
|
||||
|
@ -1,38 +0,0 @@
|
||||
import React from "react";
|
||||
import { Typography, Button } from "antd";
|
||||
import { AcademicCapIcon, ChevronRightIcon } from "@heroicons/react/24/outline";
|
||||
|
||||
const { Title } = Typography;
|
||||
|
||||
type Props = {
|
||||
Header: React.ReactNode;
|
||||
showButton?: boolean;
|
||||
onClick?: () => void;
|
||||
};
|
||||
|
||||
export const DataNavigation: React.FC<Props> = ({ Header, showButton = true, onClick }) => {
|
||||
return (
|
||||
<div className="flex items-center justify-between bg-white dark:bg-gray-800 rounded-lg mb-3">
|
||||
{/* 左侧部分 */}
|
||||
<div className="flex items-center">
|
||||
<Title
|
||||
level={4}
|
||||
style={{ marginBottom: 0, color: "#1F2937", fontSize: "16px" }}>
|
||||
{Header}
|
||||
</Title>
|
||||
</div>
|
||||
|
||||
{/* 右侧部分 */}
|
||||
{showButton && (
|
||||
<Button
|
||||
color="default"
|
||||
variant="link"
|
||||
style={{ gap: 4 }}
|
||||
onClick={onClick}>
|
||||
<span className="text-sm">更多</span>
|
||||
<ChevronRightIcon className="w-4 h-4" />
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
};
|
@ -12,7 +12,7 @@ import { preprocessLaTeX } from "@/utils/latex"
|
||||
|
||||
function Markdown({
|
||||
message,
|
||||
className = "prose-lg break-words dark:prose-invert prose-p:leading-relaxed prose-pre:p-0 dark:prose-dark"
|
||||
className = "prose break-words dark:prose-invert prose-p:leading-relaxed prose-pre:p-0 dark:prose-dark"
|
||||
}: {
|
||||
message: string
|
||||
className?: string
|
||||
|
@ -1,175 +0,0 @@
|
||||
import React from "react"
|
||||
import { DataNavigation } from "@/components/Common/DataNavigation.tsx"
|
||||
import { Card, Drawer, List } from "antd"
|
||||
import { useCallback, useState } from "react"
|
||||
|
||||
export const PlaygroundData = () => {
|
||||
// 模拟数据
|
||||
const data: {
|
||||
title: string
|
||||
description: string
|
||||
time: string
|
||||
metadata?: string
|
||||
}[] = [
|
||||
{
|
||||
title: "2019-2024年黄海清浅海域中河湖代数生物物种数据集",
|
||||
description:
|
||||
"数字对象标识: CSTR:13452.11.01.11.2021.242 国家海洋科学数据中心",
|
||||
time: "包括2019年8月,2021年8月和2024年6月",
|
||||
metadata: "热 榜 第"
|
||||
},
|
||||
{
|
||||
title: "祁连山老虎沟大本营10米气象每日值数据集(V1.0)(2018-2023)",
|
||||
description:
|
||||
"中国科学院西北生态环境资源研究院,2021年8月3日发布,2021年8月3日20:48更新",
|
||||
time: "包括2019年8月,2021年8月和2024年6月",
|
||||
metadata: "热 榜 第"
|
||||
},
|
||||
{
|
||||
title: "李嘉图为研究老虎沟大本营2014-2018年...",
|
||||
description:
|
||||
"中国科学院西北生态环境资源研究院,2021年8月3日发布,2021年8月3日20:48更新",
|
||||
time: "包括2019年8月,2021年8月和2024年6月",
|
||||
metadata: "热 榜 第"
|
||||
},
|
||||
{
|
||||
title: "青海玉树B1区俄日矿勘探数据2017-2023",
|
||||
description:
|
||||
"数字中国集团,CSTR:3260.11.1528414774204895456,DT2023年地质勘探补充调查",
|
||||
time: "包括2019年8月,2021年8月和2024年6月",
|
||||
metadata: "热 榜 第"
|
||||
}
|
||||
]
|
||||
|
||||
for (let i = 0; i < 10; i++) {
|
||||
data.push({
|
||||
title: "中国资源环境网",
|
||||
description: "中国资源环境网,2021年8月3日发布,2021年8月3日20:48更新",
|
||||
time: "包括2019年8月,2021年8月和2024年6月"
|
||||
})
|
||||
}
|
||||
|
||||
const [open, setOpen] = useState(false)
|
||||
|
||||
const showDrawer = () => {
|
||||
setOpen(true)
|
||||
}
|
||||
|
||||
const onClose = () => {
|
||||
setOpen(false)
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="flex flex-col h-full overflow-y-hidden">
|
||||
{/* 数据导航 */}
|
||||
<DataNavigation
|
||||
Header={
|
||||
<div className="flex items-center text-[#1d4eD8] gap-1">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
className="styles__StyledSVGIconPathComponent-sc-i3aj97-0 bxMexi svg-icon-path-icon"
|
||||
viewBox="0 0 32 32"
|
||||
width="20"
|
||||
height="20">
|
||||
<defs></defs>
|
||||
<g>
|
||||
<circle cx="22" cy="24" r="2" fill="rgb(29, 78, 216)"></circle>
|
||||
<path
|
||||
fill="rgb(29, 78, 216)"
|
||||
d="M29.777 23.479A8.64 8.64 0 0 0 22 18a8.64 8.64 0 0 0-7.777 5.479L14 24l.223.522A8.64 8.64 0 0 0 22 30a8.64 8.64 0 0 0 7.777-5.478L30 24zM22 28a4 4 0 1 1 4-4a4.005 4.005 0 0 1-4 4M7 17h5v2H7zm0-5h12v2H7zm0-5h12v2H7z"></path>
|
||||
<path
|
||||
fill="rgb(29, 78, 216)"
|
||||
d="M22 2H4a2.006 2.006 0 0 0-2 2v24a2.006 2.006 0 0 0 2 2h8v-2H4V4h18v11h2V4a2.006 2.006 0 0 0-2-2"></path>
|
||||
</g>
|
||||
</svg>
|
||||
相关数据
|
||||
</div>
|
||||
}
|
||||
onClick={showDrawer}
|
||||
/>
|
||||
|
||||
{/* 数据列表 */}
|
||||
<div className="space-y-1.5 flex-1 overflow-y-auto">
|
||||
{data.slice(0, 3).map((item, index) => (
|
||||
<Card key={index} hoverable className="[&>*:first-child]:!p-3 h-[148px]">
|
||||
<div className="flex flex-col gap-0.5">
|
||||
<h3 className="text-sm font-medium text-gray-900 line-clamp-2">
|
||||
{item.title}
|
||||
</h3>
|
||||
<p className="text-xs text-gray-500 line-clamp-2">
|
||||
{item.description}
|
||||
</p>
|
||||
<p className="text-gray-700 truncate">{item.time}</p>
|
||||
{item.metadata && (
|
||||
<div className="text-green-500 text-xs px-2 py-1 rounded-full flex items-center gap-1">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
className="styles__StyledSVGIconPathComponent-sc-i3aj97-0 bxMexi svg-icon-path-icon"
|
||||
viewBox="0 0 24 24"
|
||||
width="12"
|
||||
height="12">
|
||||
<defs></defs>
|
||||
<g>
|
||||
<path
|
||||
fill="rgb(34, 197, 94)"
|
||||
d="m16 11.78l4.24-7.33l1.73 1l-5.23 9.05l-6.51-3.75L5.46 19H22v2H2V3h2v14.54L9.5 8z"></path>
|
||||
</g>
|
||||
</svg>
|
||||
{item.metadata}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</Card>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* 抽屉 */}
|
||||
<Drawer
|
||||
title="相关数据"
|
||||
closable={{ "aria-label": "Close Button" }}
|
||||
onClose={onClose}
|
||||
open={open}
|
||||
width={600}>
|
||||
<List
|
||||
itemLayout="vertical"
|
||||
dataSource={data}
|
||||
renderItem={(item, index) => (
|
||||
<List.Item>
|
||||
<List.Item.Meta
|
||||
title={
|
||||
<h3 className="text-sm font-medium text-gray-900">
|
||||
{item.title}
|
||||
</h3>
|
||||
}
|
||||
description={
|
||||
<div className="space-y-1">
|
||||
<p className="text-xs text-gray-500">{item.description}</p>
|
||||
<p className="text-gray-700">{item.time}</p>
|
||||
{item.metadata && (
|
||||
<div className="text-green-500 text-xs px-2 py-1 rounded-full flex items-center gap-1">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
className="styles__StyledSVGIconPathComponent-sc-i3aj97-0 bxMexi svg-icon-path-icon"
|
||||
viewBox="0 0 24 24"
|
||||
width="12"
|
||||
height="12">
|
||||
<defs></defs>
|
||||
<g>
|
||||
<path
|
||||
fill="rgb(34, 197, 94)"
|
||||
d="m16 11.78l4.24-7.33l1.73 1l-5.23 9.05l-6.51-3.75L5.46 19H22v2H2V3h2v14.54L9.5 8z"></path>
|
||||
</g>
|
||||
</svg>
|
||||
{item.metadata}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
}
|
||||
/>
|
||||
</List.Item>
|
||||
)}
|
||||
/>
|
||||
</Drawer>
|
||||
</div>
|
||||
)
|
||||
}
|
@ -1,84 +0,0 @@
|
||||
import { Sidebar } from "@/components/Option/Sidebar.tsx"
|
||||
import React, { useContext, useState } from "react"
|
||||
import { useMessageOption } from "@/hooks/useMessageOption.tsx"
|
||||
import { useStoreChatModelSettings } from "@/store/model.tsx"
|
||||
import { Card, Tooltip } from "antd"
|
||||
import { PageAssitDatabase } from "@/db"
|
||||
import { EraserIcon } from "lucide-react"
|
||||
import { useTranslation } from "react-i18next"
|
||||
import { useQueryClient } from "@tanstack/react-query"
|
||||
import { HistoryContext } from "@/components/Layouts/Layout.tsx"
|
||||
|
||||
export const PlaygroundHistory = () => {
|
||||
const { setSystemPrompt } = useStoreChatModelSettings()
|
||||
|
||||
const { show, setShow } = useContext(HistoryContext)
|
||||
|
||||
const {
|
||||
setMessages,
|
||||
setHistory,
|
||||
setHistoryId,
|
||||
historyId,
|
||||
clearChat,
|
||||
setSelectedModel,
|
||||
temporaryChat,
|
||||
setSelectedSystemPrompt
|
||||
} = useMessageOption()
|
||||
|
||||
const { t } = useTranslation(["option", "common", "settings"])
|
||||
|
||||
const queryClient = useQueryClient()
|
||||
|
||||
return (
|
||||
<Card
|
||||
className={`flex flex-col [&>:nth-child(2)]:flex-1 [&>:nth-child(2)]:overflow-y-auto w-[300px] h-full pt-16 pb-5 transition-all duration-300 ease-in-out transform ${
|
||||
show
|
||||
? 'opacity-100 translate-x-0'
|
||||
: 'opacity-0 -translate-x-full absolute'
|
||||
}`}
|
||||
style={{ paddingTop: "4rem" }}
|
||||
title={
|
||||
<div className="flex items-center justify-between w-full">
|
||||
{t("sidebarTitle")}
|
||||
<Tooltip
|
||||
title={t("settings:generalSettings.system.deleteChatHistory.label")}
|
||||
placement="right">
|
||||
<button
|
||||
onClick={async () => {
|
||||
const confirm = window.confirm(
|
||||
t("settings:generalSettings.system.deleteChatHistory.confirm")
|
||||
)
|
||||
|
||||
if (confirm) {
|
||||
const db = new PageAssitDatabase()
|
||||
await db.deleteAllChatHistory()
|
||||
await queryClient.invalidateQueries({
|
||||
queryKey: ["fetchChatHistory"]
|
||||
})
|
||||
clearChat()
|
||||
}
|
||||
}}
|
||||
className="text-gray-600 hover:text-gray-800 dark:text-gray-300 dark:hover:text-gray-100">
|
||||
<EraserIcon className="size-5" />
|
||||
</button>
|
||||
</Tooltip>
|
||||
</div>
|
||||
}>
|
||||
<div className="overflow-y-auto">
|
||||
<Sidebar
|
||||
onClose={() => setShow(true)}
|
||||
setMessages={setMessages}
|
||||
setHistory={setHistory}
|
||||
setHistoryId={setHistoryId}
|
||||
setSelectedModel={setSelectedModel}
|
||||
setSelectedSystemPrompt={setSelectedSystemPrompt}
|
||||
clearChat={clearChat}
|
||||
historyId={historyId}
|
||||
setSystemPrompt={setSystemPrompt}
|
||||
temporaryChat={temporaryChat}
|
||||
history={history}
|
||||
/>
|
||||
</div>
|
||||
</Card>
|
||||
)
|
||||
}
|
@ -1,95 +0,0 @@
|
||||
import React from 'react';
|
||||
|
||||
const SuccessIcon = () => {
|
||||
return (<svg
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
className="w-full h-full text-green-500">
|
||||
<path
|
||||
d="M9 12L11 14L15 10M21 12C21 16.9706 16.9706 21 12 21C7.02944 21 3 16.9706 3 12C3 7.02944 7.02944 3 12 3C16.9706 3 21 7.02944 21 12Z"
|
||||
stroke="currentColor"
|
||||
strokeWidth="2"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
</svg>)
|
||||
}
|
||||
|
||||
const LoadingIcon = () => {
|
||||
return (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
className="styles__StyledSVGIconPathComponent-sc-i3aj97-0 bxMexi svg-icon-path-icon"
|
||||
viewBox="0 0 24 24"
|
||||
width="18"
|
||||
height="18">
|
||||
<defs></defs>
|
||||
<g>
|
||||
<path
|
||||
fill="rgb(59, 130, 246)"
|
||||
d="M13 2.03v2.02c4.39.54 7.5 4.53 6.96 8.92c-.46 3.64-3.32 6.53-6.96 6.96v2c5.5-.55 9.5-5.43 8.95-10.93c-.45-4.75-4.22-8.5-8.95-8.97m-2 .03c-1.95.19-3.81.94-5.33 2.2L7.1 5.74c1.12-.9 2.47-1.48 3.9-1.68zM4.26 5.67A9.9 9.9 0 0 0 2.05 11h2c.19-1.42.75-2.77 1.64-3.9zM2.06 13c.2 1.96.97 3.81 2.21 5.33l1.42-1.43A8 8 0 0 1 4.06 13zm5.04 5.37l-1.43 1.37A10 10 0 0 0 11 22v-2a8 8 0 0 1-3.9-1.63M12.5 7v5.25l4.5 2.67l-.75 1.23L11 13V7z"></path>
|
||||
</g>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
export const PlaygroundIodRelevant: React.FC = () => {
|
||||
const data = [
|
||||
{
|
||||
title: "已在29个科学数据中心的50万个科学数据集中进行搜索",
|
||||
description: "已发现4个数据集",
|
||||
status: "success"
|
||||
},
|
||||
{
|
||||
title: "已在100万篇论文、2800个科创场景中进行搜索",
|
||||
description: "已发现4个数据集",
|
||||
status: "success"
|
||||
},
|
||||
{
|
||||
title: "正在1000位智库专家、12万个创新机构中进行搜索",
|
||||
status: "loading"
|
||||
},
|
||||
]
|
||||
|
||||
for (let i = 0; i < 10; i++) {
|
||||
data.push({
|
||||
title: "已在29个科学数据中心的50万个科学数据集中进行搜索" + i,
|
||||
description: "已发现4个数据集",
|
||||
status: "success"
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
return (
|
||||
<div className="flex flex-col h-full">
|
||||
{/* Header */}
|
||||
<div className="flex justify-between items-center mb-4">
|
||||
<h2 className="text-lg font-semibold text-gray-900">
|
||||
数联网搜索相关内
|
||||
</h2>
|
||||
<span className="text-sm text-blue-600 font-medium">{data.length}个结果</span>
|
||||
</div>
|
||||
|
||||
{/* Content */}
|
||||
<div className="space-y-3 flex-1 overflow-y-auto">
|
||||
{
|
||||
data.map((item, index) => (
|
||||
<div className="flex items-start space-x-3">
|
||||
<div className="w-5 h-5 mt-1 flex-shrink-0">
|
||||
{item.status === "success" ? <SuccessIcon /> : <LoadingIcon />}
|
||||
</div>
|
||||
<div className="flex-1">
|
||||
<p className="text-sm text-gray-700">
|
||||
{item.title}
|
||||
</p>
|
||||
{item.description && <p className="text-xs text-gray-500 mt-1">{item.description}</p>}
|
||||
</div>
|
||||
</div>
|
||||
))
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
@ -58,7 +58,7 @@ export const PlaygroundMessage = (props: Props) => {
|
||||
const { t } = useTranslation("common")
|
||||
const { cancel, isSpeaking, speak } = useTTS()
|
||||
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="group relative flex w-full max-w-3xl flex-col items-end justify-center pb-2 md:px-4 lg:w-4/5 text-gray-800 dark:text-gray-100">
|
||||
{/* <div className="text-base md:max-w-2xl lg:max-w-xl xl:max-w-3xl flex lg:px-0 m-auto w-full"> */}
|
||||
<div className="flex flex-row gap-4 md:gap-6 my-2 m-auto w-full">
|
||||
<div className="w-8 flex flex-col relative items-end">
|
||||
@ -138,7 +138,7 @@ export const PlaygroundMessage = (props: Props) => {
|
||||
</>
|
||||
) : (
|
||||
<p
|
||||
className={`prose-lg dark:prose-invert whitespace-pre-line prose-p:leading-relaxed prose-pre:p-0 dark:prose-dark ${
|
||||
className={`prose dark:prose-invert whitespace-pre-line prose-p:leading-relaxed prose-pre:p-0 dark:prose-dark ${
|
||||
props.message_type &&
|
||||
"italic text-gray-500 dark:text-gray-400 text-sm"
|
||||
}`}>
|
||||
|
@ -1,142 +0,0 @@
|
||||
import React, { useState } from "react"
|
||||
import { DataNavigation } from "@/components/Common/DataNavigation.tsx"
|
||||
import { Card, Drawer, List } from "antd"
|
||||
|
||||
export const PlaygroundScene = () => {
|
||||
// 模拟数据
|
||||
const data = [
|
||||
{
|
||||
title: "绿色化工工艺项目",
|
||||
description:
|
||||
"基于生物基原料,采用repeal2.0可降解材料技术,开发新型环保材料。",
|
||||
demander: "奥赛康药业 供方:美国Propella公司"
|
||||
},
|
||||
{
|
||||
title: "智能农业解决方案",
|
||||
description: "利用物联网技术,实现精准农业管理,提高农作物产量。",
|
||||
demander: "奥赛康药业 供方:美国Propella公司"
|
||||
},
|
||||
{
|
||||
title: "新能源汽车电池技术",
|
||||
description: "研发高能量密度、长寿命的新型电池材料,推动电动汽车发展。",
|
||||
demander: "奥赛康药业 供方:美国Propella公司"
|
||||
},
|
||||
{
|
||||
title: "碳捕集与封存技术",
|
||||
description: "开发高效的碳捕集技术,减少工业排放,助力碳中和目标。",
|
||||
demander: "奥赛康药业 供方:美国Propella公司"
|
||||
}
|
||||
]
|
||||
|
||||
for (let i = 0; i < 10; i++) {
|
||||
data.push({
|
||||
title: "开发新型电池材料",
|
||||
description: "研发高能量密度、长寿命的新型电池材料,推动电动汽车发展。",
|
||||
demander: "奥赛康药业 供方:美国Propella公司"
|
||||
})
|
||||
}
|
||||
|
||||
const [open, setOpen] = useState(false)
|
||||
|
||||
const showDrawer = () => {
|
||||
setOpen(true)
|
||||
}
|
||||
|
||||
const onClose = () => {
|
||||
setOpen(false)
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="h-full overflow-y-hidden flex flex-col">
|
||||
{/* 数据导航 */}
|
||||
<DataNavigation
|
||||
Header={
|
||||
<div className="flex items-center text-[#15803d] gap-1">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
className="styles__StyledSVGIconPathComponent-sc-i3aj97-0 bxMexi svg-icon-path-icon"
|
||||
viewBox="0 0 32 32"
|
||||
width="20"
|
||||
height="20">
|
||||
<defs></defs>
|
||||
<g>
|
||||
<path
|
||||
fill="rgb(21, 128, 61)"
|
||||
d="M16 18H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h10a2 2 0 0 1 2 2v10a2 2 0 0 1-2 2M6 6v10h10V6zm20 6v4h-4v-4zm0-2h-4a2 2 0 0 0-2 2v4a2 2 0 0 0 2 2h4a2 2 0 0 0 2-2v-4a2 2 0 0 0-2-2m0 12v4h-4v-4zm0-2h-4a2 2 0 0 0-2 2v4a2 2 0 0 0 2 2h4a2 2 0 0 0 2-2v-4a2 2 0 0 0-2-2m-10 2v4h-4v-4zm0-2h-4a2 2 0 0 0-2 2v4a2 2 0 0 0 2 2h4a2 2 0 0 0 2-2v-4a2 2 0 0 0-2-2"></path>
|
||||
</g>
|
||||
</svg>
|
||||
相关场景
|
||||
</div>
|
||||
}
|
||||
onClick={showDrawer}
|
||||
/>
|
||||
|
||||
{/* 场景列表 */}
|
||||
<div className="space-y-1.5 flex-1 overflow-y-auto">
|
||||
{data.slice(0,3).map((item, index) => (
|
||||
<Card key={index} hoverable className="[&>*:first-child]:!p-4 h-[148px]" >
|
||||
<div className="flex flex-col gap-0.5">
|
||||
<h3 className="text-sm font-medium text-gray-900 line-clamp-2">
|
||||
{item.title}
|
||||
</h3>
|
||||
<p className="flex items-center gap-1.5">
|
||||
<span className="inline-block bg-blue-100 text-green-800 text-xs px-2 py-1 rounded-full">
|
||||
技 术 应
|
||||
</span>
|
||||
<span className="inline-block bg-blue-100 text-blue-800 text-xs px-2 py-1 rounded-full">
|
||||
制 造
|
||||
</span>
|
||||
</p>
|
||||
<p className="text-xs text-gray-500 truncate">{item.demander}</p>
|
||||
<span className="text-gray-700 line-clamp-2">
|
||||
{item.description}
|
||||
</span>
|
||||
</div>
|
||||
</Card>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* 抽屉 */}
|
||||
<Drawer
|
||||
title="相关场景"
|
||||
closable={{ "aria-label": "Close Button" }}
|
||||
onClose={onClose}
|
||||
open={open}
|
||||
width={600}>
|
||||
<List
|
||||
itemLayout="vertical"
|
||||
dataSource={data}
|
||||
renderItem={(item, index) => (
|
||||
<List.Item>
|
||||
<List.Item.Meta
|
||||
title={
|
||||
<h3 className="text-sm font-medium text-gray-900">
|
||||
{item.title}
|
||||
</h3>
|
||||
}
|
||||
description={
|
||||
<div className="space-y-1">
|
||||
<p className="flex items-center gap-1.5">
|
||||
<span className="inline-block bg-blue-100 text-green-800 text-xs px-2 py-1 rounded-full">
|
||||
技 术 应
|
||||
</span>
|
||||
<span className="inline-block bg-blue-100 text-blue-800 text-xs px-2 py-1 rounded-full">
|
||||
制 造
|
||||
</span>
|
||||
</p>
|
||||
<p className="text-xs text-gray-500">
|
||||
{item.demander}
|
||||
</p>
|
||||
<span className="text-gray-700">
|
||||
{item.description}
|
||||
</span>
|
||||
</div>
|
||||
}
|
||||
/>
|
||||
</List.Item>
|
||||
)}
|
||||
/>
|
||||
</Drawer>
|
||||
</div>
|
||||
)
|
||||
}
|
@ -1,136 +0,0 @@
|
||||
import React, { useState } from "react"
|
||||
import { DataNavigation } from "@/components/Common/DataNavigation.tsx"
|
||||
import { Card, Drawer, List } from "antd"
|
||||
|
||||
export const PlaygroundTeam = () => {
|
||||
// 模拟数据
|
||||
const data = [
|
||||
{
|
||||
title: "绿色化工工艺项目",
|
||||
description:
|
||||
"基于生物基原料,采用repeal2.0可降解材料技术,开发新型环保材料。",
|
||||
demander: "奥赛康药业 供方:美国Propella公司"
|
||||
},
|
||||
{
|
||||
title: "智能农业解决方案",
|
||||
description: "利用物联网技术,实现精准农业管理,提高农作物产量。",
|
||||
demander: "奥赛康药业 供方:美国Propella公司"
|
||||
},
|
||||
{
|
||||
title: "新能源汽车电池技术",
|
||||
description: "研发高能量密度、长寿命的新型电池材料,推动电动汽车发展。",
|
||||
demander: "奥赛康药业 供方:美国Propella公司"
|
||||
},
|
||||
{
|
||||
title: "碳捕集与封存技术",
|
||||
description: "开发高效的碳捕集技术,减少工业排放,助力碳中和目标。",
|
||||
demander: "奥赛康药业 供方:美国Propella公司"
|
||||
}
|
||||
]
|
||||
|
||||
for (let i = 0; i < 10; i++) {
|
||||
data.push({
|
||||
title: "开发新型电池材料",
|
||||
description: "研发高能量密度、长寿命的新型电池材料,推动电动汽车发展。",
|
||||
demander: "奥赛康药业 供方:美国Propella公司"
|
||||
})
|
||||
}
|
||||
|
||||
const [open, setOpen] = useState(false)
|
||||
|
||||
const showDrawer = () => {
|
||||
setOpen(true)
|
||||
}
|
||||
|
||||
const onClose = () => {
|
||||
setOpen(false)
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="h-full overflow-y-hidden flex flex-col">
|
||||
{/* 数据导航 */}
|
||||
<DataNavigation
|
||||
Header={
|
||||
<div className="flex items-center text-[#15803d] gap-1">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
className="styles__StyledSVGIconPathComponent-sc-i3aj97-0 bxMexi svg-icon-path-icon"
|
||||
viewBox="0 0 32 32"
|
||||
width="20"
|
||||
height="20">
|
||||
<defs></defs>
|
||||
<g>
|
||||
<path
|
||||
fill="rgb(21, 128, 61)"
|
||||
d="M16 18H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h10a2 2 0 0 1 2 2v10a2 2 0 0 1-2 2M6 6v10h10V6zm20 6v4h-4v-4zm0-2h-4a2 2 0 0 0-2 2v4a2 2 0 0 0 2 2h4a2 2 0 0 0 2-2v-4a2 2 0 0 0-2-2m0 12v4h-4v-4zm0-2h-4a2 2 0 0 0-2 2v4a2 2 0 0 0 2 2h4a2 2 0 0 0 2-2v-4a2 2 0 0 0-2-2m-10 2v4h-4v-4zm0-2h-4a2 2 0 0 0-2 2v4a2 2 0 0 0 2 2h4a2 2 0 0 0 2-2v-4a2 2 0 0 0-2-2"></path>
|
||||
</g>
|
||||
</svg>
|
||||
相关团队
|
||||
</div>
|
||||
}
|
||||
onClick={showDrawer}
|
||||
/>
|
||||
|
||||
{/* 场景列表 */}
|
||||
<div className="grid grid-cols-2 gap-3">
|
||||
{data.slice(0,2).map((item, index) => (
|
||||
<Card key={index} hoverable className="[&>*:first-child]:!p-3" >
|
||||
<div className="flex flex-col gap-0.5">
|
||||
<h3 className="text-sm font-medium text-gray-900 line-clamp-2">
|
||||
{item.title}
|
||||
</h3>
|
||||
<p className="flex items-center gap-1.5">
|
||||
<span className="inline-block bg-blue-100 text-green-800 text-xs px-2 py-1 rounded-full">
|
||||
技 术 应
|
||||
</span>
|
||||
<span className="inline-block bg-blue-100 text-blue-800 text-xs px-2 py-1 rounded-full">
|
||||
制 造
|
||||
</span>
|
||||
</p>
|
||||
<p className="text-xs text-gray-500 line-clamp-2">{item.description}</p>
|
||||
</div>
|
||||
</Card>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* 抽屉 */}
|
||||
<Drawer
|
||||
title="相关团队"
|
||||
closable={{ "aria-label": "Close Button" }}
|
||||
onClose={onClose}
|
||||
open={open}
|
||||
width={600}>
|
||||
<List
|
||||
itemLayout="vertical"
|
||||
dataSource={data}
|
||||
renderItem={(item, index) => (
|
||||
<List.Item>
|
||||
<List.Item.Meta
|
||||
title={
|
||||
<h3 className="text-sm font-medium text-gray-900">
|
||||
{item.title}
|
||||
</h3>
|
||||
}
|
||||
description={
|
||||
<div className="space-y-1">
|
||||
<p className="flex items-center gap-1.5">
|
||||
<span className="inline-block bg-blue-100 text-green-800 text-xs px-2 py-1 rounded-full">
|
||||
技 术 应
|
||||
</span>
|
||||
<span className="inline-block bg-blue-100 text-blue-800 text-xs px-2 py-1 rounded-full">
|
||||
制 造
|
||||
</span>
|
||||
</p>
|
||||
<p className="text-xs text-gray-500">
|
||||
{item.description}
|
||||
</p>
|
||||
</div>
|
||||
}
|
||||
/>
|
||||
</List.Item>
|
||||
)}
|
||||
/>
|
||||
</Drawer>
|
||||
</div>
|
||||
)
|
||||
}
|
@ -1,46 +0,0 @@
|
||||
import { DataNavigation } from "@/components/Common/DataNavigation.tsx"
|
||||
import { Card, Descriptions, DescriptionsProps, Drawer, List, Spin } from "antd"
|
||||
import { useCallback, useMemo, useState } from "react"
|
||||
import { useMessageOption } from "@/hooks/useMessageOption.tsx"
|
||||
import { useStoreMessageOption } from "@/store/option.tsx"
|
||||
|
||||
export const PlaygroundTokenStatistics = () => {
|
||||
const { currentMeteringEntry } = useStoreMessageOption()
|
||||
|
||||
const items = useMemo<DescriptionsProps["items"]>(() => {
|
||||
const { data } = currentMeteringEntry
|
||||
return [
|
||||
// {
|
||||
// key: "relatedDataCount",
|
||||
// label: "关联数据个数",
|
||||
// children: data.relatedDataCount
|
||||
// },
|
||||
{
|
||||
key: "iodTokenCount",
|
||||
label: "数联网引用token总数",
|
||||
children: data.iodTokenCount ?? 0
|
||||
},
|
||||
{
|
||||
key: "modelInputTokenCount",
|
||||
label: "大模型输入token数量",
|
||||
children: data.modelInputTokenCount ?? 0
|
||||
},
|
||||
{
|
||||
key: "modelOutputTokenCount",
|
||||
label: "大模型输出token数量",
|
||||
children: data.modelOutputTokenCount ?? 0
|
||||
}
|
||||
]
|
||||
}, [currentMeteringEntry])
|
||||
|
||||
return (
|
||||
<Card
|
||||
style={{ marginBottom: "1rem" }}
|
||||
className="h-full"
|
||||
title={<DataNavigation title="Token统计" showButton={false} />}>
|
||||
<Spin spinning={currentMeteringEntry.loading}>
|
||||
<Descriptions layout="horizontal" items={items} column={2} />
|
||||
</Spin>
|
||||
</Card>
|
||||
)
|
||||
}
|
@ -24,24 +24,18 @@ import { ProviderIcons } from "../Common/ProviderIcon"
|
||||
import { NewChat } from "./NewChat"
|
||||
import { PageAssistSelect } from "../Select"
|
||||
import { MoreOptions } from "./MoreOptions"
|
||||
import { useContext } from "react"
|
||||
import { HistoryContext } from "@/components/Layouts/Layout.tsx"
|
||||
type Props = {
|
||||
sidebarOpen: boolean
|
||||
setSidebarOpen: () => void
|
||||
setSidebarOpen: (open: boolean) => void
|
||||
setOpenModelSettings: (open: boolean) => void
|
||||
}
|
||||
|
||||
export const Header: React.FC<Props> = ({
|
||||
setOpenModelSettings,
|
||||
setSidebarOpen,
|
||||
sidebarOpen
|
||||
setSidebarOpen
|
||||
}) => {
|
||||
const { t, i18n } = useTranslation(["option", "common"])
|
||||
const isRTL = i18n?.dir() === "rtl"
|
||||
|
||||
|
||||
|
||||
const [shareModeEnabled] = useStorage("shareMode", false)
|
||||
const [hideCurrentChatModelSettings] = useStorage(
|
||||
"hideCurrentChatModelSettings",
|
||||
@ -115,16 +109,10 @@ export const Header: React.FC<Props> = ({
|
||||
</NavLink>
|
||||
</div>
|
||||
)}
|
||||
<div style={{width: sidebarOpen ? "288px" : "205px"}} className="flex items-center justify-between transition-all duration-300 ease-in-out">
|
||||
<h2
|
||||
className="text-xl font-bold text-zinc-700 dark:text-zinc-300 mr-3"
|
||||
style={{ lineHeight: "0" }}>
|
||||
{t("projectTitle")}
|
||||
</h2>
|
||||
|
||||
<div>
|
||||
<button
|
||||
className="text-gray-500 dark:text-gray-400"
|
||||
onClick={() => setSidebarOpen()}>
|
||||
onClick={() => setSidebarOpen(true)}>
|
||||
<PanelLeftIcon className="w-6 h-6" />
|
||||
</button>
|
||||
</div>
|
||||
|
@ -1,50 +1,103 @@
|
||||
import React, { useCallback, useEffect, useState } from "react"
|
||||
import React, { useState } from "react"
|
||||
|
||||
import { Sidebar } from "../Option/Sidebar"
|
||||
import { Drawer, Tooltip } from "antd"
|
||||
|
||||
import { useTranslation } from "react-i18next"
|
||||
|
||||
import { CurrentChatModelSettings } from "../Common/Settings/CurrentChatModelSettings"
|
||||
import { Header } from "./Header"
|
||||
|
||||
interface History {
|
||||
show: boolean
|
||||
setShow: (show: boolean) => void
|
||||
}
|
||||
|
||||
export const HistoryContext = React.createContext<History>({
|
||||
show: true,
|
||||
setShow: () => {}
|
||||
})
|
||||
import { EraserIcon } from "lucide-react"
|
||||
import { PageAssitDatabase } from "@/db"
|
||||
import { useMessageOption } from "@/hooks/useMessageOption"
|
||||
import { useQueryClient } from "@tanstack/react-query"
|
||||
import { useStoreChatModelSettings } from "@/store/model"
|
||||
|
||||
export default function OptionLayout({
|
||||
children
|
||||
}: {
|
||||
children: React.ReactNode
|
||||
}) {
|
||||
const [showHistory, setShowHistory] = useState(true)
|
||||
const [sidebarOpen, setSidebarOpen] = useState(false)
|
||||
const { t } = useTranslation(["option", "common", "settings"])
|
||||
const [openModelSettings, setOpenModelSettings] = useState(false)
|
||||
const {
|
||||
setMessages,
|
||||
setHistory,
|
||||
setHistoryId,
|
||||
historyId,
|
||||
clearChat,
|
||||
setSelectedModel,
|
||||
temporaryChat,
|
||||
setSelectedSystemPrompt
|
||||
} = useMessageOption()
|
||||
|
||||
const historyContextValue = {
|
||||
show: showHistory,
|
||||
setShow: setShowHistory
|
||||
}
|
||||
|
||||
const useToggle = useCallback(() => {
|
||||
setShowHistory(!showHistory)
|
||||
}, [showHistory])
|
||||
const queryClient = useQueryClient()
|
||||
const { setSystemPrompt } = useStoreChatModelSettings()
|
||||
|
||||
return (
|
||||
<div className="flex h-full w-full">
|
||||
<main className="relative h-dvh w-full">
|
||||
<div className="relative z-10 w-full">
|
||||
<Header
|
||||
sidebarOpen={showHistory}
|
||||
setSidebarOpen={useToggle}
|
||||
setSidebarOpen={setSidebarOpen}
|
||||
setOpenModelSettings={setOpenModelSettings}
|
||||
/>
|
||||
</div>
|
||||
{/* <div className="relative flex h-full flex-col items-center"> */}
|
||||
<HistoryContext.Provider value={historyContextValue}>
|
||||
{children}
|
||||
</HistoryContext.Provider>
|
||||
{/* </div> */}
|
||||
<Drawer
|
||||
title={
|
||||
<div className="flex items-center justify-between">
|
||||
{t("sidebarTitle")}
|
||||
|
||||
<Tooltip
|
||||
title={t(
|
||||
"settings:generalSettings.system.deleteChatHistory.label"
|
||||
)}
|
||||
placement="right">
|
||||
<button
|
||||
onClick={async () => {
|
||||
const confirm = window.confirm(
|
||||
t(
|
||||
"settings:generalSettings.system.deleteChatHistory.confirm"
|
||||
)
|
||||
)
|
||||
|
||||
if (confirm) {
|
||||
const db = new PageAssitDatabase()
|
||||
await db.deleteAllChatHistory()
|
||||
await queryClient.invalidateQueries({
|
||||
queryKey: ["fetchChatHistory"]
|
||||
})
|
||||
clearChat()
|
||||
}
|
||||
}}
|
||||
className="text-gray-600 hover:text-gray-800 dark:text-gray-300 dark:hover:text-gray-100">
|
||||
<EraserIcon className="size-5" />
|
||||
</button>
|
||||
</Tooltip>
|
||||
</div>
|
||||
}
|
||||
placement="left"
|
||||
closeIcon={null}
|
||||
onClose={() => setSidebarOpen(false)}
|
||||
open={sidebarOpen}>
|
||||
<Sidebar
|
||||
onClose={() => setSidebarOpen(false)}
|
||||
setMessages={setMessages}
|
||||
setHistory={setHistory}
|
||||
setHistoryId={setHistoryId}
|
||||
setSelectedModel={setSelectedModel}
|
||||
setSelectedSystemPrompt={setSelectedSystemPrompt}
|
||||
clearChat={clearChat}
|
||||
historyId={historyId}
|
||||
setSystemPrompt={setSystemPrompt}
|
||||
temporaryChat={temporaryChat}
|
||||
history={history}
|
||||
/>
|
||||
</Drawer>
|
||||
|
||||
<CurrentChatModelSettings
|
||||
open={openModelSettings}
|
||||
|
@ -1,14 +1,8 @@
|
||||
import React from "react"
|
||||
|
||||
import { Card } from "antd"
|
||||
|
||||
import { PlaygroundForm } from "./PlaygroundForm"
|
||||
import { PlaygroundChat } from "./PlaygroundChat"
|
||||
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,
|
||||
formatToMessage,
|
||||
@ -19,11 +13,6 @@ import { getLastUsedChatSystemPrompt } from "@/services/model-settings"
|
||||
import { useStoreChatModelSettings } from "@/store/model"
|
||||
import { useSmartScroll } from "@/hooks/useSmartScroll"
|
||||
import { ChevronDown } from "lucide-react"
|
||||
import { PlaygroundTeam } from "@/components/Common/Playground/Team.tsx"
|
||||
import { PlaygroundTokenStatistics } from "@/components/Common/Playground/TokenStatistics.tsx"
|
||||
import { PlaygroundHistory } from "@/components/Common/Playground/History.tsx"
|
||||
import { PlaygroundIodRelevant } from "@/components/Common/Playground/IodRelevant.tsx"
|
||||
|
||||
|
||||
export const Playground = () => {
|
||||
const drop = React.useRef<HTMLDivElement>(null)
|
||||
@ -143,11 +132,9 @@ export const Playground = () => {
|
||||
return (
|
||||
<div
|
||||
ref={drop}
|
||||
className={`relative flex gap-3 h-full items-center ${
|
||||
className={`relative flex h-full flex-col items-center ${
|
||||
dropState === "dragging" ? "bg-gray-100 dark:bg-gray-800" : ""
|
||||
} bg-white dark:bg-[#171717]`}>
|
||||
<PlaygroundHistory />
|
||||
<div className="relative h-full flex-1 prose-lg flex justify-center [&>*]:max-w-[848px]">
|
||||
<div
|
||||
ref={containerRef}
|
||||
className="custom-scrollbar bg-bottom-mask-light dark:bg-bottom-mask-dark mask-bottom-fade will-change-mask flex h-full w-full flex-col items-center overflow-x-hidden overflow-y-auto px-5">
|
||||
@ -155,7 +142,7 @@ export const Playground = () => {
|
||||
</div>
|
||||
<div className="absolute bottom-0 w-full">
|
||||
{!isAtBottom && (
|
||||
<div className="absolute bottom-36 z-20 left-0 right-0 flex justify-center">
|
||||
<div className="fixed bottom-36 z-20 left-0 right-0 flex justify-center">
|
||||
<button
|
||||
onClick={scrollToBottom}
|
||||
className="bg-gray-50 shadow border border-gray-200 dark:border-none dark:bg-white/20 p-1.5 rounded-full pointer-events-auto">
|
||||
@ -166,20 +153,5 @@ export const Playground = () => {
|
||||
<PlaygroundForm dropedFile={dropedFile} />
|
||||
</div>
|
||||
</div>
|
||||
{messages.length && (
|
||||
<div className="w-1/4 h-full grid grid-rows-[auto_530px_165px] pt-16 pr-5 pb-0 border-l border-gray-200" style={{"paddingTop": "4rem"}}>
|
||||
<div className="w-full overflow-y-auto border-gray-200 border-b p-3">
|
||||
<PlaygroundIodRelevant />
|
||||
</div>
|
||||
<div className="w-full grid grid-cols-2 gap-3 custom-scrollbar border-gray-200 border-b p-3">
|
||||
<PlaygroundData />
|
||||
<PlaygroundScene />
|
||||
</div>
|
||||
<div className="w-full p-3 pb-0">
|
||||
<PlaygroundTeam />
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
@ -20,7 +20,7 @@ export const PlaygroundChat = () => {
|
||||
<>
|
||||
<div className="relative flex w-full flex-col items-center pt-16 pb-4">
|
||||
{messages.length === 0 && (
|
||||
<div className="mt-3 w-full">
|
||||
<div className="mt-32 w-full">
|
||||
<PlaygroundEmpty />
|
||||
</div>
|
||||
)}
|
||||
|
@ -1,113 +1,130 @@
|
||||
import { Card, Col, Row } from "antd"
|
||||
|
||||
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'
|
||||
import { useMessageOption } from "@/hooks/useMessageOption.tsx"
|
||||
import { useMutation, useQueryClient } from "@tanstack/react-query"
|
||||
|
||||
import { cleanUrl } from "@/libs/clean-url"
|
||||
import { useStorage } from "@plasmohq/storage/hook"
|
||||
import { useQuery } from "@tanstack/react-query"
|
||||
import { RotateCcw } from "lucide-react"
|
||||
import { useEffect, useState } from "react"
|
||||
import { Trans, useTranslation } from "react-i18next"
|
||||
import {
|
||||
getOllamaURL,
|
||||
isOllamaRunning,
|
||||
setOllamaURL as saveOllamaURL
|
||||
} from "~/services/ollama"
|
||||
|
||||
export const PlaygroundEmpty = () => {
|
||||
const [ollamaURL, setOllamaURL] = useState<string>("")
|
||||
const { t } = useTranslation(["playground", "common"])
|
||||
|
||||
const [checkOllamaStatus] = useStorage("checkOllamaStatus", true)
|
||||
|
||||
const {
|
||||
onSubmit,
|
||||
setMessages,
|
||||
setHistory,
|
||||
setHistoryId,
|
||||
historyId,
|
||||
clearChat,
|
||||
setSelectedModel,
|
||||
temporaryChat,
|
||||
setSelectedSystemPrompt
|
||||
} = useMessageOption()
|
||||
data: ollamaInfo,
|
||||
status: ollamaStatus,
|
||||
refetch,
|
||||
isRefetching
|
||||
} = useQuery({
|
||||
queryKey: ["ollamaStatus"],
|
||||
queryFn: async () => {
|
||||
const ollamaURL = await getOllamaURL()
|
||||
const isOk = await isOllamaRunning()
|
||||
|
||||
const queryClient = useQueryClient()
|
||||
|
||||
|
||||
const questions = [
|
||||
{
|
||||
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: <img src={ASvg} alt="Rocket" className="w-10 my-0" />,
|
||||
},
|
||||
{
|
||||
title: "如何加速新药申报和审批?",
|
||||
icon: <img src={BSvg} alt="Rocket" className="w-10 my-0" />,
|
||||
},
|
||||
{
|
||||
title: "如何研制与司美格鲁肽相似的新药?",
|
||||
icon: <img src={CSvg} alt="Rocket" className="w-10 my-0" />,
|
||||
},
|
||||
{
|
||||
title: "如何解决固态电池的成本和寿命难题?",
|
||||
icon: <img src={DSvg} alt="Rocket" className="w-10 my-0" />,
|
||||
},
|
||||
{
|
||||
title: "如何解决船舶制造中的材料腐蚀难题?",
|
||||
icon: <img src={ESvg} alt="Rocket" className="w-10 my-0" />,
|
||||
},
|
||||
{
|
||||
title: "如何解决船舶制造中流体模拟和建模优化难题?",
|
||||
icon: <img src={FSvg} alt="Rocket" className="w-10 my-0" />,
|
||||
},
|
||||
]
|
||||
|
||||
const { mutateAsync: sendMessage } = useMutation({
|
||||
mutationFn: onSubmit,
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: ["fetchChatHistory"]
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
function handleQuestion(message: string) {
|
||||
void sendMessage({message, image: ''})
|
||||
if (ollamaURL) {
|
||||
saveOllamaURL(ollamaURL)
|
||||
}
|
||||
|
||||
return {
|
||||
isOk,
|
||||
ollamaURL
|
||||
}
|
||||
},
|
||||
enabled: checkOllamaStatus
|
||||
})
|
||||
|
||||
useEffect(() => {
|
||||
if (ollamaInfo?.ollamaURL) {
|
||||
setOllamaURL(ollamaInfo.ollamaURL)
|
||||
}
|
||||
}, [ollamaInfo])
|
||||
|
||||
|
||||
if (!checkOllamaStatus) {
|
||||
return (
|
||||
<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 className="mx-auto sm:max-w-xl px-4 mt-10">
|
||||
<div className="rounded-lg justify-center items-center flex flex-col border p-8 bg-gray-50 dark:bg-[#262626] dark:border-gray-600">
|
||||
<h1 className="text-sm font-medium text-center text-gray-500 dark:text-gray-400 flex gap-3 items-center justify-center">
|
||||
<span >👋</span>
|
||||
<span className="text-gray-700 dark:text-gray-300">
|
||||
{t("welcome")}
|
||||
</span>
|
||||
</h1>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
return (
|
||||
<div className="mx-auto sm:max-w-xl px-4 mt-10">
|
||||
<div className="rounded-lg justify-center items-center flex flex-col border p-8 bg-gray-50 dark:bg-[#262626] dark:border-gray-600">
|
||||
{(ollamaStatus === "pending" || isRefetching) && (
|
||||
<div className="inline-flex items-center space-x-2">
|
||||
<div className="w-3 h-3 bg-blue-500 rounded-full animate-pulse"></div>
|
||||
<p className="dark:text-gray-400 text-gray-900">
|
||||
{t("ollamaState.searching")}
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
{!isRefetching && ollamaStatus === "success" ? (
|
||||
ollamaInfo.isOk ? (
|
||||
<div className="inline-flex items-center space-x-2">
|
||||
<div className="w-3 h-3 bg-green-500 rounded-full animate-pulse"></div>
|
||||
<p className="dark:text-gray-400 text-gray-900">
|
||||
{t("ollamaState.running")}
|
||||
</p>
|
||||
</div>
|
||||
) : (
|
||||
<div className="flex flex-col space-y-2 justify-center items-center">
|
||||
<div className="inline-flex space-x-2">
|
||||
<div className="w-3 h-3 bg-red-500 rounded-full animate-pulse"></div>
|
||||
<p className="dark:text-gray-400 text-gray-900">
|
||||
{t("ollamaState.notRunning")}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<input
|
||||
className="bg-gray-100 dark:bg-[#262626] dark:text-gray-100 rounded-md px-4 py-2 mt-2 w-full"
|
||||
type="url"
|
||||
value={ollamaURL}
|
||||
onChange={(e) => setOllamaURL(e.target.value)}
|
||||
/>
|
||||
|
||||
<button
|
||||
onClick={() => {
|
||||
saveOllamaURL(ollamaURL)
|
||||
refetch()
|
||||
}}
|
||||
className="inline-flex mt-4 items-center rounded-md border border-transparent bg-black px-2 py-2 text-sm font-medium leading-4 text-white shadow-sm hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2 dark:bg-white dark:text-gray-800 dark:hover:bg-gray-100 dark:focus:ring-gray-500 dark:focus:ring-offset-gray-100 disabled:opacity-50 ">
|
||||
<RotateCcw className="h-4 w-4 mr-3" />
|
||||
{t("common:retry")}
|
||||
</button>
|
||||
|
||||
{ollamaURL &&
|
||||
cleanUrl(ollamaURL) !== "http://127.0.0.1:11434" && (
|
||||
<p className="text-xs text-gray-500 dark:text-gray-400 mb-4 text-center">
|
||||
<Trans
|
||||
i18nKey="playground:ollamaState.connectionError"
|
||||
components={{
|
||||
anchor: (
|
||||
<a
|
||||
href="https://github.com/n4ze3m/page-assist/blob/main/docs/connection-issue.md"
|
||||
target="__blank"
|
||||
className="text-blue-600 dark:text-blue-400"></a>
|
||||
)
|
||||
}}
|
||||
/>
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
) : null}
|
||||
</div>
|
||||
|
||||
{/* 卡片网格布局 */}
|
||||
<Row gutter={[16, 16]} className="w-full">
|
||||
{questions.map((item, index) => (
|
||||
<Col key={index} xs={24} sm={12} md={8}>
|
||||
<Card
|
||||
hoverable
|
||||
style={{backgroundColor: "#f3f4f6"}}
|
||||
className="border border-gray-200 rounded-lg shadow-sm hover:shadow-md transition-shadow duration-200 cursor-pointer"
|
||||
onClick={() => handleQuestion(item.title)}
|
||||
>
|
||||
<div className="flex items-center">
|
||||
<div className="text-blue-500 mr-2">{item.icon}</div>
|
||||
<div className="font-medium text-sm text-gray-800">{item.title}</div>
|
||||
</div>
|
||||
</Card>
|
||||
</Col>
|
||||
))}
|
||||
</Row>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
@ -207,11 +207,11 @@ export const PlaygroundForm = ({ dropedFile }: Props) => {
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="flex w-full flex-col items-center p-2 px-5 pt-1 pb-4">
|
||||
<div className="flex w-full flex-col items-center p-2 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="relative flex w-full flex-row justify-center gap-2 lg:w-4/5">
|
||||
<div
|
||||
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
|
||||
className={` bg-neutral-50 dark:bg-[#262626] relative w-full max-w-[48rem] p-1 backdrop-blur-lg duration-100 border border-gray-300 rounded-xl dark:border-gray-600
|
||||
${temporaryChat ? "!bg-gray-200 dark:!bg-black " : ""}
|
||||
`}>
|
||||
<div
|
||||
|
@ -60,7 +60,6 @@ export const useMessageOption = () => {
|
||||
setHistory,
|
||||
meteringEntries,
|
||||
setMeteringEntries,
|
||||
setCurrentMeteringEntry,
|
||||
setStreaming,
|
||||
streaming,
|
||||
setIsFirstMessage,
|
||||
@ -201,11 +200,6 @@ export const useMessageOption = () => {
|
||||
date: new Date().getTime()
|
||||
} as MeteringEntry
|
||||
|
||||
setCurrentMeteringEntry({
|
||||
loading: true,
|
||||
data: meter
|
||||
})
|
||||
|
||||
if (!isRegenerate) {
|
||||
newMessage = [
|
||||
...messages,
|
||||
@ -506,7 +500,7 @@ export const useMessageOption = () => {
|
||||
|
||||
// Save metering entry
|
||||
const { cot, content } = responseResolver(fullText)
|
||||
const currentMeteringEntry = {
|
||||
const _meteringEntries = [{
|
||||
...meter,
|
||||
modelInputTokenCount: prompt.length,
|
||||
modelOutputTokenCount: fullText.length,
|
||||
@ -517,15 +511,9 @@ export const useMessageOption = () => {
|
||||
cot,
|
||||
responseContent: content,
|
||||
modelResponseContent: fullText,
|
||||
}
|
||||
const _meteringEntries = [
|
||||
currentMeteringEntry,
|
||||
},
|
||||
...meteringEntries,
|
||||
]
|
||||
setCurrentMeteringEntry({
|
||||
loading: false,
|
||||
data: currentMeteringEntry,
|
||||
})
|
||||
setMeteringEntries(_meteringEntries)
|
||||
localStorage.setItem("meteringEntries", JSON.stringify(_meteringEntries))
|
||||
} catch (e) {
|
||||
@ -645,16 +633,6 @@ export const useMessageOption = () => {
|
||||
|
||||
let newMessage: Message[] = []
|
||||
let generateMessageId = generateID()
|
||||
const meter: MeteringEntry = {
|
||||
id: generateMessageId,
|
||||
queryContent: message,
|
||||
date: new Date().getTime()
|
||||
} as MeteringEntry
|
||||
|
||||
setCurrentMeteringEntry({
|
||||
loading: true,
|
||||
data: meter,
|
||||
})
|
||||
|
||||
if (!isRegenerate) {
|
||||
newMessage = [
|
||||
@ -781,7 +759,6 @@ export const useMessageOption = () => {
|
||||
let reasoningStartTime: Date | null = null
|
||||
let reasoningEndTime: Date | null = null
|
||||
let apiReasoning: boolean = false
|
||||
const chatStartTime = new Date()
|
||||
|
||||
for await (const chunk of chunks) {
|
||||
if (chunk?.additional_kwargs?.reasoning_content) {
|
||||
@ -882,31 +859,6 @@ export const useMessageOption = () => {
|
||||
setStreaming(false)
|
||||
setIsProcessing(false)
|
||||
setStreaming(false)
|
||||
|
||||
|
||||
// Save metering entry
|
||||
const { cot, content } = responseResolver(fullText)
|
||||
const currentMeteringEntry = {
|
||||
...meter,
|
||||
modelInputTokenCount: prompt.length,
|
||||
modelOutputTokenCount: fullText.length,
|
||||
model: ollama.modelName ?? ollama.model,
|
||||
relatedDataCount: 0,
|
||||
timeTaken: new Date().getTime() - chatStartTime.getTime(),
|
||||
date: chatStartTime.getTime(),
|
||||
cot,
|
||||
responseContent: content,
|
||||
modelResponseContent: fullText,
|
||||
}
|
||||
const _meteringEntries = [
|
||||
currentMeteringEntry,
|
||||
...meteringEntries,
|
||||
]
|
||||
setCurrentMeteringEntry({
|
||||
loading: false,
|
||||
data: currentMeteringEntry,
|
||||
})
|
||||
setMeteringEntries(_meteringEntries)
|
||||
} catch (e) {
|
||||
const errorSave = await saveMessageOnError({
|
||||
e,
|
||||
|
@ -21,27 +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助手。根据用户的问题,请筛选出<iod-search-results>中的相关信息,并结合<iod-search-results>中的数据、场景(项目)、人员团队等类型的相关信息,
|
||||
以举例的方式,在思维链和回答中引用<iod-search-results>中的相关数据、场景和人员团队,例如回答中举例介绍一下如何利用这些数据、过往成功项目和这些人员团队及其相关经验来解决用户的问题。 当前日期和时间是: {current_date_time}.
|
||||
在<iod-search-results> 中提供了来自数联网(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\` :
|
||||
\`[IoD source [id] doId: {doId} "{name}"]({url})\`
|
||||
Or in Chinese:
|
||||
\`[数联网引用[id] doId: {doId} "{name}"]({url})\`
|
||||
For example, in English:
|
||||
\`[IoD source [1] doId: 10.48550/arXiv.1803.05591v2 "On the insufficiency of existing momentum schemes for Stochastic Optimization"](http://arxiv.org/pdf/1803.05591v2.pdf)\`
|
||||
Or in Chinese:
|
||||
\`[数联网引用[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>
|
||||
{iod_search_results}
|
||||
</iod-search-results>
|
||||
在回答中,需要首先从问题的角度,梳理一下数联网搜索结果中的数据、场景(项目)、人员与问题的相关性,再从合理的角度开始展开回答问题。
|
||||
`
|
||||
|
||||
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}.
|
||||
|
||||
@ -67,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.
|
||||
|
@ -35,8 +35,6 @@ type State = {
|
||||
setMessages: (messages: Message[]) => void
|
||||
history: ChatHistory
|
||||
setHistory: (history: ChatHistory) => void
|
||||
currentMeteringEntry: {data: MeteringEntry, loading: boolean}
|
||||
setCurrentMeteringEntry: (meteringEntry: {data: MeteringEntry, loading: boolean}) => void
|
||||
meteringEntries: MeteringEntry[]
|
||||
setMeteringEntries: (meteringEntries: MeteringEntry[]) => void
|
||||
streaming: boolean
|
||||
@ -122,8 +120,6 @@ 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([])),
|
||||
setMeteringEntries: (meteringEntries) => set({ meteringEntries }),
|
||||
streaming: false,
|
||||
|
161
src/web/iod.ts
@ -26,12 +26,6 @@ export const tokenizeInput = function (input: string): string[] {
|
||||
}
|
||||
//doipUrl = tcp://reg01.public.internetofdata.cn:21037
|
||||
export const iodConfig = {
|
||||
"gatewayUrl": "tcp://reg01.public.internetofdata.cn:21037",
|
||||
"registry":"data/Registry",
|
||||
"localRepository":"data/Repository",
|
||||
"doBrowser":"http://021.node.internetapi.cn:21030/SCIDE/SCManager"
|
||||
}
|
||||
export const iodConfigLocal = {
|
||||
"gatewayUrl": "tcp://127.0.0.1:21036",
|
||||
"registry":"bdware/Registry",
|
||||
"localRepository":"bdtest.local/myrepo1",
|
||||
@ -40,51 +34,6 @@ export const iodConfigLocal = {
|
||||
function inGrepList(str: string){
|
||||
return "什么|问题|需要|合适|设计|考虑|合作|精度|传感器|最新|研究|药物".indexOf(str)!=-1;
|
||||
}
|
||||
|
||||
export const makeSearchParamsWithDataType = function(count: number, keyword: string| string[], dataType: string){
|
||||
const searchMode = [];
|
||||
searchMode.push({"key":"data_type", "type":"MUST", "value":dataType})
|
||||
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
|
||||
});
|
||||
});
|
||||
}
|
||||
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 makeRegSearchParams = function(count: number, keyword: string| string[]){
|
||||
const searchMode = [];
|
||||
if (typeof keyword === 'string') {
|
||||
@ -236,61 +185,92 @@ export async function localIodSearch(
|
||||
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");
|
||||
|
||||
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 allResults: IodRegistryEntry[] = [];
|
||||
for (const res of results) {
|
||||
// 检查顶层状态
|
||||
const response = await fetch(iodConfig.doBrowser, {
|
||||
method: "POST",
|
||||
body: JSON.stringify(params),
|
||||
signal: abortController.signal
|
||||
});
|
||||
|
||||
const res = await response.json();
|
||||
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;
|
||||
console.log(res);
|
||||
return [];
|
||||
}
|
||||
|
||||
const body = JSON.parse(res.result.body);
|
||||
if (body.code !== 0) {
|
||||
continue;
|
||||
console.log(body);
|
||||
return [];
|
||||
}
|
||||
const entries: IodRegistryEntry[] = body.data?.results || [];
|
||||
// 数据清洗:补全 url 和 doId
|
||||
for (const r of entries) {
|
||||
|
||||
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;
|
||||
}
|
||||
// 合并到总结果
|
||||
allResults.push(...entries);
|
||||
|
||||
// results 根据 doId 去重
|
||||
const map = new Map<string, IodRegistryEntry>();
|
||||
for (const r of results) {
|
||||
map.set(r.doId, r);
|
||||
}
|
||||
const seenDoIds = new Set<string>();
|
||||
const prunedResults: IodRegistryEntry[] = [];
|
||||
for (const r of allResults) {
|
||||
if (r.doId && !seenDoIds.has(r.doId)) {
|
||||
seenDoIds.add(r.doId);
|
||||
prunedResults.push(r);
|
||||
}
|
||||
}
|
||||
return prunedResults;
|
||||
return Array.from(map.values());
|
||||
} catch (e) {
|
||||
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\//
|
||||
@ -301,7 +281,6 @@ export const searchIod = async (query: string, keywords: string[]) => {
|
||||
|
||||
const isSimpleMode = await getIsSimpleInternetSearch()
|
||||
console.log("searchMode:"+isSimpleMode+"\n kw:"+JSON.stringify(keywords)+"\n"+" ->searchResult:\n"+JSON.stringify(searchResults))
|
||||
console.log("pruned Search Result:"+JSON.stringify(searchResults.map(r=>r.doId+" "+r.name)))
|
||||
if (isSimpleMode) {
|
||||
await getOllamaURL()
|
||||
return searchResults
|
||||
|
@ -5,9 +5,6 @@ module.exports = {
|
||||
content: ["./src/**/*.tsx"],
|
||||
theme: {
|
||||
extend: {
|
||||
width: {
|
||||
'1/10': '10%',
|
||||
},
|
||||
backgroundImage: {
|
||||
'bottom-mask-light': 'linear-gradient(0deg, transparent 0, #ffffff 160px)',
|
||||
'bottom-mask-dark': 'linear-gradient(0deg, transparent 0, #171717 160px)',
|
||||
|