feat: app ui

This commit is contained in:
archer
2023-07-10 08:56:11 +08:00
parent aef42cef9d
commit cd77d81135
33 changed files with 661 additions and 547 deletions

View File

@@ -19,6 +19,7 @@ export interface InitChatResponse {
export interface InitShareChatResponse {
maxContext: number;
userAvatar: string;
appId: string;
model: {
name: string;
avatar: string;

View File

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

@@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1688892007547" class="icon" viewBox="0 0 1025 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2705" xmlns:xlink="http://www.w3.org/1999/xlink" width="64.0625" height="64"><path d="M533.353412 682.285176c0-0.030118 0.060235-0.060235 0.060235-0.090353C533.172706 682.194824 533.142588 682.224941 533.353412 682.285176zM40.176941 555.008l314.398118 314.398118c27.045647 27.045647 70.927059 27.045647 97.942588 0 27.045647-26.985412 27.045647-70.866824 0-97.912471l-196.186353-196.216471 665.630118 0c38.249412 0 69.240471-31.021176 69.240471-69.240471 0-38.279529-31.021176-69.270588-69.240471-69.270588L256.361412 436.766118l196.186353-196.186353c27.045647-27.015529 27.045647-70.927059 0-97.942588C439.024941 129.114353 421.285647 122.337882 403.546353 122.337882c-17.709176 0-35.448471 6.776471-48.941176 20.299294L40.176941 457.065412C13.101176 484.111059 13.101176 527.932235 40.176941 555.008z" p-id="2706"></path></svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1688888958304" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2590" xmlns:xlink="http://www.w3.org/1999/xlink" width="64" height="64"><path d="M512 85.333333c235.648 0 426.666667 191.018667 426.666667 426.666667s-191.018667 426.666667-426.666667 426.666667a424.96 424.96 0 0 1-198.144-48.725334l-183.04 47.658667a36.266667 36.266667 0 0 1-44.245333-44.202667l47.616-182.997333A424.917333 424.917333 0 0 1 85.333333 512C85.333333 276.352 276.352 85.333333 512 85.333333z m53.418667 469.333334H373.333333l-4.352 0.298666a32 32 0 0 0 0 63.402667l4.352 0.298667h192.085334l4.309333-0.298667a32 32 0 0 0 0-63.402667L565.418667 554.666667z m85.248-149.333334h-277.333334l-4.352 0.298667a32 32 0 0 0 0 63.402667L373.333333 469.333333h277.333334l4.352-0.298666a32 32 0 0 0 0-63.402667L650.666667 405.333333z" p-id="2591"></path></svg>

After

Width:  |  Height:  |  Size: 1014 B

View File

@@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1688889092975" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2652" xmlns:xlink="http://www.w3.org/1999/xlink" width="64" height="64"><path d="M932.15857778 820.62601482V639.06702222c-26.82121482 23.42305185-57.04059259 42.71976297-89.4445037 57.28331853-104.97896297 46.11792592-218.45333333 69.41961482-333.14133333 68.32734814-148.06281482 8.61677037-294.66927408-32.768-416.39632593-117.35798519v182.89398519C101.4290963 910.07051852 277.52675555 997.45185185 512.36408889 997.45185185c234.83733333 0 409.6-88.10951111 419.79448889-168.57315555v-8.25268148z" p-id="2653"></path><path d="M932.15857778 505.93185185v-3.39816296 8.25268148c-10.19448889 80.58500741-184.95715555 168.57315555-419.79448889 168.57315555-234.83733333 0-410.93499259-87.38133333-419.18767407-167.23816295V329.10601482C214.90346667 413.81736297 361.50992592 455.08077037 509.57274075 446.464c114.688 1.09226667 228.16237037-22.08805925 333.14133333-68.32734815 32.40391111-14.68491852 62.62328889-33.98162963 89.4445037-57.28331852V505.93185185z" p-id="2654"></path><path d="M816.13558518 85.40918518c69.66234075 30.70482963 111.28983703 74.3954963 111.28983704 113.35300741 0 38.22933333-42.96248889 80.58500741-111.28983704 113.35300741-97.09037037 41.26340741-201.70524445 61.53102222-307.16965926 59.34648889-238.96367408 0-417.1245037-90.7794963-417.1245037-171.97131852s178.88900741-172.6994963 418.4594963-172.69949629c104.97896297-2.3058963 209.10838518 17.71899259 305.83466666 58.6183111z" p-id="2655"></path></svg>

After

Width:  |  Height:  |  Size: 1.7 KiB

View File

@@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1688888528308" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2619" xmlns:xlink="http://www.w3.org/1999/xlink" width="64" height="64"><path d="M936.896 785.792c9.28-14.336 17.664-29.056 25.472-44.224 1.856-3.648 3.52-7.36 5.312-11.072 5.952-12.288 11.392-24.832 16.32-37.632 1.984-5.184 3.904-10.368 5.76-15.616 4.096-11.904 7.68-23.936 10.944-36.16 1.536-5.888 3.2-11.712 4.544-17.6 2.816-12.608 4.992-25.472 6.848-38.464 0.832-5.568 1.92-11.008 2.56-16.64 2.048-18.56 3.328-37.312 3.328-56.32 0-278.976-227.008-505.984-505.984-505.984S6.016 233.024 6.016 512c0 19.072 1.28 37.824 3.328 56.32 0.64 5.632 1.728 11.072 2.56 16.704 1.92 12.928 4.032 25.728 6.848 38.336 1.344 6.016 3.008 11.84 4.544 17.664 3.264 12.224 6.848 24.32 10.944 36.16 1.792 5.248 3.712 10.432 5.696 15.616 4.928 12.8 10.432 25.344 16.32 37.632 1.792 3.712 3.456 7.424 5.312 11.072 7.744 15.168 16.192 29.888 25.408 44.096 0.32 0.576 0.64 1.152 1.024 1.664C178.432 926.016 334.592 1017.984 512 1017.984s333.632-92.032 423.936-230.656C936.256 786.816 936.576 786.304 936.896 785.792zM512 248.448c102.592 0 185.984 83.456 185.984 185.984 0 102.656-83.456 186.176-185.984 186.176S326.016 537.088 326.016 434.432C326.016 331.904 409.408 248.448 512 248.448zM204.416 828.8C257.216 708.096 377.152 627.264 512 627.264s254.784 80.896 307.648 201.472C739.968 906.112 631.552 953.984 512 953.984 392.448 953.984 284.032 906.112 204.416 828.8z" p-id="2620"></path></svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

@@ -1 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1684122143852" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2364" xmlns:xlink="http://www.w3.org/1999/xlink" width="64" height="64"><path d="M511.6 76.3C264.3 76.2 64 276.4 64 523.5 64 718.9 189.3 885 363.8 946c23.5 5.9 19.9-10.8 19.9-22.2v-77.5c-135.7 15.9-141.2-73.9-150.3-88.9C215 726 171.5 718 184.5 703c30.9-15.9 62.4 4 98.9 57.9 26.4 39.1 77.9 32.5 104 26 5.7-23.5 17.9-44.5 34.7-60.8-140.6-25.2-199.2-111-199.2-213 0-49.5 16.3-95 48.3-131.7-20.4-60.5 1.9-112.3 4.9-120 58.1-5.2 118.5 41.6 123.2 45.3 33-8.9 70.7-13.6 112.9-13.6 42.4 0 80.2 4.9 113.5 13.9 11.3-8.6 67.3-48.8 121.3-43.9 2.9 7.7 24.7 58.3 5.5 118 32.4 36.8 48.9 82.7 48.9 132.3 0 102.2-59 188.1-200 212.9 23.5 23.2 38.1 55.4 38.1 91v112.5c0.8 9 0 17.9 15 17.9 177.1-59.7 304.6-227 304.6-424.1 0-247.2-200.4-447.3-447.5-447.3z" p-id="2365"></path></svg>
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1688888426962" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="24999" xmlns:xlink="http://www.w3.org/1999/xlink" width="64" height="64"><path d="M1024 496C1012.8 232 798.4 17.6 534.4 8 393.6 1.6 259.2 52.8 156.8 150.4 56 248 0 379.2 0 520c0 230.4 155.2 433.6 376 492.8 6.4 1.6 12.8 3.2 19.2 3.2 16 0 32-4.8 44.8-16 17.6-14.4 28.8-35.2 28.8-57.6v-52.8c0-14.4 1.6-78.4 1.6-91.2 1.6-8 3.2-14.4 8-19.2 11.2-14.4 12.8-33.6 4.8-49.6-8-14.4-20.8-24-36.8-25.6-11.2-1.6-22.4-3.2-30.4-6.4-12.8-3.2-25.6-9.6-38.4-16-9.6-4.8-19.2-12.8-27.2-22.4-6.4-8-12.8-20.8-17.6-36.8-6.4-19.2-9.6-40-9.6-62.4 0-28.8 9.6-52.8 27.2-72l17.6-19.2-9.6-24c-3.2-8-6.4-19.2-4.8-38.4 22.4 8 28.8 12.8 28.8 12.8l1.6 1.6c6.4 3.2 11.2 8 16 9.6l19.2 11.2 16-4.8c27.2-8 54.4-11.2 83.2-11.2 28.8 0 56 3.2 83.2 11.2l16 4.8 33.6-20.8c11.2-6.4 22.4-12.8 35.2-17.6 1.6 14.4 0 27.2-4.8 40l-9.6 24 17.6 19.2c17.6 19.2 27.2 43.2 27.2 72 0 24-3.2 43.2-8 60.8-6.4 19.2-12.8 30.4-19.2 38.4-6.4 8-16 16-28.8 24-14.4 8-25.6 11.2-38.4 16-8 3.2-20.8 4.8-32 6.4h-1.6c-16 3.2-28.8 14.4-35.2 28.8-6.4 14.4-4.8 30.4 4.8 44.8 6.4 8 9.6 20.8 9.6 36.8v126.4c0 22.4 11.2 44.8 28.8 57.6 17.6 12.8 41.6 17.6 64 11.2C888 942.4 1035.2 729.6 1024 496zM651.2 931.2V816c0-14.4-1.6-27.2-4.8-38.4 17.6-4.8 35.2-11.2 54.4-22.4l1.6-1.6c20.8-12.8 36.8-25.6 49.6-41.6 16-19.2 25.6-41.6 33.6-64 8-25.6 12.8-52.8 12.8-86.4 0-41.6-11.2-76.8-33.6-108.8 6.4-33.6 3.2-67.2-11.2-104l-6.4-17.6-17.6-6.4c-17.6-6.4-40-4.8-67.2 6.4-17.6 6.4-33.6 14.4-51.2 24l-3.2 3.2c-56-12.8-116.8-12.8-174.4 0-1.6-1.6-3.2-1.6-4.8-3.2-9.6-6.4-24-14.4-46.4-22.4-27.2-12.8-51.2-14.4-70.4-8l-17.6 6.4-6.4 17.6c-14.4 38.4-17.6 72-9.6 104-22.4 30.4-33.6 67.2-33.6 108.8 0 30.4 3.2 57.6 12.8 84.8 8 27.2 19.2 49.6 32 65.6 16 17.6 32 32 51.2 41.6 17.6 9.6 35.2 17.6 54.4 22.4-1.6 4.8-1.6 9.6-3.2 16V843.2c-38.4-12.8-70.4-28.8-96-56-17.6-19.2-36.8-38.4-46.4-44.8-20.8-9.6-33.6 11.2-28.8 20.8 4.8 14.4 25.6 22.4 36.8 33.6 12.8 12.8 14.4 30.4 25.6 44.8 16 22.4 72 49.6 110.4 41.6v51.2C206.4 881.6 80 712 80 520c0-118.4 46.4-228.8 132.8-312C294.4 129.6 400 88 513.6 88h17.6C753.6 96 934.4 276.8 944 499.2c9.6 193.6-110.4 369.6-292.8 432z" p-id="25000"></path></svg>

Before

Width:  |  Height:  |  Size: 1013 B

After

Width:  |  Height:  |  Size: 2.3 KiB

View File

@@ -1 +0,0 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1684163814302" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="3451" xmlns:xlink="http://www.w3.org/1999/xlink" width="64" height="64"><path d="M512 384c-229.8 0-416-57.3-416-128v256c0 70.7 186.2 128 416 128s416-57.3 416-128V256c0 70.7-186.2 128-416 128z" p-id="3452"></path><path d="M512 704c-229.8 0-416-57.3-416-128v256c0 70.7 186.2 128 416 128s416-57.3 416-128V576c0 70.7-186.2 128-416 128zM512 320c229.8 0 416-57.3 416-128S741.8 64 512 64 96 121.3 96 192s186.2 128 416 128z" p-id="3453"></path></svg>

Before

Width:  |  Height:  |  Size: 694 B

View File

@@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1688888266618" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="6979" xmlns:xlink="http://www.w3.org/1999/xlink" width="64" height="64"><path d="M1008.302197 306.556735 789.691542 525.16739C769.561962 545.29697 736.950904 545.29697 716.821324 525.16739L498.2391 306.556735C478.10952 286.455586 478.10952 253.816097 498.2391 233.714948L716.821324 15.104293C736.950904-5.025287 769.561962-5.025287 789.691542 15.104293L1008.302197 233.714948C1028.431777 253.816097 1028.431777 286.455586 1008.302197 306.556735ZM753.668691 54.823253 537.958061 270.533884 753.668691 486.244515 969.379322 270.533884 753.668691 54.823253ZM369.841946 1023.971568 57.094228 1023.971568C25.677298 1023.971568 0.231007 998.525277 0.231007 967.108347L0.231007 654.360629C0.231007 622.972131 25.677298 597.497407 57.094228 597.497407L369.841946 597.497407C401.230445 597.497407 426.705168 622.972131 426.705168 654.360629L426.705168 967.108347C426.705168 998.525277 401.230445 1023.971568 369.841946 1023.971568ZM369.841946 654.360629 57.094228 654.360629 57.094228 967.108347 369.841946 967.108347 369.841946 654.360629ZM369.841946 512.202575 57.094228 512.202575C25.677298 512.202575 0.231007 486.756284 0.231007 455.339354L0.231007 142.591636C0.231007 111.203137 25.677298 85.728414 57.094228 85.728414L369.841946 85.728414C401.230445 85.728414 426.705168 111.203137 426.705168 142.591636L426.705168 455.339354C426.705168 486.756284 401.230445 512.202575 369.841946 512.202575ZM369.841946 142.591636 57.094228 142.591636 57.094228 455.339354 369.841946 455.339354 369.841946 142.591636ZM568.863221 597.497407 881.61094 597.497407C912.999438 597.497407 938.474161 622.972131 938.474161 654.360629L938.474161 967.108347C938.474161 998.525277 912.999438 1023.971568 881.61094 1023.971568L568.863221 1023.971568C537.446292 1023.971568 512 998.525277 512 967.108347L512 654.360629C512 622.972131 537.446292 597.497407 568.863221 597.497407ZM568.863221 967.108347 881.61094 967.108347 881.61094 654.360629 568.863221 654.360629 568.863221 967.108347Z" p-id="6980"></path></svg>

After

Width:  |  Height:  |  Size: 2.2 KiB

View File

@@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1688883703351" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1412" xmlns:xlink="http://www.w3.org/1999/xlink" ><path d="M238.93333333 512l87.04 116.05333333a34.13333333 34.13333333 0 0 1-54.61333333 40.96l-102.4-136.53333333a34.0992 34.0992 0 0 1 0-40.96l102.4-136.53333333a34.13333333 34.13333333 0 0 1 54.61333333 40.96L238.93333333 512zM680.96 628.05333333a34.13333333 34.13333333 0 0 0 54.61333333 40.96l102.4-136.53333333a34.0992 34.0992 0 0 0 0-40.96l-102.4-136.53333333a34.13333333 34.13333333 0 0 0-54.61333333 40.96L768 512l-87.04 116.05333333zM502.64746667 655.94026667a34.13333333 34.13333333 0 1 1-66.62826667-14.81386667l68.26666667-307.2a34.13333333 34.13333333 0 1 1 66.62826666 14.81386667l-68.26666666 307.2z" p-id="1413"></path><path d="M136.53333333 119.46666667a34.13333333 34.13333333 0 0 0-34.13333333 34.13333333V887.46666667a34.13333333 34.13333333 0 0 0 34.13333333 34.13333333h733.86666667a34.13333333 34.13333333 0 0 0 34.13333333-34.13333333V153.6a34.13333333 34.13333333 0 0 0-34.13333333-34.13333333H136.53333333z m0-68.26666667h733.86666667a102.4 102.4 0 0 1 102.4 102.4V887.46666667a102.4 102.4 0 0 1-102.4 102.4H136.53333333a102.4 102.4 0 0 1-102.4-102.4V153.6a102.4 102.4 0 0 1 102.4-102.4z" p-id="1414"></path></svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

@@ -1 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1688783528309" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="5845" xmlns:xlink="http://www.w3.org/1999/xlink" ><path d="M931.53 960H512C264.97 960 64 759.03 64 512S264.97 64 512 64s448 200.97 448 448v419.53c0 15.69-12.77 28.47-28.47 28.47zM512 116.71c-217.96 0-395.29 177.33-395.29 395.29 0 217.97 177.33 395.3 395.29 395.3h395.29V512c0-217.97-177.33-395.29-395.29-395.29z" fill="#333333" p-id="5846"></path><path d="M730.13 439.04H293.87c-14.55 0-26.35-11.8-26.35-26.35s11.8-26.35 26.35-26.35h436.26c14.55 0 26.35 11.8 26.35 26.35s-11.8 26.35-26.35 26.35zM730.13 637.67H293.87c-14.55 0-26.35-11.8-26.35-26.35s11.8-26.35 26.35-26.35h436.26c14.55 0 26.35 11.8 26.35 26.35s-11.8 26.35-26.35 26.35z" p-id="5847"></path></svg>
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1688888938208" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2436" xmlns:xlink="http://www.w3.org/1999/xlink" width="64" height="64"><path d="M512 85.333333c235.648 0 426.666667 191.018667 426.666667 426.666667s-191.018667 426.666667-426.666667 426.666667a424.96 424.96 0 0 1-195.712-47.445334l-163.242667 45.525334a53.333333 53.333333 0 0 1-65.706666-65.706667l45.568-163.114667A424.96 424.96 0 0 1 85.333333 512C85.333333 276.352 276.352 85.333333 512 85.333333z m0 64A362.666667 362.666667 0 0 0 149.333333 512c0 62.72 15.914667 123.008 45.781334 176.512l6.4 11.52-47.445334 169.984 170.112-47.445333 11.52 6.4A362.666667 362.666667 0 1 0 512 149.333333zM373.333333 554.666667h191.914667a32 32 0 0 1 4.352 63.701333l-4.352 0.298667H373.333333a32 32 0 0 1-4.352-63.701334L373.333333 554.666667h191.914667H373.333333z m0-149.333334h277.546667a32 32 0 0 1 4.309333 63.701334l-4.309333 0.298666H373.333333a32 32 0 0 1-4.352-63.701333L373.333333 405.333333h277.546667H373.333333z" p-id="2437"></path></svg>

Before

Width:  |  Height:  |  Size: 912 B

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1688785060655" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="8670" xmlns:xlink="http://www.w3.org/1999/xlink" ><path d="M901.3 504.8l-76.3-150c-13.4-26.3-40-42.6-69.5-42.6H639c-1.1 0-2-0.9-2-2V120.6c0-31.1-25.3-56.3-56.3-56.3h-90c-31.1 0-56.3 25.3-56.3 56.3v189.6c0 1.1-0.9 2-2 2H315.8c-29.5 0-56.1 16.3-69.5 42.6l-76.3 150c-9.2 18.1-8.4 39.3 2.2 56.6 10.3 16.8 27.9 27 47.4 27.6-4.8 101-38.3 205.9-90.2 279.5-12.5 17.8-14.1 40.8-4.1 60.1 10 19.3 29.7 31.3 51.5 31.3h601.5c35 0 66-23.6 75.2-57.4 15.5-56.5 28.4-107.9 29.4-164.9C884 685 874 636 852.9 589c19-1.1 36.1-11.2 46.2-27.6 10.6-17.3 11.4-38.5 2.2-56.6z m-681.4 25.4l76.3-150c3.8-7.4 11.3-12 19.6-12h116.4c32 0 58-26 58-58V120.6c0-0.1 0.2-0.3 0.3-0.3h90c0.1 0 0.3 0.2 0.3 0.3v189.6c0 32 26 58 58 58h116.4c8.3 0 15.8 4.6 19.6 12l76.3 150c0.2 0.3 0.5 1-0.1 2s-1.3 1-1.7 1H221.7c-0.4 0-1.1 0-1.7-1-0.6-1-0.3-1.7-0.1-2zM827 736.6c-0.9 50.5-12.9 98.3-27.4 151.1-2.6 9.5-11.3 16.2-21.2 16.2H651.8c11.3-22.3 18.5-44 23.1-61.2 7.1-26.7 10.7-53.5 10.6-78-0.1-17.1-15.5-30.1-32.4-27.4-13.6 2.2-23.6 14-23.6 27.8 0.1 42.7-14.1 98.2-42.7 138.8H406.2c15.2-21.7 26.1-43.8 33.6-61.9 10-24.3 17.4-49.7 21.2-72.5 2.8-17-10.4-32.5-27.6-32.5-13.6 0-25.3 9.8-27.6 23.3-2.8 16.6-8.3 37.7-17.7 60.4-10.1 24.6-27.8 58.1-55.6 83.3H176.9c-0.5 0-1.2 0-1.8-1.1-0.6-1.1-0.2-1.6 0.1-2 29.7-42.1 54.8-94.5 72.5-151.4 16.2-52.1 25.7-106.9 28-160.3h514.6C816 635.6 828 684 827 736.6z" fill="" p-id="8671"></path></svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 6.3 KiB

View File

@@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1688888642805" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2495" xmlns:xlink="http://www.w3.org/1999/xlink" width="64" height="64"><path d="M512 1.616824c-281.876855 0-510.383176 228.508367-510.383176 510.385223 0 281.874809 228.506321 510.380106 510.383176 510.380106s510.383176-228.506321 510.383176-510.380106C1022.383176 230.125191 793.876855 1.616824 512 1.616824zM835.144748 838.149175c0.101307-0.001023 0.204661-0.001023 0.304945-0.002047-83.016739 82.334193-197.291388 133.199675-323.449693 133.199675-126.424364 0-240.915955-51.078329-323.974649-133.718491 0.039909 0 0.080841 0 0.119727-0.001023-5.981225-5.209652-11.280928-10.671037-15.808034-16.390295 71.405283-118.755708 190.999079-181.641967 339.662957-181.641967 152.783709 0 258.138198 66.104557 339.661933 181.64606C846.923002 827.15375 841.388962 832.788074 835.144748 838.149175zM358.88474 435.444417c0-84.562954 68.552306-153.11526 153.11526-153.11526s153.11526 68.552306 153.11526 153.11526-68.552306 153.11526-153.11526 153.11526S358.88474 520.007371 358.88474 435.444417zM628.101251 603.373057c53.199643-36.849256 88.051405-98.314143 88.051405-167.927617 0-112.750947-91.40171-204.155726-204.15368-204.155726s-204.15368 91.40478-204.15368 204.155726c0 69.614497 34.852786 131.079384 88.052428 167.92864-105.013729 27.350928-195.406458 90.915639-257.029958 176.574554-54.253648-75.417667-86.213547-167.948083-86.213547-267.945565 0-253.688863 205.655893-459.344756 459.344756-459.344756s459.344756 205.655893 459.344756 459.344756c0 99.998505-31.960922 192.528921-86.215594 267.947612C823.506686 694.290743 733.113957 630.723985 628.101251 603.373057z" p-id="2496"></path></svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

View File

@@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1688883742264" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1872" xmlns:xlink="http://www.w3.org/1999/xlink" ><path d="M848.57995022 110.69313539a64.72691317 64.72691317 0 0 1 64.72691439 64.72691439v673.15990044a64.72691317 64.72691317 0 0 1-64.72691439 64.72691439H175.42004978A64.72691317 64.72691317 0 0 1 110.69313539 848.57995022V175.42004978A64.72691317 64.72691317 0 0 1 175.42004978 110.69313539h673.15990044M848.57995022 45.96622222H175.42004978a129.45382756 129.45382756 0 0 0-129.45382756 129.45382756v673.15990044a129.45382756 129.45382756 0 0 0 129.45382756 129.45382756h673.15990044a129.45382756 129.45382756 0 0 0 129.45382756-129.45382756V175.42004978a129.45382756 129.45382756 0 0 0-129.45382756-129.45382756z" p-id="1873"></path><path d="M520.54395259 751.48957984A32.36345719 32.36345719 0 0 1 488.18049539 719.12612386V434.3277037a32.36345719 32.36345719 0 0 1 64.72691439 0v284.79842016a32.36345719 32.36345719 0 0 1-32.36345719 32.36345598zM304.87387614 751.48957984A32.36345719 32.36345719 0 0 1 272.51042016 719.12612386v-155.34459259a32.36345719 32.36345719 0 0 1 64.72691317 0v155.34459259A32.36345719 32.36345719 0 0 1 304.87387614 751.48957984zM719.12612386 751.48957984A32.36345719 32.36345719 0 0 1 686.76266667 719.12612386V304.87387614a32.36345719 32.36345719 0 0 1 64.72691317 0v414.25224772A32.36345719 32.36345719 0 0 1 719.12612386 751.48957984z" p-id="1874"></path></svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

@@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1688883731248" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1717" xmlns:xlink="http://www.w3.org/1999/xlink" width="64" height="64"><path d="M930.70222187 416.42666667l-91.02222187-15.92888854-18.2044448-40.96 52.33777813-75.09333333c15.92888853-22.7555552 13.65333333-52.33777813-6.82666666-72.81777813l-56.88888854-56.88888854c-20.48-20.48-50.06222187-22.7555552-72.81777813-6.82666666l-75.09333333 52.33777706-40.96-18.20444373-15.92888854-91.02222293c-2.2755552-25.03111147-25.03111147-45.51111147-52.33777813-45.5111104h-79.6444448c-27.30666667 0-50.06222187 20.48-56.88888853 47.78666666l-15.92888854 91.02222187-40.96 18.2044448L282.16888853 147.91111147c-22.7555552-15.92888853-52.33777813-13.65333333-72.81777706 6.82666666l-56.8888896 56.88888854c-20.48 20.48-22.7555552 50.06222187-6.82666667 72.81777813l52.33777813 75.09333333-18.2044448 40.96-91.02222186 15.92888854c-22.7555552 4.55111147-43.2355552 27.30666667-43.2355552 54.61333333v79.6444448c0 27.30666667 20.48 50.06222187 47.78666666 56.88888853l91.02222187 15.92888854 18.2044448 40.96L147.91111147 741.83111147c-15.92888853 22.7555552-13.65333333 52.33777813 6.82666666 72.81777706l56.88888854 56.8888896c20.48 20.48 50.06222187 22.7555552 72.81777813 6.82666667l75.09333333-52.33777813 40.96 18.2044448 15.92888854 91.02222186c4.55111147 27.30666667 27.30666667 47.78666667 56.88888853 47.78666667h79.6444448c27.30666667 0 50.06222187-20.48 56.88888853-47.78666667l15.9288896-91.02222186 40.96-18.2044448 75.09333334 52.33777813c22.7555552 15.92888853 52.33777813 13.65333333 72.81777706-6.82666667l56.8888896-56.8888896c20.48-20.48 22.7555552-50.06222187 6.82666667-72.81777706l-52.33777813-75.09333334 18.2044448-40.96 91.02222186-15.9288896c27.30666667-4.55111147 47.78666667-27.30666667 47.78666667-56.88888853v-79.6444448c-4.55111147-29.58222187-25.03111147-52.33777813-52.33777813-56.88888853z m-20.48 125.1555552l-100.12444374 18.2044448c-11.37777813 2.2755552-20.48 9.10222187-25.03111146 20.48l-31.85777814 75.09333333c-4.55111147 11.37777813-2.2755552 22.7555552 4.55111147 31.85777813l56.88888853 81.92-43.2355552 43.2355552-81.92-56.88888853c-9.10222187-6.82666667-22.7555552-9.10222187-31.85777813-4.55111147L580.26666667 785.06666667c-11.37777813 4.55111147-18.2044448 13.65333333-20.48 25.03111146l-18.2044448 100.12444374h-61.44l-18.20444374-100.12444374c-2.2755552-11.37777813-9.10222187-20.48-20.48-25.03111146l-75.09333333-31.85777814c-11.37777813-4.55111147-22.7555552-2.2755552-31.85777813 4.55111147l-81.92 56.88888853-43.2355552-43.2355552 56.88888853-81.92c6.82666667-9.10222187 9.10222187-22.7555552 4.55111147-31.85777813L238.93333333 580.26666667c-4.55111147-11.37777813-13.65333333-18.2044448-25.03111146-20.48l-100.12444374-18.2044448v-61.44l100.12444374-18.20444374c11.37777813-2.2755552 20.48-9.10222187 25.03111146-20.48l31.85777814-75.09333333c4.55111147-11.37777813 2.2755552-22.7555552-4.55111147-31.85777813l-56.88888853-81.92 43.2355552-43.2355552 81.92 56.88888853c9.10222187 6.82666667 22.7555552 9.10222187 31.85777813 4.55111147L443.73333333 238.93333333c11.37777813-4.55111147 18.2044448-13.65333333 20.48-25.03111146l18.2044448-100.12444374h61.44l18.20444374 100.12444374c2.2755552 11.37777813 9.10222187 20.48 20.48 25.03111146l75.09333333 31.85777814c11.37777813 4.55111147 22.7555552 2.2755552 31.85777813-4.55111147l81.92-56.88888853 43.2355552 43.2355552-56.88888853 81.92c-6.82666667 9.10222187-9.10222187 22.7555552-4.55111147 31.85777813L785.06666667 443.73333333c4.55111147 11.37777813 13.65333333 18.2044448 25.03111146 20.48l100.12444374 18.2044448v59.16444374z" p-id="1718"></path><path d="M512 386.8444448c-68.26666667 0-125.1555552 56.88888853-125.1555552 125.1555552s56.88888853 125.1555552 125.1555552 125.1555552 125.1555552-56.88888853 125.1555552-125.1555552-56.88888853-125.1555552-125.1555552-125.1555552z m0 182.04444373c-31.85777813 0-56.88888853-25.03111147-56.88888853-56.88888853s25.03111147-56.88888853 56.88888853-56.88888853 56.88888853 25.03111147 56.88888853 56.88888853-25.03111147 56.88888853-56.88888853 56.88888853z" p-id="1719"></path></svg>

After

Width:  |  Height:  |  Size: 4.2 KiB

View File

@@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1688886474530" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2328" xmlns:xlink="http://www.w3.org/1999/xlink" width="64" height="64"><path d="M786.03921707 717.99566506c-38.03315307 0-72.4544448 15.70379307-97.23973974 40.86784747L358.93031147 575.64967893c9.66437653-18.7469696 15.67104747-39.6748096 15.67104746-62.1777216 0-13.4214112-2.52579307-26.12241493-6.14202026-38.34533012l325.01296426-201.82545388c24.2122432 21.7835968 55.8981696 35.40693973 90.93180694 35.40693973 75.3197024 0 136.57290133-61.25319893 136.57290133-136.57945066 0-75.3207936-61.25319893-136.57508373-136.57290133-136.57508373-75.32625173 0-136.58054187 61.25429013-136.58054187 136.57508373 0 15.53569813 3.17852587 30.2538432 7.9561376 44.18608853l-322.04401173 199.94802773c-24.691424-24.31375573-58.45779947-39.40301867-95.7465312-39.40301866-75.3197024 0-136.57399253 61.25429013-136.57399254 136.57399253 0 75.32188587 61.25319893 136.58163307 136.57399254 136.58163307 27.38531093 0 52.82552213-8.26285653 74.23363306-22.19510187l342.73935574 190.35241707c-3.24729173 11.67605973-5.53294827 23.69595093-5.53294827 36.36530026 0 75.32188587 61.25210773 136.57508373 136.5783584 136.57508374 75.32188587 0 136.57508373-61.25319893 136.57508373-136.57508374C922.5826464 779.21284266 861.39712213 717.99566506 786.03921707 717.99566506L786.03921707 717.99566506zM784.43467307 103.8427584c37.6642176 0 68.28917973 30.629328 68.28917973 68.28699627 0 37.66312533-30.62496213 68.29136213-68.28917973 68.29136213-37.65766827 0-68.28699627-30.629328-68.28699627-68.29136213C716.1476768 134.4709952 746.7770048 103.8427584 784.43467307 103.8427584L784.43467307 103.8427584zM169.77093227 513.47195733c0-37.6642176 30.63042027-68.28808747 68.2924544-68.28808746 37.65766827 0 68.28699627 30.62387093 68.28699626 68.28808746 0 37.65875947-30.629328 68.28808747-68.28699626 68.28808747C200.40135253 581.7600448 169.77093227 551.1307168 169.77093227 513.47195733L169.77093227 513.47195733zM786.03921707 922.85774506c-37.65766827 0-68.28699627-30.62496213-68.28699627-68.28699626 0-37.65766827 30.629328-68.28699627 68.28699627-68.28699627 37.6642176 0 68.28699627 30.629328 68.28699626 68.28699627C854.32621333 892.23278294 823.70343467 922.85774506 786.03921707 922.85774506L786.03921707 922.85774506zM786.03921707 922.85774506" p-id="2329"></path></svg>

After

Width:  |  Height:  |  Size: 2.5 KiB

View File

@@ -6,15 +6,14 @@ const map = {
model: require('./icons/model.svg').default,
copy: require('./icons/copy.svg').default,
chatSend: require('./icons/chatSend.svg').default,
user: require('./icons/user.svg').default,
delete: require('./icons/delete.svg').default,
withdraw: require('./icons/withdraw.svg').default,
stop: require('./icons/stop.svg').default,
collectionLight: require('./icons/collectionLight.svg').default,
collectionSolid: require('./icons/collectionSolid.svg').default,
chat: require('./icons/chat.svg').default,
empty: require('./icons/empty.svg').default,
back: require('./icons/back.svg').default,
backFill: require('./icons/fill/back.svg').default,
more: require('./icons/more.svg').default,
tabbarChat: require('./icons/phoneTabbar/chat.svg').default,
tabbarModel: require('./icons/phoneTabbar/model.svg').default,
@@ -24,8 +23,6 @@ const map = {
wx: require('./icons/wx.svg').default,
out: require('./icons/out.svg').default,
git: require('./icons/git.svg').default,
kb: require('./icons/kb.svg').default,
appStore: require('./icons/appStore.svg').default,
menu: require('./icons/menu.svg').default,
edit: require('./icons/edit.svg').default,
inform: require('./icons/inform.svg').default,
@@ -37,7 +34,19 @@ const map = {
apikey: require('./icons/apikey.svg').default,
save: require('./icons/save.svg').default,
minus: require('./icons/minus.svg').default,
chatLight: require('./icons/light/chat.svg').default
chatLight: require('./icons/light/chat.svg').default,
chatFill: require('./icons/fill/chat.svg').default,
clearLight: require('./icons/light/clear.svg').default,
apiLight: require('./icons/light/appApi.svg').default,
overviewLight: require('./icons/light/overview.svg').default,
settingLight: require('./icons/light/setting.svg').default,
shareLight: require('./icons/light/share.svg').default,
dbLight: require('./icons/light/db.svg').default,
dbFill: require('./icons/fill/db.svg').default,
appLight: require('./icons/light/app.svg').default,
appFill: require('./icons/fill/app.svg').default,
meLight: require('./icons/light/me.svg').default,
meFill: require('./icons/fill/me.svg').default
};
export type IconName = keyof typeof map;

View File

@@ -61,16 +61,16 @@ const Layout = ({ children }: { children: JSX.Element }) => {
return (
<>
<Box h={'100%'} bg={'#F3F4F5E0'}>
<Box h={'100%'} bg={'myWhite.600'}>
<Box h={'100%'} display={['none', 'block']}>
{pcUnShowLayoutRoute[router.pathname] ? (
<Auth>{children}</Auth>
) : (
<>
<Box h={'100%'} position={'fixed'} left={0} top={0} w={'60px'}>
<Box h={'100%'} position={'fixed'} left={0} top={0} w={'70px'}>
<Navbar unread={unread} />
</Box>
<Box h={'100%'} ml={'60px'} overflow={'overlay'}>
<Box h={'100%'} ml={'70px'} overflow={'overlay'}>
<Auth>{children}</Auth>
</Box>
</>

View File

@@ -22,31 +22,36 @@ const Navbar = ({ unread }: { unread: number }) => {
() => [
{
label: '聊天',
icon: 'chat',
icon: 'chatLight',
activeIcon: 'chatFill',
link: `/chat?appId=${lastChatModelId}&chatId=${lastChatId}`,
activeLink: ['/chat']
},
{
label: '应用',
icon: 'model',
icon: 'tabbarModel',
activeIcon: 'model',
link: `/app/list`,
activeLink: ['/app/list', '/app/detail']
},
{
label: '知识库',
icon: 'kb',
icon: 'dbLight',
activeIcon: 'dbFill',
link: `/kb`,
activeLink: ['/kb']
},
{
label: '市场',
icon: 'appStore',
icon: 'appLight',
activeIcon: 'appFill',
link: '/appStore',
activeLink: ['/appStore']
},
{
label: '账号',
icon: 'user',
icon: 'meLight',
activeIcon: 'meFill',
link: '/number',
activeLink: ['/number']
}
@@ -65,7 +70,7 @@ const Navbar = ({ unread }: { unread: number }) => {
h: '54px',
borderRadius: 'md',
_hover: {
color: '#ffffff'
bg: 'myWhite.600'
}
};
@@ -74,17 +79,17 @@ const Navbar = ({ unread }: { unread: number }) => {
flexDirection={'column'}
alignItems={'center'}
pt={6}
backgroundImage={'linear-gradient(to bottom right,#465069,#000000)'}
bg={'white'}
h={'100%'}
w={'100%'}
boxShadow={'4px 0px 4px 0px rgba(43, 45, 55, 0.01)'}
boxShadow={'2px 0px 8px 0px rgba(0,0,0,0.1)'}
userSelect={'none'}
>
{/* logo */}
<Box
mb={5}
border={'2px solid #fff'}
borderRadius={'36px'}
borderRadius={'50%'}
overflow={'hidden'}
cursor={'pointer'}
onClick={() => router.push('/number')}
@@ -101,15 +106,24 @@ const Navbar = ({ unread }: { unread: number }) => {
{...itemStyles}
{...(item.activeLink.includes(router.pathname)
? {
color: '#ffffff ',
backgroundImage: 'linear-gradient(to bottom right, #2152d9 0%, #4e83fd 100%)'
color: 'myBlue.700',
bg: 'white !important',
boxShadow: '1px 1px 10px rgba(0,0,0,0.2)'
}
: {
color: '#9096a5',
color: 'myGray.500',
backgroundColor: 'transparent'
})}
>
<MyIcon name={item.icon as any} width={'20px'} height={'20px'} />
<MyIcon
name={
item.activeLink.includes(router.pathname)
? (item.activeIcon as any)
: (item.icon as any)
}
width={'20px'}
height={'20px'}
/>
<Box fontSize={'12px'} transform={'scale(0.9)'} mt={'5px'} lineHeight={1}>
{item.label}
</Box>

View File

@@ -4,7 +4,7 @@ import { Box, useTheme, type BoxProps } from '@chakra-ui/react';
const PageContainer = ({ children, ...props }: BoxProps) => {
const theme = useTheme();
return (
<Box bg={'myGray.100'} h={'100%'} p={[0, 5]} {...props}>
<Box bg={'myGray.100'} h={'100%'} p={[0, 5]} px={[0, 6]} {...props}>
<Box
flex={1}
h={'100%'}

View File

@@ -60,7 +60,7 @@ const SlideTabs = ({ list, size = 'md', activeId, onChange, ...props }: Props) =
onChange(item.id);
}}
>
<MyIcon mr={2} name={item.icon as IconName} w={'14px'} />
<MyIcon mr={2} name={item.icon as IconName} w={'16px'} />
{item.label}
</Flex>
))}

View File

@@ -0,0 +1,30 @@
.stopIcon {
animation: zoomStopIcon 0.4s infinite alternate;
}
@keyframes zoomStopIcon {
0% {
transform: scale(0.8);
}
100% {
transform: scale(1.2);
}
}
.newChat {
.modelListContainer {
height: 0;
overflow: hidden;
}
.modelList {
border-radius: 6px;
}
&:hover {
.modelListContainer {
height: 60vh;
}
.modelList {
box-shadow: 0 0 5px rgba($color: #000000, $alpha: 0.05);
border: 1px solid #dee0e2;
}
}
}

View File

@@ -0,0 +1,367 @@
import { useCallback, useRef, useState, useEffect, useMemo } from 'react';
import { throttle } from 'lodash';
import { ChatSiteItemType } from '@/types/chat';
import { useToast } from './useToast';
import { useCopyData } from '@/utils/tools';
import { Box, Card, Flex, Textarea } from '@chakra-ui/react';
import { useUserStore } from '@/store/user';
import { useRouter } from 'next/router';
import { Types } from 'mongoose';
import { HUMAN_ICON } from '@/constants/chat';
import Markdown from '@/components/Markdown';
import MyIcon from '@/components/Icon';
import Avatar from '@/components/Avatar';
import styles from './useChat.module.scss';
import { adaptChatItem_openAI } from '@/utils/plugin/openai';
import { streamFetch } from '@/api/fetch';
const textareaMinH = '22px';
export const useChat = ({ appId }: { appId: string }) => {
const router = useRouter();
const ChatBoxParentRef = useRef<HTMLDivElement>(null);
const TextareaDom = useRef<HTMLTextAreaElement>(null);
// stop chat
const controller = useRef(new AbortController());
const isLeavePage = useRef(false);
const [chatHistory, setChatHistory] = useState<ChatSiteItemType[]>([]);
const { toast } = useToast();
const { copyData } = useCopyData();
const { userInfo } = useUserStore();
const isChatting = useMemo(
() => chatHistory[chatHistory.length - 1]?.status === 'loading',
[chatHistory]
);
const isLargeWidth =
ChatBoxParentRef?.current?.clientWidth && ChatBoxParentRef?.current?.clientWidth > 900;
// 滚动到底部
const scrollToBottom = useCallback((behavior: 'smooth' | 'auto' = 'smooth') => {
if (!ChatBoxParentRef.current) return;
console.log(ChatBoxParentRef.current.scrollHeight);
ChatBoxParentRef.current.scrollTo({
top: ChatBoxParentRef.current.scrollHeight,
behavior
});
}, []);
// 聊天信息生成中……获取当前滚动条位置,判断是否需要滚动到底部
// eslint-disable-next-line react-hooks/exhaustive-deps
const generatingMessage = useCallback(
throttle(() => {
if (!ChatBoxParentRef.current) return;
const isBottom =
ChatBoxParentRef.current.scrollTop + ChatBoxParentRef.current.clientHeight + 150 >=
ChatBoxParentRef.current.scrollHeight;
isBottom && scrollToBottom('auto');
}, 100),
[]
);
// 复制内容
const onclickCopy = useCallback(
(value: string) => {
const val = value.replace(/\n+/g, '\n');
copyData(val);
},
[copyData]
);
// 重置输入内容
const resetInputVal = useCallback((val: string) => {
if (!TextareaDom.current) return;
TextareaDom.current.value = val;
setTimeout(() => {
/* 回到最小高度 */
if (TextareaDom.current) {
TextareaDom.current.style.height =
val === '' ? textareaMinH : `${TextareaDom.current.scrollHeight}px`;
}
}, 100);
}, []);
const startChat = useCallback(
async (prompts: ChatSiteItemType[]) => {
// create abort obj
const abortSignal = new AbortController();
controller.current = abortSignal;
isLeavePage.current = false;
const messages = adaptChatItem_openAI({ messages: prompts, reserveId: true });
// 流请求,获取数据
await streamFetch({
data: {
messages,
appId,
model: ''
},
onMessage: (text: string) => {
setChatHistory((state) =>
state.map((item, index) => {
if (index !== state.length - 1) return item;
return {
...item,
value: item.value + text
};
})
);
generatingMessage();
},
abortSignal
});
// 重置了页面,说明退出了当前聊天, 不缓存任何内容
if (isLeavePage.current) {
return;
}
// 设置聊天内容为完成状态
setChatHistory((state) =>
state.map((item, index) => {
if (index !== state.length - 1) return item;
return {
...item,
status: 'finish'
};
})
);
setTimeout(() => {
generatingMessage();
TextareaDom.current?.focus();
}, 100);
},
[appId, generatingMessage]
);
/**
* user confirm send prompt
*/
const sendPrompt = useCallback(async () => {
if (isChatting) {
toast({
title: '正在聊天中...请等待结束',
status: 'warning'
});
return;
}
// get input value
const value = TextareaDom.current?.value || '';
const val = value.trim().replace(/\n\s*/g, '\n');
if (!val) {
toast({
title: '内容为空',
status: 'warning'
});
return;
}
const newChatList: ChatSiteItemType[] = [
...chatHistory,
{
_id: String(new Types.ObjectId()),
obj: 'Human',
value: val,
status: 'finish'
},
{
_id: String(new Types.ObjectId()),
obj: 'AI',
value: '',
status: 'loading'
}
];
// 插入内容
setChatHistory(newChatList);
// 清空输入内容
resetInputVal('');
setTimeout(() => {
scrollToBottom();
}, 100);
try {
await startChat(newChatList);
} catch (err: any) {
toast({
title: typeof err === 'string' ? err : err?.message || '聊天出错了~',
status: 'warning',
duration: 5000,
isClosable: true
});
resetInputVal(value);
setChatHistory(newChatList.slice(0, newChatList.length - 2));
}
}, [isChatting, chatHistory, resetInputVal, toast, scrollToBottom, startChat]);
const ChatBox = useCallback(
({ appAvatar }: { appAvatar: string }) => {
return (
<Box id={'history'}>
{chatHistory.map((item, index) => (
<Flex key={item._id} alignItems={'flex-start'} py={2}>
{item.obj === 'Human' && <Box flex={1} />}
{/* avatar */}
<Avatar
src={item.obj === 'Human' ? userInfo?.avatar || HUMAN_ICON : appAvatar}
w={isLargeWidth ? '34px' : '24px'}
h={isLargeWidth ? '34px' : '24px'}
{...(item.obj === 'AI'
? {
order: 1,
mr: ['6px', 2]
}
: {
order: 3,
ml: ['6px', 2]
})}
/>
{/* message */}
<Flex order={2} pt={2} maxW={`calc(100% - ${isLargeWidth ? '75px' : '58px'})`}>
{item.obj === 'AI' ? (
<Box w={'100%'}>
<Card bg={'white'} px={4} py={3} borderRadius={'0 8px 8px 8px'}>
<Markdown
source={item.value}
isChatting={index === chatHistory.length - 1 && isChatting}
/>
</Card>
</Box>
) : (
<Box>
<Card
className="markdown"
whiteSpace={'pre-wrap'}
px={4}
py={3}
borderRadius={'8px 0 8px 8px'}
bg={'myBlue.300'}
>
<Box as={'p'}>{item.value}</Box>
</Card>
</Box>
)}
</Flex>
</Flex>
))}
</Box>
);
},
[chatHistory, isChatting, userInfo?.avatar]
);
const ChatInput = useCallback(() => {
return (
<Box m={['0 auto', '20px auto']} w={'100%'} maxW={['auto', 'min(750px, 100%)']}>
<Box
py={'18px'}
position={'relative'}
boxShadow={`0 0 10px rgba(0,0,0,0.1)`}
borderTop={['1px solid', 0]}
borderTopColor={'gray.200'}
borderRadius={['none', 'md']}
backgroundColor={'white'}
>
{/* 输入框 */}
<Textarea
ref={TextareaDom}
py={0}
pr={['45px', '55px']}
border={'none'}
_focusVisible={{
border: 'none'
}}
placeholder="提问"
resize={'none'}
rows={1}
height={'22px'}
lineHeight={'22px'}
maxHeight={'150px'}
maxLength={-1}
overflowY={'auto'}
whiteSpace={'pre-wrap'}
wordBreak={'break-all'}
boxShadow={'none !important'}
color={'myGray.900'}
onChange={(e) => {
const textarea = e.target;
textarea.style.height = textareaMinH;
textarea.style.height = `${textarea.scrollHeight}px`;
}}
onKeyDown={(e) => {
// 触发快捷发送
if (e.keyCode === 13 && !e.shiftKey) {
sendPrompt();
e.preventDefault();
}
// 全选内容
// @ts-ignore
e.key === 'a' && e.ctrlKey && e.target?.select();
}}
/>
{/* 发送和等待按键 */}
<Flex
alignItems={'center'}
justifyContent={'center'}
h={'25px'}
w={'25px'}
position={'absolute'}
right={['12px', '20px']}
bottom={'15px'}
>
{isChatting ? (
<MyIcon
className={styles.stopIcon}
width={['22px', '25px']}
height={['22px', '25px']}
cursor={'pointer'}
name={'stop'}
color={'gray.500'}
onClick={() => controller.current?.abort()}
/>
) : (
<MyIcon
name={'chatSend'}
width={['18px', '20px']}
height={['18px', '20px']}
cursor={'pointer'}
color={'gray.500'}
onClick={sendPrompt}
/>
)}
</Flex>
</Box>
</Box>
);
}, [isChatting, sendPrompt]);
// abort stream
useEffect(() => {
return () => {
window.speechSynthesis?.cancel();
isLeavePage.current = true;
// eslint-disable-next-line react-hooks/exhaustive-deps
controller.current?.abort();
};
}, [router.asPath]);
return {
ChatBoxParentRef,
scrollToBottom,
setChatHistory,
ChatBox,
ChatInput
};
};

View File

@@ -45,6 +45,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
jsonRes<InitShareChatResponse>(res, {
data: {
appId: shareChat.modelId,
maxContext: shareChat.maxContext,
userAvatar: user?.avatar || HUMAN_ICON,
model: {

View File

@@ -24,7 +24,7 @@ const API = ({ modelId }: { modelId: string }) => {
}, []);
return (
<Flex flexDirection={'column'} h={'100%'}>
<Flex flexDirection={'column'} pt={[0, 5]} h={'100%'}>
<Box display={['none', 'flex']} px={5} alignItems={'center'}>
<Box flex={1}>
AppId:

View File

@@ -161,7 +161,9 @@ const Settings = ({ modelId }: { modelId: string }) => {
position={'relative'}
maxW={['auto', '800px']}
>
<Box fontSize={['md', 'xl']}></Box>
<Box fontSize={['md', 'xl']} fontWeight={'bold'}>
</Box>
<Flex mt={5} alignItems={'center'}>
<Box w={['60px', '100px', '140px']} flexShrink={0}>

View File

@@ -108,7 +108,7 @@ ${e.password ? `密码为: ${e.password}` : ''}`;
};
return (
<Box position={'relative'} px={5} minH={'50vh'}>
<Box position={'relative'} pt={[0, 5]} px={5} minH={'50vh'}>
<Flex justifyContent={'space-between'}>
<Box fontWeight={'bold'}>

View File

@@ -0,0 +1,66 @@
import { AppModuleItemType } from '@/types/app';
import { AppSchema } from '@/types/mongoSchema';
import React, { useState, useEffect, useRef, useMemo } from 'react';
import { Box, useOutsideClick, Flex, IconButton } from '@chakra-ui/react';
import MyIcon from '@/components/Icon';
import { useChat } from '@/hooks/useChat';
const ChatTest = ({
app,
modules,
onClose
}: {
app: AppSchema;
modules?: AppModuleItemType[];
onClose: () => void;
}) => {
const isOpen = useMemo(() => !!modules, [modules]);
const { ChatBox, ChatInput, ChatBoxParentRef, setChatHistory } = useChat({
appId: app._id
});
return (
<Flex
zIndex={3}
flexDirection={'column'}
position={'absolute'}
top={5}
right={0}
h={isOpen ? '95%' : '0'}
w={isOpen ? '460px' : '0'}
bg={'white'}
boxShadow={'3px 0 20px rgba(0,0,0,0.2)'}
borderRadius={'md'}
overflow={'hidden'}
transition={'.2s ease'}
>
<Flex py={4} px={5} whiteSpace={'nowrap'}>
<Box fontSize={'xl'} fontWeight={'bold'} flex={1}>
</Box>
<IconButton
className="chat"
size={'sm'}
icon={<MyIcon name={'clearLight'} w={'14px'} />}
variant={'base'}
borderRadius={'md'}
aria-label={'delete'}
onClick={(e) => {
e.stopPropagation();
setChatHistory([]);
}}
/>
</Flex>
<Box ref={ChatBoxParentRef} flex={1} px={5} overflow={'overlay'}>
<ChatBox appAvatar={app.avatar} />
</Box>
<Box px={5}>
<ChatInput />
</Box>
</Flex>
);
};
export default ChatTest;

View File

@@ -1,4 +1,4 @@
import React, { useCallback, useEffect, useRef } from 'react';
import React, { useCallback, useEffect, useRef, useState } from 'react';
import ReactFlow, {
Background,
Controls,
@@ -27,6 +27,7 @@ import dynamic from 'next/dynamic';
import MyIcon from '@/components/Icon';
import ButtonEdge from './components/modules/ButtonEdge';
import MyTooltip from '@/components/MyTooltip';
const NodeChat = dynamic(() => import('./components/NodeChat'), {
ssr: false
});
@@ -48,13 +49,16 @@ const NodeQuestionInput = dynamic(() => import('./components/NodeQuestionInput')
const TemplateList = dynamic(() => import('./components/TemplateList'), {
ssr: false
});
const ChatTest = dynamic(() => import('./components/ChatTest'), {
ssr: false
});
const NodeCQNode = dynamic(() => import('./components/NodeCQNode'), {
ssr: false
});
import 'reactflow/dist/style.css';
import styles from './index.module.scss';
import { AppModuleTemplateItemType } from '@/types/app';
import { AppModuleItemType, AppModuleTemplateItemType } from '@/types/app';
const nanoid = customAlphabet('abcdefghijklmnopqrstuvwxyz1234567890', 6);
@@ -83,6 +87,7 @@ const AppEdit = ({ app, onBack }: Props) => {
onOpen: onOpenTemplate,
onClose: onCloseTemplate
} = useDisclosure();
const [testModules, setTestModules] = useState<AppModuleItemType[]>();
const onChangeNode = useCallback(
({ moduleId, key, type = 'inputs', value, valueKey = 'value' }: FlowModuleItemChangeProps) => {
@@ -149,6 +154,41 @@ const AppEdit = ({ app, onBack }: Props) => {
},
[onChangeNode, onDelNode, setNodes, x, y, zoom]
);
const flow2Modules = useCallback(() => {
const modules: AppModuleItemType[] = nodes.map((item) => ({
...item.data,
position: item.position,
onChangeNode: undefined,
onDelNode: undefined,
outputs: item.data.outputs.map((output) => ({
...output,
targets: [] as FlowOutputTargetItemType[]
}))
}));
// update inputs and outputs
modules.forEach((module) => {
module.inputs.forEach((input) => {
input.connected = !!edges.find(
(edge) => edge.target === module.moduleId && edge.targetHandle === input.key
);
});
module.outputs.forEach((output) => {
output.targets = edges
.filter(
(edge) =>
edge.source === module.moduleId &&
edge.sourceHandle === output.key &&
edge.targetHandle
)
.map((edge) => ({
moduleId: edge.target,
key: edge.targetHandle || ''
}));
});
});
return modules;
}, [edges, nodes]);
const onDelConnect = useCallback(
(id: string) => {
@@ -177,42 +217,8 @@ const AppEdit = ({ app, onBack }: Props) => {
const { mutate: onclickSave, isLoading } = useRequest({
mutationFn: () => {
const modules = nodes.map((item) => ({
...item.data,
position: item.position,
onChangeNode: undefined,
onDelNode: undefined,
outputs: item.data.outputs.map((output) => ({
...output,
targets: [] as FlowOutputTargetItemType[]
}))
}));
console.log(modules);
// update inputs and outputs
modules.forEach((module) => {
module.inputs.forEach((input) => {
input.connected = !!edges.find(
(edge) => edge.target === module.moduleId && edge.targetHandle === input.key
);
});
module.outputs.forEach((output) => {
output.targets = edges
.filter(
(edge) =>
edge.source === module.moduleId &&
edge.sourceHandle === output.key &&
edge.targetHandle
)
.map((edge) => ({
moduleId: edge.target,
key: edge.targetHandle || ''
}));
});
});
return putAppById(app._id, {
modules
modules: flow2Modules()
});
},
successToast: '保存配置成功',
@@ -244,10 +250,11 @@ const AppEdit = ({ app, onBack }: Props) => {
useEffect(() => {
initData(JSON.parse(JSON.stringify(app)));
}, [app]);
}, [app, initData]);
return (
<Flex h={'100%'} flexDirection={'column'} bg={'#fff'}>
{/* header */}
<Flex py={3} px={5} borderBottom={theme.borders.base} alignItems={'center'}>
<IconButton
size={'sm'}
@@ -263,20 +270,42 @@ const AppEdit = ({ app, onBack }: Props) => {
<Box ml={5} fontSize={'xl'} flex={1}>
{app.name}
</Box>
<IconButton
icon={<MyIcon name={'save'} w={'16px'} />}
borderRadius={'lg'}
isLoading={isLoading}
aria-label={'save'}
bg={'myBlue.200'}
variant={'base'}
border={'none'}
color={'myGray.900'}
_hover={{
bg: 'myBlue.300'
}}
onClick={onclickSave}
/>
{testModules ? (
<IconButton
mr={6}
icon={<SmallCloseIcon fontSize={'25px'} />}
variant={'base'}
color={'myGray.600'}
borderRadius={'lg'}
aria-label={''}
onClick={() => setTestModules(undefined)}
/>
) : (
<MyTooltip label={'测试对话'}>
<IconButton
mr={6}
icon={<MyIcon name={'chatLight'} w={'16px'} />}
borderRadius={'lg'}
aria-label={'save'}
variant={'base'}
onClick={() => {
// @ts-ignore
onclickSave();
setTestModules(flow2Modules());
}}
/>
</MyTooltip>
)}
<MyTooltip label={'保存配置'}>
<IconButton
icon={<MyIcon name={'save'} w={'16px'} />}
borderRadius={'lg'}
isLoading={isLoading}
aria-label={'save'}
onClick={onclickSave}
/>
</MyTooltip>
</Flex>
<Box
flex={'1 0 0'}
@@ -288,6 +317,7 @@ const AppEdit = ({ app, onBack }: Props) => {
return false;
}}
>
{/* open module template */}
<IconButton
position={'absolute'}
top={5}
@@ -334,7 +364,9 @@ const AppEdit = ({ app, onBack }: Props) => {
showInteractive={false}
/>
</ReactFlow>
<TemplateList isOpen={isOpenTemplate} onAddNode={onAddNode} onClose={onCloseTemplate} />
<ChatTest modules={testModules} app={app} onClose={() => setTestModules(undefined)} />
</Box>
</Flex>
);

View File

@@ -1,14 +1,15 @@
import React, { useState, useEffect, useMemo, useCallback, useRef } from 'react';
import { useRouter } from 'next/router';
import { Box, Flex, useTheme } from '@chakra-ui/react';
import { Box, Flex, IconButton, useTheme } from '@chakra-ui/react';
import { useUserStore } from '@/store/user';
import dynamic from 'next/dynamic';
import { defaultApp } from '@/constants/model';
import Tabs from '@/components/Tabs';
import SlideTabs from '@/components/SlideTabs';
import Settings from './components/Settings';
import { defaultApp } from '@/constants/model';
import Avatar from '@/components/Avatar';
import MyIcon from '@/components/Icon';
import PageContainer from '@/components/PageContainer';
const EditApp = dynamic(() => import('./components/edit'), {
@@ -54,11 +55,11 @@ const AppDetail = ({ currentTab }: { currentTab: `${TabEnum}` }) => {
const tabList = useMemo(
() => [
{ label: '基础信息', id: TabEnum.settings, icon: 'text' },
...(isOwner ? [{ label: '编排', id: TabEnum.edit, icon: 'edit' }] : []),
{ label: '分享', id: TabEnum.share, icon: 'edit' },
{ label: 'API', id: TabEnum.API, icon: 'edit' },
{ label: '立即对话', id: 'startChat', icon: 'chat' }
{ label: '概览', id: TabEnum.settings, icon: 'overviewLight' },
...(isOwner ? [{ label: '高级设置', id: TabEnum.edit, icon: 'settingLight' }] : []),
{ label: '链接分享', id: TabEnum.share, icon: 'shareLight' },
{ label: 'API访问', id: TabEnum.API, icon: 'apiLight' },
{ label: '立即对话', id: 'startChat', icon: 'chatLight' }
],
[isOwner]
);
@@ -82,7 +83,13 @@ const AppDetail = ({ currentTab }: { currentTab: `${TabEnum}` }) => {
<PageContainer>
<Box display={['block', 'flex']} h={'100%'}>
{/* pc tab */}
<Box display={['none', 'block']} p={4} w={'200px'} borderRight={theme.borders.base}>
<Box
display={['none', 'flex']}
flexDirection={'column'}
p={4}
w={'200px'}
borderRight={theme.borders.base}
>
<Flex mb={4} alignItems={'center'}>
<Avatar src={appDetail.avatar} w={'34px'} borderRadius={'lg'} />
<Box ml={2} fontWeight={'bold'}>
@@ -90,6 +97,7 @@ const AppDetail = ({ currentTab }: { currentTab: `${TabEnum}` }) => {
</Box>
</Flex>
<SlideTabs
flex={1}
mx={'auto'}
mt={2}
w={'100%'}
@@ -103,6 +111,27 @@ const AppDetail = ({ currentTab }: { currentTab: `${TabEnum}` }) => {
}
}}
/>
<Flex
alignItems={'center'}
cursor={'pointer'}
py={2}
px={3}
borderRadius={'md'}
_hover={{ bg: 'myGray.100' }}
onClick={() => router.replace('/app/list')}
>
<IconButton
mr={3}
icon={<MyIcon name={'backFill'} w={'18px'} color={'myBlue.600'} />}
bg={'white'}
boxShadow={'1px 1px 9px rgba(0,0,0,0.15)'}
h={'28px'}
size={'sm'}
borderRadius={'50%'}
aria-label={''}
/>
</Flex>
</Box>
{/* phone tab */}
<Box display={['block', 'none']} textAlign={'center'} px={5} py={3}>

View File

@@ -57,6 +57,7 @@ const ShareHistory = dynamic(() => import('./components/ShareHistory'), {
import styles from './index.module.scss';
import { adaptChatItem_openAI } from '@/utils/plugin/openai';
import { useChat } from '@/hooks/useChat';
const textareaMinH = '22px';
@@ -65,16 +66,9 @@ const Chat = () => {
const { shareId = '', historyId } = router.query as { shareId: string; historyId: string };
const theme = useTheme();
const ChatBox = useRef<HTMLDivElement>(null);
const TextareaDom = useRef<HTMLTextAreaElement>(null);
const ContextMenuRef = useRef(null);
const PhoneContextShow = useRef(false);
// 中断请求
const controller = useRef(new AbortController());
const isLeavePage = useRef(false);
const [inputVal, setInputVal] = useState(''); // user input prompt
const [messageContextMenuData, setMessageContextMenuData] = useState<{
// message messageContextMenuData
left: number;
@@ -99,6 +93,10 @@ const Chat = () => {
[shareChatData.history]
);
const { ChatBox, ChatInput, ChatBoxParentRef, setChatHistory, scrollToBottom } = useChat({
appId: shareChatData.appId
});
const { toast } = useToast();
const { copyData } = useCopyData();
const { isPc } = useGlobalStore();
@@ -129,220 +127,6 @@ const Chat = () => {
}
});
// 滚动到底部
const scrollToBottom = useCallback((behavior: 'smooth' | 'auto' = 'smooth') => {
if (!ChatBox.current) return;
ChatBox.current.scrollTo({
top: ChatBox.current.scrollHeight,
behavior
});
}, []);
// 聊天信息生成中……获取当前滚动条位置,判断是否需要滚动到底部
// eslint-disable-next-line react-hooks/exhaustive-deps
const generatingMessage = useCallback(
throttle(() => {
if (!ChatBox.current) return;
const isBottom =
ChatBox.current.scrollTop + ChatBox.current.clientHeight + 150 >=
ChatBox.current.scrollHeight;
isBottom && scrollToBottom('auto');
}, 100),
[]
);
// 重置输入内容
const resetInputVal = useCallback((val: string) => {
setInputVal(val);
setTimeout(() => {
/* 回到最小高度 */
if (TextareaDom.current) {
TextareaDom.current.style.height =
val === '' ? textareaMinH : `${TextareaDom.current.scrollHeight}px`;
}
}, 100);
}, []);
// gpt 对话
const gptChatPrompt = useCallback(
async (prompts: ChatSiteItemType[]) => {
// create abort obj
const abortSignal = new AbortController();
controller.current = abortSignal;
isLeavePage.current = false;
const messages = adaptChatItem_openAI({ messages: prompts, reserveId: true });
// 流请求,获取数据
const { responseText } = await streamFetch({
data: {
messages: messages.slice(-shareChatData.maxContext - 1, -1),
password,
shareId,
model: ''
},
onMessage: (text: string) => {
setShareChatData((state) => ({
...state,
history: state.history.map((item, index) => {
if (index !== state.history.length - 1) return item;
return {
...item,
value: item.value + text
};
})
}));
generatingMessage();
},
abortSignal
});
// 重置了页面,说明退出了当前聊天, 不缓存任何内容
if (isLeavePage.current) {
return;
}
let responseHistory: ChatSiteItemType[] = [];
// 设置聊天内容为完成状态
setShareChatData((state) => {
responseHistory = state.history.map((item, index) => {
if (index !== state.history.length - 1) return item;
return {
...item,
status: 'finish'
};
});
return {
...state,
history: responseHistory
};
});
setShareChatHistory({
historyId,
shareId,
title: prompts[prompts.length - 2].value,
latestChat: responseText,
chats: responseHistory
});
window.top?.postMessage(
{
type: 'shareChatFinish',
data: {
question: prompts[prompts.length - 2].value,
answer: responseText
}
},
'*'
);
setTimeout(() => {
generatingMessage();
}, 100);
},
[
generatingMessage,
historyId,
password,
setShareChatData,
setShareChatHistory,
shareChatData.maxContext,
shareId
]
);
/**
* 发送一个内容
*/
const sendPrompt = useCallback(async () => {
if (isChatting) {
toast({
title: '正在聊天中...请等待结束',
status: 'warning'
});
return;
}
const storeInput = inputVal;
// 去除空行
const val = inputVal.trim().replace(/\n\s*/g, '\n');
if (!val) {
toast({
title: '内容为空',
status: 'warning'
});
return;
}
const newChatList: ChatSiteItemType[] = [
...shareChatData.history,
{
_id: String(new Types.ObjectId()),
obj: 'Human',
value: val,
status: 'finish'
},
{
_id: String(new Types.ObjectId()),
obj: 'AI',
value: '',
status: 'loading'
}
];
// 插入内容
setShareChatData((state) => ({
...state,
history: newChatList
}));
// 清空输入内容
resetInputVal('');
setTimeout(() => {
scrollToBottom();
}, 100);
try {
await gptChatPrompt(newChatList);
} catch (err: any) {
toast({
title: typeof err === 'string' ? err : err?.message || '聊天出错了~',
status: 'warning',
duration: 5000,
isClosable: true
});
resetInputVal(storeInput);
setShareChatData((state) => ({
...state,
history: newChatList.slice(0, newChatList.length - 2)
}));
}
}, [
isChatting,
inputVal,
shareChatData.history,
setShareChatData,
resetInputVal,
toast,
scrollToBottom,
gptChatPrompt
]);
// 复制内容
const onclickCopy = useCallback(
(value: string) => {
const val = value.replace(/\n+/g, '\n');
copyData(val);
},
[copyData]
);
// export chat data
const onclickExportChat = useCallback(
(type: ExportChatType) => {
@@ -411,34 +195,6 @@ const Chat = () => {
[shareChatData.history]
);
// onclick chat message context
const onclickContextMenu = useCallback(
(e: MouseEvent<HTMLDivElement>, message: ChatSiteItemType) => {
e.preventDefault(); // 阻止默认右键菜单
// select all text
const range = document.createRange();
range.selectNodeContents(e.currentTarget as HTMLDivElement);
window.getSelection()?.removeAllRanges();
window.getSelection()?.addRange(range);
navigator.vibrate?.(50); // 震动 50 毫秒
if (!isPc) {
PhoneContextShow.current = true;
}
setMessageContextMenuData({
left: e.clientX - 20,
top: e.clientY,
message
});
return false;
},
[isPc]
);
// 获取对话信息
const loadChatInfo = useCallback(async () => {
setIsLoading(true);
@@ -503,41 +259,8 @@ const Chat = () => {
return loadChatInfo();
});
// abort stream
useEffect(() => {
return () => {
window.speechSynthesis?.cancel();
isLeavePage.current = true;
controller.current?.abort();
};
}, [shareId, historyId]);
// context menu component
const RenderContextMenu = useCallback(
({ history, index }: { history: ChatSiteItemType; index: number }) => (
<MenuList fontSize={'sm'} minW={'100px !important'}>
<MenuItem onClick={() => onclickCopy(history.value)}></MenuItem>
{hasVoiceApi && (
<MenuItem
borderBottom={theme.borders.base}
onClick={() => voiceBroadcast({ text: history.value })}
>
</MenuItem>
)}
<MenuItem onClick={() => delShareChatHistoryItemById(historyId, index)}></MenuItem>
</MenuList>
),
[delShareChatHistoryItemById, historyId, onclickCopy, theme.borders.base]
);
return (
<Flex
h={'100%'}
flexDirection={['column', 'row']}
backgroundColor={useColorModeValue('#fdfdfd', '')}
>
<Flex h={'100%'} flexDirection={['column', 'row']} backgroundColor={'#fdfdfd'}>
{/* pc always show history. */}
{isPc && (
<SideBar>
@@ -612,164 +335,11 @@ const Chat = () => {
)}
</Flex>
{/* chat content box */}
<Box ref={ChatBox} pb={[4, 0]} flex={'1 0 0'} h={0} w={'100%'} overflow={'overlay'}>
<Box id={'history'}>
{shareChatData.history.map((item, index) => (
<Flex key={item._id} alignItems={'flex-start'} py={2} px={[2, 6, 8]}>
{item.obj === 'Human' && <Box flex={1} />}
{/* avatar */}
<Menu autoSelect={false} isLazy>
<MyTooltip label={item.obj === 'AI' ? '应用详情' : ''}>
<MenuButton
as={Box}
{...(item.obj === 'AI'
? {
order: 1,
mr: ['6px', 2]
}
: {
order: 3,
ml: ['6px', 2]
})}
>
<Avatar
src={
item.obj === 'Human'
? shareChatData.userAvatar || HUMAN_ICON
: shareChatData.model.avatar
}
w={['20px', '34px']}
h={['20px', '34px']}
/>
</MenuButton>
</MyTooltip>
{!isPc && <RenderContextMenu history={item} index={index} />}
</Menu>
{/* message */}
<Flex order={2} pt={2} maxW={['calc(100% - 50px)', '80%']}>
{item.obj === 'AI' ? (
<Box w={'100%'}>
<Card
bg={'white'}
px={4}
py={3}
borderRadius={'0 8px 8px 8px'}
onContextMenu={(e) => onclickContextMenu(e, item)}
>
<Markdown
source={item.value}
isChatting={isChatting && index === shareChatData.history.length - 1}
/>
</Card>
</Box>
) : (
<Box>
<Card
className="markdown"
whiteSpace={'pre-wrap'}
px={4}
py={3}
borderRadius={'8px 0 8px 8px'}
bg={'myBlue.300'}
onContextMenu={(e) => onclickContextMenu(e, item)}
>
<Box as={'p'}>{item.value}</Box>
</Card>
</Box>
)}
</Flex>
</Flex>
))}
{shareChatData.history.length === 0 && (
<Empty model={shareChatData.model} showChatProblem={false} />
)}
</Box>
<Box ref={ChatBoxParentRef} flex={1}>
<ChatBox appAvatar={shareChatData.model.avatar} />
</Box>
{/* 发送区 */}
<Box m={['0 auto', '20px auto']} w={'100%'} maxW={['auto', 'min(750px, 100%)']}>
<Box
py={'18px'}
position={'relative'}
boxShadow={`0 0 10px rgba(0,0,0,0.1)`}
borderTop={['1px solid', 0]}
borderTopColor={useColorModeValue('gray.200', 'gray.700')}
borderRadius={['none', 'md']}
backgroundColor={useColorModeValue('white', 'gray.700')}
>
{/* 输入框 */}
<Textarea
ref={TextareaDom}
py={0}
pr={['45px', '55px']}
border={'none'}
_focusVisible={{
border: 'none'
}}
placeholder="提问"
resize={'none'}
value={inputVal}
rows={1}
height={'22px'}
lineHeight={'22px'}
maxHeight={'150px'}
maxLength={-1}
overflowY={'auto'}
whiteSpace={'pre-wrap'}
wordBreak={'break-all'}
boxShadow={'none !important'}
color={useColorModeValue('blackAlpha.700', 'white')}
onChange={(e) => {
const textarea = e.target;
setInputVal(textarea.value);
textarea.style.height = textareaMinH;
textarea.style.height = `${textarea.scrollHeight}px`;
}}
onKeyDown={(e) => {
// 触发快捷发送
if (isPc && e.keyCode === 13 && !e.shiftKey) {
sendPrompt();
e.preventDefault();
}
// 全选内容
// @ts-ignore
e.key === 'a' && e.ctrlKey && e.target?.select();
}}
/>
{/* 发送和等待按键 */}
<Flex
alignItems={'center'}
justifyContent={'center'}
h={'25px'}
w={'25px'}
position={'absolute'}
right={['12px', '20px']}
bottom={'15px'}
>
{isChatting ? (
<MyIcon
className={styles.stopIcon}
width={['22px', '25px']}
height={['22px', '25px']}
cursor={'pointer'}
name={'stop'}
color={useColorModeValue('gray.500', 'white')}
onClick={() => {
controller.current?.abort();
}}
/>
) : (
<MyIcon
name={'chatSend'}
width={['18px', '20px']}
height={['18px', '20px']}
cursor={'pointer'}
color={useColorModeValue('gray.500', 'white')}
onClick={sendPrompt}
/>
)}
</Flex>
</Box>
</Box>
<ChatInput />
<Loading fixed={false} />
</Flex>
@@ -787,25 +357,6 @@ const Chat = () => {
</DrawerContent>
</Drawer>
)}
{/* context menu */}
{messageContextMenuData && (
<Box
zIndex={10}
position={'fixed'}
top={messageContextMenuData.top}
left={messageContextMenuData.left}
>
<Box ref={ContextMenuRef}></Box>
<Menu isOpen>
<RenderContextMenu
history={messageContextMenuData.message}
index={shareChatData.history.findIndex(
(item) => item._id === messageContextMenuData.message._id
)}
/>
</Menu>
</Box>
)}
{/* password input */}
{
<Modal isOpen={isOpenPassword} onClose={onClosePassword}>

View File

@@ -57,6 +57,7 @@ const defaultChatData: ChatType = {
history: []
};
const defaultShareChatData: ShareChatType = {
appId: '',
maxContext: 5,
userAvatar: HUMAN_ICON,
model: {