增加ai问片功能
This commit is contained in:
@@ -51,7 +51,9 @@
|
|||||||
"react": "^18.3.1",
|
"react": "^18.3.1",
|
||||||
"react-dom": "^18.3.1",
|
"react-dom": "^18.3.1",
|
||||||
"react-icons": "^5.4.0",
|
"react-icons": "^5.4.0",
|
||||||
|
"react-markdown": "^10.1.0",
|
||||||
"redis": "^4.6.7",
|
"redis": "^4.6.7",
|
||||||
|
"remark-gfm": "^4.0.1",
|
||||||
"socket.io": "^4.8.1",
|
"socket.io": "^4.8.1",
|
||||||
"socket.io-client": "^4.8.1",
|
"socket.io-client": "^4.8.1",
|
||||||
"swiper": "^11.2.8",
|
"swiper": "^11.2.8",
|
||||||
@@ -65,6 +67,7 @@
|
|||||||
"@commitlint/config-conventional": "^16.2.4",
|
"@commitlint/config-conventional": "^16.2.4",
|
||||||
"@svgr/webpack": "^8.1.0",
|
"@svgr/webpack": "^8.1.0",
|
||||||
"@tailwindcss/forms": "^0.5.10",
|
"@tailwindcss/forms": "^0.5.10",
|
||||||
|
"@tailwindcss/typography": "^0.5.19",
|
||||||
"@testing-library/jest-dom": "^5.17.0",
|
"@testing-library/jest-dom": "^5.17.0",
|
||||||
"@testing-library/react": "^15.0.7",
|
"@testing-library/react": "^15.0.7",
|
||||||
"@types/bs58": "^5.0.0",
|
"@types/bs58": "^5.0.0",
|
||||||
|
|||||||
Generated
+885
@@ -101,9 +101,15 @@ importers:
|
|||||||
react-icons:
|
react-icons:
|
||||||
specifier: ^5.4.0
|
specifier: ^5.4.0
|
||||||
version: 5.5.0([email protected])
|
version: 5.5.0([email protected])
|
||||||
|
react-markdown:
|
||||||
|
specifier: ^10.1.0
|
||||||
|
version: 10.1.0(@types/[email protected])([email protected])
|
||||||
redis:
|
redis:
|
||||||
specifier: ^4.6.7
|
specifier: ^4.6.7
|
||||||
version: 4.7.1
|
version: 4.7.1
|
||||||
|
remark-gfm:
|
||||||
|
specifier: ^4.0.1
|
||||||
|
version: 4.0.1
|
||||||
socket.io:
|
socket.io:
|
||||||
specifier: ^4.8.1
|
specifier: ^4.8.1
|
||||||
version: 4.8.1
|
version: 4.8.1
|
||||||
@@ -138,6 +144,9 @@ importers:
|
|||||||
'@tailwindcss/forms':
|
'@tailwindcss/forms':
|
||||||
specifier: ^0.5.10
|
specifier: ^0.5.10
|
||||||
version: 0.5.10([email protected]([email protected](@types/[email protected])([email protected])))
|
version: 0.5.10([email protected]([email protected](@types/[email protected])([email protected])))
|
||||||
|
'@tailwindcss/typography':
|
||||||
|
specifier: ^0.5.19
|
||||||
|
version: 0.5.19([email protected]([email protected](@types/[email protected])([email protected])))
|
||||||
'@testing-library/jest-dom':
|
'@testing-library/jest-dom':
|
||||||
specifier: ^5.17.0
|
specifier: ^5.17.0
|
||||||
version: 5.17.0
|
version: 5.17.0
|
||||||
@@ -1449,6 +1458,11 @@ packages:
|
|||||||
peerDependencies:
|
peerDependencies:
|
||||||
tailwindcss: '>=3.0.0 || >= 3.0.0-alpha.1 || >= 4.0.0-alpha.20 || >= 4.0.0-beta.1'
|
tailwindcss: '>=3.0.0 || >= 3.0.0-alpha.1 || >= 4.0.0-alpha.20 || >= 4.0.0-beta.1'
|
||||||
|
|
||||||
|
'@tailwindcss/[email protected]':
|
||||||
|
resolution: {integrity: sha512-w31dd8HOx3k9vPtcQh5QHP9GwKcgbMp87j58qi6xgiBnFFtKEAgCWnDw4qUT8aHwkCp8bKvb/KGKWWHedP0AAg==}
|
||||||
|
peerDependencies:
|
||||||
|
tailwindcss: '>=3.0.0 || insiders || >=4.0.0-alpha.20 || >=4.0.0-beta.1'
|
||||||
|
|
||||||
'@tanstack/[email protected]':
|
'@tanstack/[email protected]':
|
||||||
resolution: {integrity: sha512-nvrzk4E9mWB4124YdJ7/yzwou7IfHxlSef6ugCFcBfRmsnsma3heciiiV97sBNxyc3VuwtZvmwXd0aB5BpucVw==}
|
resolution: {integrity: sha512-nvrzk4E9mWB4124YdJ7/yzwou7IfHxlSef6ugCFcBfRmsnsma3heciiiV97sBNxyc3VuwtZvmwXd0aB5BpucVw==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
@@ -1525,12 +1539,18 @@ packages:
|
|||||||
'@types/[email protected]':
|
'@types/[email protected]':
|
||||||
resolution: {integrity: sha512-sDOLlVbHhXpAUAL0YHDUUwDZf3iN4Bwi4W6a0W0b+QcAezUbRtH4FVb+9J4h+XFPW7l/gQ9F8qC7P+Ec4k8QVQ==}
|
resolution: {integrity: sha512-sDOLlVbHhXpAUAL0YHDUUwDZf3iN4Bwi4W6a0W0b+QcAezUbRtH4FVb+9J4h+XFPW7l/gQ9F8qC7P+Ec4k8QVQ==}
|
||||||
|
|
||||||
|
'@types/[email protected]':
|
||||||
|
resolution: {integrity: sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==}
|
||||||
|
|
||||||
'@types/[email protected]':
|
'@types/[email protected]':
|
||||||
resolution: {integrity: sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==}
|
resolution: {integrity: sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==}
|
||||||
|
|
||||||
'@types/[email protected]':
|
'@types/[email protected]':
|
||||||
resolution: {integrity: sha512-FXx2pKgId/WyYo2jXw63kk7/+TY7u7AziEJxJAnSFzHlqTAS3Ync6SvgYAN/k4/PQpnnVuzoMuVnByKK2qp0ag==}
|
resolution: {integrity: sha512-FXx2pKgId/WyYo2jXw63kk7/+TY7u7AziEJxJAnSFzHlqTAS3Ync6SvgYAN/k4/PQpnnVuzoMuVnByKK2qp0ag==}
|
||||||
|
|
||||||
|
'@types/[email protected]':
|
||||||
|
resolution: {integrity: sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg==}
|
||||||
|
|
||||||
'@types/[email protected]':
|
'@types/[email protected]':
|
||||||
resolution: {integrity: sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw==}
|
resolution: {integrity: sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw==}
|
||||||
|
|
||||||
@@ -1543,6 +1563,9 @@ packages:
|
|||||||
'@types/[email protected]':
|
'@types/[email protected]':
|
||||||
resolution: {integrity: sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ==}
|
resolution: {integrity: sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ==}
|
||||||
|
|
||||||
|
'@types/[email protected]':
|
||||||
|
resolution: {integrity: sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==}
|
||||||
|
|
||||||
'@types/[email protected]':
|
'@types/[email protected]':
|
||||||
resolution: {integrity: sha512-q67/qwlxblDzEDvzHhVkwc1gzVWxaNxeyHUBF4xElrvjL11O+Ytze+1fGpBHlr/H9myiBUaUXNnNPmBHxxfAcA==}
|
resolution: {integrity: sha512-q67/qwlxblDzEDvzHhVkwc1gzVWxaNxeyHUBF4xElrvjL11O+Ytze+1fGpBHlr/H9myiBUaUXNnNPmBHxxfAcA==}
|
||||||
|
|
||||||
@@ -1564,6 +1587,9 @@ packages:
|
|||||||
'@types/[email protected]':
|
'@types/[email protected]':
|
||||||
resolution: {integrity: sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==}
|
resolution: {integrity: sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==}
|
||||||
|
|
||||||
|
'@types/[email protected]':
|
||||||
|
resolution: {integrity: sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==}
|
||||||
|
|
||||||
'@types/[email protected]':
|
'@types/[email protected]':
|
||||||
resolution: {integrity: sha512-Klz949h02Gz2uZCMGwDUSDS1YBlTdDDgbWHi+81l29tQALUtvz4rAYi5uoVhE5Lagoq6DeqAUlbrHvW/mXDgdQ==}
|
resolution: {integrity: sha512-Klz949h02Gz2uZCMGwDUSDS1YBlTdDDgbWHi+81l29tQALUtvz4rAYi5uoVhE5Lagoq6DeqAUlbrHvW/mXDgdQ==}
|
||||||
|
|
||||||
@@ -1573,6 +1599,9 @@ packages:
|
|||||||
'@types/[email protected]':
|
'@types/[email protected]':
|
||||||
resolution: {integrity: sha512-hov8bUuiLiyFPGyFPE1lwWhmzYbirOXQNNo40+y3zow8aFVTeyn3VWL0VFFfdNddA8S4Vf0Tc062rzyNr7Paag==}
|
resolution: {integrity: sha512-hov8bUuiLiyFPGyFPE1lwWhmzYbirOXQNNo40+y3zow8aFVTeyn3VWL0VFFfdNddA8S4Vf0Tc062rzyNr7Paag==}
|
||||||
|
|
||||||
|
'@types/[email protected]':
|
||||||
|
resolution: {integrity: sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==}
|
||||||
|
|
||||||
'@types/[email protected]':
|
'@types/[email protected]':
|
||||||
resolution: {integrity: sha512-QGpRVpzSaUs30JBSGPjOg4Uveu384erbHBoT1zeONvyCfwQxIkUshLAOqN/k9EjGviPRmWTTe6aH2qySWKTVSw==}
|
resolution: {integrity: sha512-QGpRVpzSaUs30JBSGPjOg4Uveu384erbHBoT1zeONvyCfwQxIkUshLAOqN/k9EjGviPRmWTTe6aH2qySWKTVSw==}
|
||||||
|
|
||||||
@@ -1619,6 +1648,12 @@ packages:
|
|||||||
'@types/[email protected]':
|
'@types/[email protected]':
|
||||||
resolution: {integrity: sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==}
|
resolution: {integrity: sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==}
|
||||||
|
|
||||||
|
'@types/[email protected]':
|
||||||
|
resolution: {integrity: sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==}
|
||||||
|
|
||||||
|
'@types/[email protected]':
|
||||||
|
resolution: {integrity: sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==}
|
||||||
|
|
||||||
'@types/[email protected]':
|
'@types/[email protected]':
|
||||||
resolution: {integrity: sha512-T8L6i7wCuyoK8A/ZeLYt1+q0ty3Zb9+qbSSvrIVitzT3YjZqkTZ40IbRsPanlB4h1QB3JVL1SYCdR6ngtFYcuA==}
|
resolution: {integrity: sha512-T8L6i7wCuyoK8A/ZeLYt1+q0ty3Zb9+qbSSvrIVitzT3YjZqkTZ40IbRsPanlB4h1QB3JVL1SYCdR6ngtFYcuA==}
|
||||||
|
|
||||||
@@ -2147,6 +2182,9 @@ packages:
|
|||||||
peerDependencies:
|
peerDependencies:
|
||||||
'@babel/core': ^7.0.0
|
'@babel/core': ^7.0.0
|
||||||
|
|
||||||
|
[email protected]:
|
||||||
|
resolution: {integrity: sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==}
|
||||||
|
|
||||||
[email protected]:
|
[email protected]:
|
||||||
resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==}
|
resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==}
|
||||||
|
|
||||||
@@ -2249,6 +2287,9 @@ packages:
|
|||||||
[email protected]:
|
[email protected]:
|
||||||
resolution: {integrity: sha512-Pzfx9fOKoKvevQf8oCXoyNRQ5QyxJj+3O0Rqx2V5oxT61KGx8+n6hV/IUyJeifUci2clnmmKVpvtiqRzgiWjSw==}
|
resolution: {integrity: sha512-Pzfx9fOKoKvevQf8oCXoyNRQ5QyxJj+3O0Rqx2V5oxT61KGx8+n6hV/IUyJeifUci2clnmmKVpvtiqRzgiWjSw==}
|
||||||
|
|
||||||
|
[email protected]:
|
||||||
|
resolution: {integrity: sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==}
|
||||||
|
|
||||||
[email protected]:
|
[email protected]:
|
||||||
resolution: {integrity: sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==}
|
resolution: {integrity: sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==}
|
||||||
engines: {node: '>=8'}
|
engines: {node: '>=8'}
|
||||||
@@ -2264,6 +2305,18 @@ packages:
|
|||||||
resolution: {integrity: sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==}
|
resolution: {integrity: sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==}
|
||||||
engines: {node: '>=10'}
|
engines: {node: '>=10'}
|
||||||
|
|
||||||
|
[email protected]:
|
||||||
|
resolution: {integrity: sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==}
|
||||||
|
|
||||||
|
[email protected]:
|
||||||
|
resolution: {integrity: sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==}
|
||||||
|
|
||||||
|
[email protected]:
|
||||||
|
resolution: {integrity: sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==}
|
||||||
|
|
||||||
|
[email protected]:
|
||||||
|
resolution: {integrity: sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw==}
|
||||||
|
|
||||||
[email protected]:
|
[email protected]:
|
||||||
resolution: {integrity: sha512-yrLQ/yVUFXkzg7EDQsPieE/53+0RlaWTs+wBrvW36cyilJ2SaDWfl4Yj7MtLTXleV9uEKefbAGUPv2/iWSooRA==}
|
resolution: {integrity: sha512-yrLQ/yVUFXkzg7EDQsPieE/53+0RlaWTs+wBrvW36cyilJ2SaDWfl4Yj7MtLTXleV9uEKefbAGUPv2/iWSooRA==}
|
||||||
|
|
||||||
@@ -2357,6 +2410,9 @@ packages:
|
|||||||
resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==}
|
resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==}
|
||||||
engines: {node: '>= 0.8'}
|
engines: {node: '>= 0.8'}
|
||||||
|
|
||||||
|
[email protected]:
|
||||||
|
resolution: {integrity: sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==}
|
||||||
|
|
||||||
[email protected]:
|
[email protected]:
|
||||||
resolution: {integrity: sha512-zS5PnTI22FIRM6ylNW8G4Ap0IEOyk62fhLSD0+uHRT9McRCLGpkVNvao4bjimpK/GShynyQkFFxHhwMcETmduA==}
|
resolution: {integrity: sha512-zS5PnTI22FIRM6ylNW8G4Ap0IEOyk62fhLSD0+uHRT9McRCLGpkVNvao4bjimpK/GShynyQkFFxHhwMcETmduA==}
|
||||||
engines: {node: '>=14'}
|
engines: {node: '>=14'}
|
||||||
@@ -2557,6 +2613,9 @@ packages:
|
|||||||
[email protected]:
|
[email protected]:
|
||||||
resolution: {integrity: sha512-8vDa8Qxvr/+d94hSh5P3IJwI5t8/c0KsMp+g8bNw9cY2icONa5aPfvKeieW1WlG0WQYwwhJ7mjui2xtiePQSXw==}
|
resolution: {integrity: sha512-8vDa8Qxvr/+d94hSh5P3IJwI5t8/c0KsMp+g8bNw9cY2icONa5aPfvKeieW1WlG0WQYwwhJ7mjui2xtiePQSXw==}
|
||||||
|
|
||||||
|
[email protected]:
|
||||||
|
resolution: {integrity: sha512-c6fcElNV6ShtZXmsgNgFFV5tVX2PaV4g+MOAkb8eXHvn6sryJBrZa9r0zV6+dtTyoCKxtDy5tyQ5ZwQuidtd+Q==}
|
||||||
|
|
||||||
[email protected]:
|
[email protected]:
|
||||||
resolution: {integrity: sha512-Q6fKUPqnAHAyhiUgFU7BUzLiv0kd8saH9al7tnu5Q/okj6dnupxyTgFIBjVzJATdfIAm9NAsvXNzjaKa+bxVyA==}
|
resolution: {integrity: sha512-Q6fKUPqnAHAyhiUgFU7BUzLiv0kd8saH9al7tnu5Q/okj6dnupxyTgFIBjVzJATdfIAm9NAsvXNzjaKa+bxVyA==}
|
||||||
|
|
||||||
@@ -2591,6 +2650,9 @@ packages:
|
|||||||
resolution: {integrity: sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==}
|
resolution: {integrity: sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==}
|
||||||
engines: {node: '>=8'}
|
engines: {node: '>=8'}
|
||||||
|
|
||||||
|
[email protected]:
|
||||||
|
resolution: {integrity: sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==}
|
||||||
|
|
||||||
[email protected]:
|
[email protected]:
|
||||||
resolution: {integrity: sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==}
|
resolution: {integrity: sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==}
|
||||||
|
|
||||||
@@ -2759,6 +2821,10 @@ packages:
|
|||||||
resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==}
|
resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==}
|
||||||
engines: {node: '>=10'}
|
engines: {node: '>=10'}
|
||||||
|
|
||||||
|
[email protected]:
|
||||||
|
resolution: {integrity: sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==}
|
||||||
|
engines: {node: '>=12'}
|
||||||
|
|
||||||
[email protected]:
|
[email protected]:
|
||||||
resolution: {integrity: sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==}
|
resolution: {integrity: sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==}
|
||||||
engines: {node: '>=6.0'}
|
engines: {node: '>=6.0'}
|
||||||
@@ -2914,6 +2980,9 @@ packages:
|
|||||||
resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==}
|
resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==}
|
||||||
engines: {node: '>=4.0'}
|
engines: {node: '>=4.0'}
|
||||||
|
|
||||||
|
[email protected]:
|
||||||
|
resolution: {integrity: sha512-hFtqIDZTIUZ9BXLb8y4pYGyk6+wekIivNVTcmvk8NoOh+VeRn5y6cEHzbURrWbfp1fIqdVipilzj+lfaadNZmg==}
|
||||||
|
|
||||||
[email protected]:
|
[email protected]:
|
||||||
resolution: {integrity: sha512-1fMXF3YP4pZZVozF8j/ZLfvnR8NSIljt56UhbZ5PeeDmmGHpgpdwQt7ITlGvYaQukCvuBRMLEiKiYC+oeIg4cg==}
|
resolution: {integrity: sha512-1fMXF3YP4pZZVozF8j/ZLfvnR8NSIljt56UhbZ5PeeDmmGHpgpdwQt7ITlGvYaQukCvuBRMLEiKiYC+oeIg4cg==}
|
||||||
|
|
||||||
@@ -2941,6 +3010,9 @@ packages:
|
|||||||
resolution: {integrity: sha512-xCdPp6gwiR9q9lsPCHANarIkFTN/IMZso6Kkq03sOm9IIGtzK/UJqml0dkhHibGh8HKOj8BIDIpZ0BZuU7QK6w==}
|
resolution: {integrity: sha512-xCdPp6gwiR9q9lsPCHANarIkFTN/IMZso6Kkq03sOm9IIGtzK/UJqml0dkhHibGh8HKOj8BIDIpZ0BZuU7QK6w==}
|
||||||
engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0}
|
engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0}
|
||||||
|
|
||||||
|
[email protected]:
|
||||||
|
resolution: {integrity: sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==}
|
||||||
|
|
||||||
[email protected]:
|
[email protected]:
|
||||||
resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==}
|
resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==}
|
||||||
|
|
||||||
@@ -3199,6 +3271,12 @@ packages:
|
|||||||
resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==}
|
resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==}
|
||||||
engines: {node: '>= 0.4'}
|
engines: {node: '>= 0.4'}
|
||||||
|
|
||||||
|
[email protected]:
|
||||||
|
resolution: {integrity: sha512-zl6s8LwNyo1P9uw+XJGvZtdFF1GdAkOg8ujOw+4Pyb76874fLps4ueHXDhXWdk6YHQ6OgUtinliG7RsYvCbbBg==}
|
||||||
|
|
||||||
|
[email protected]:
|
||||||
|
resolution: {integrity: sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==}
|
||||||
|
|
||||||
[email protected]:
|
[email protected]:
|
||||||
resolution: {integrity: sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==}
|
resolution: {integrity: sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
@@ -3220,6 +3298,9 @@ packages:
|
|||||||
[email protected]:
|
[email protected]:
|
||||||
resolution: {integrity: sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==}
|
resolution: {integrity: sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==}
|
||||||
|
|
||||||
|
[email protected]:
|
||||||
|
resolution: {integrity: sha512-ol6UPyBWqsrO6EJySPz2O7ZSr856WDrEzM5zMqp+FJJLGMW35cLYmmZnl0vztAZxRUoNZJFTCohfjuIJ8I4QBQ==}
|
||||||
|
|
||||||
[email protected]:
|
[email protected]:
|
||||||
resolution: {integrity: sha512-TwAZM+zE5Tq3lrEHvOlvwgj1XLWQCtaaibSN11Q+gGBAS7Y1uZSWwXXRe4iF6OXnaq1riyQAPFOBtYc77Mxq0g==}
|
resolution: {integrity: sha512-TwAZM+zE5Tq3lrEHvOlvwgj1XLWQCtaaibSN11Q+gGBAS7Y1uZSWwXXRe4iF6OXnaq1riyQAPFOBtYc77Mxq0g==}
|
||||||
|
|
||||||
@@ -3286,6 +3367,9 @@ packages:
|
|||||||
[email protected]:
|
[email protected]:
|
||||||
resolution: {integrity: sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==}
|
resolution: {integrity: sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==}
|
||||||
|
|
||||||
|
[email protected]:
|
||||||
|
resolution: {integrity: sha512-Nb2ctOyNR8DqQoR0OwRG95uNWIC0C1lCgf5Naz5H6Ji72KZ8OcFZLz2P5sNgwlyoJ8Yif11oMuYs5pBQa86csA==}
|
||||||
|
|
||||||
[email protected]:
|
[email protected]:
|
||||||
resolution: {integrity: sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==}
|
resolution: {integrity: sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==}
|
||||||
engines: {node: '>= 0.4'}
|
engines: {node: '>= 0.4'}
|
||||||
@@ -3293,6 +3377,12 @@ packages:
|
|||||||
[email protected]:
|
[email protected]:
|
||||||
resolution: {integrity: sha512-B3ex30927698TJENHR++8FfEaJGqoWOgI6ZY5Ht/nLUsFCwHn6akbwtnUAPCgUepAnTpe2qHxhDNjoKLyz6rgQ==}
|
resolution: {integrity: sha512-B3ex30927698TJENHR++8FfEaJGqoWOgI6ZY5Ht/nLUsFCwHn6akbwtnUAPCgUepAnTpe2qHxhDNjoKLyz6rgQ==}
|
||||||
|
|
||||||
|
[email protected]:
|
||||||
|
resolution: {integrity: sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ==}
|
||||||
|
|
||||||
|
[email protected]:
|
||||||
|
resolution: {integrity: sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw==}
|
||||||
|
|
||||||
[email protected]:
|
[email protected]:
|
||||||
resolution: {integrity: sha512-7bVbi0huj/wrIAOzb8U1aszg9kdi3KN/CyU19CTI7tAoZYEZoL9yCDXpbXN+uPsuWnP02cyug1gleqq+TU+YCA==}
|
resolution: {integrity: sha512-7bVbi0huj/wrIAOzb8U1aszg9kdi3KN/CyU19CTI7tAoZYEZoL9yCDXpbXN+uPsuWnP02cyug1gleqq+TU+YCA==}
|
||||||
engines: {node: '>= 0.4'}
|
engines: {node: '>= 0.4'}
|
||||||
@@ -3342,6 +3432,9 @@ packages:
|
|||||||
resolution: {integrity: sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==}
|
resolution: {integrity: sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==}
|
||||||
engines: {node: '>= 0.4'}
|
engines: {node: '>= 0.4'}
|
||||||
|
|
||||||
|
[email protected]:
|
||||||
|
resolution: {integrity: sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A==}
|
||||||
|
|
||||||
[email protected]:
|
[email protected]:
|
||||||
resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==}
|
resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==}
|
||||||
engines: {node: '>=0.10.0'}
|
engines: {node: '>=0.10.0'}
|
||||||
@@ -3374,6 +3467,9 @@ packages:
|
|||||||
resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==}
|
resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==}
|
||||||
engines: {node: '>=0.10.0'}
|
engines: {node: '>=0.10.0'}
|
||||||
|
|
||||||
|
[email protected]:
|
||||||
|
resolution: {integrity: sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg==}
|
||||||
|
|
||||||
[email protected]:
|
[email protected]:
|
||||||
resolution: {integrity: sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==}
|
resolution: {integrity: sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==}
|
||||||
engines: {node: '>= 0.4'}
|
engines: {node: '>= 0.4'}
|
||||||
@@ -3425,6 +3521,10 @@ packages:
|
|||||||
resolution: {integrity: sha512-yvkRyxmFKEOQ4pNXCmJG5AEQNlXJS5LaONXo5/cLdTZdWvsZ1ioJEonLGAosKlMWE8lwUy/bJzMjcw8az73+Fg==}
|
resolution: {integrity: sha512-yvkRyxmFKEOQ4pNXCmJG5AEQNlXJS5LaONXo5/cLdTZdWvsZ1ioJEonLGAosKlMWE8lwUy/bJzMjcw8az73+Fg==}
|
||||||
engines: {node: '>=0.10.0'}
|
engines: {node: '>=0.10.0'}
|
||||||
|
|
||||||
|
[email protected]:
|
||||||
|
resolution: {integrity: sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==}
|
||||||
|
engines: {node: '>=12'}
|
||||||
|
|
||||||
[email protected]:
|
[email protected]:
|
||||||
resolution: {integrity: sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==}
|
resolution: {integrity: sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==}
|
||||||
|
|
||||||
@@ -3862,6 +3962,9 @@ packages:
|
|||||||
resolution: {integrity: sha512-9fkkDevMefjg0mmzWFBW8YkFP91OrizzkW3diF7CpG+S2EYdy4+TVfGwz1zeF8x7hCx1ovSPTOE9Ngib74qqUg==}
|
resolution: {integrity: sha512-9fkkDevMefjg0mmzWFBW8YkFP91OrizzkW3diF7CpG+S2EYdy4+TVfGwz1zeF8x7hCx1ovSPTOE9Ngib74qqUg==}
|
||||||
engines: {node: '>=10'}
|
engines: {node: '>=10'}
|
||||||
|
|
||||||
|
[email protected]:
|
||||||
|
resolution: {integrity: sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==}
|
||||||
|
|
||||||
[email protected]:
|
[email protected]:
|
||||||
resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==}
|
resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
@@ -3913,6 +4016,9 @@ packages:
|
|||||||
resolution: {integrity: sha512-hdN1wVrZbb29eBGiGjJbeP8JbKjq1urkHJ/LIP/NY48MZ1QVXUsQBV1G1zvYFHn1XE06cwjBsOI2K3Ulnj1YXQ==}
|
resolution: {integrity: sha512-hdN1wVrZbb29eBGiGjJbeP8JbKjq1urkHJ/LIP/NY48MZ1QVXUsQBV1G1zvYFHn1XE06cwjBsOI2K3Ulnj1YXQ==}
|
||||||
engines: {node: '>=8'}
|
engines: {node: '>=8'}
|
||||||
|
|
||||||
|
[email protected]:
|
||||||
|
resolution: {integrity: sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw==}
|
||||||
|
|
||||||
[email protected]:
|
[email protected]:
|
||||||
resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==}
|
resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==}
|
||||||
engines: {node: '>= 0.4'}
|
engines: {node: '>= 0.4'}
|
||||||
@@ -3924,6 +4030,51 @@ packages:
|
|||||||
[email protected]:
|
[email protected]:
|
||||||
resolution: {integrity: sha512-T1GITYmFaKuO91vxyoQMFETst+O71VUPEU3ze5GNzDm0OWdP8v1ziTaAEPUr/3kLsY3Sftgz242A1SetQiDL7g==}
|
resolution: {integrity: sha512-T1GITYmFaKuO91vxyoQMFETst+O71VUPEU3ze5GNzDm0OWdP8v1ziTaAEPUr/3kLsY3Sftgz242A1SetQiDL7g==}
|
||||||
|
|
||||||
|
[email protected]:
|
||||||
|
resolution: {integrity: sha512-Tmd1Vg/m3Xz43afeNxDIhWRtFZgM2VLyaf4vSTYwudTyeuTneoL3qtWMA5jeLyz/O1vDJmmV4QuScFCA2tBPwg==}
|
||||||
|
|
||||||
|
[email protected]:
|
||||||
|
resolution: {integrity: sha512-uZhTV/8NBuw0WHkPTrCqDOl0zVe1BIng5ZtHoDk49ME1qqcjYmmLmOf0gELgcRMxN4w2iuIeVso5/6QymSrgmA==}
|
||||||
|
|
||||||
|
[email protected]:
|
||||||
|
resolution: {integrity: sha512-5HVP2MKaP6L+G6YaxPNjuL0BPrq9orG3TsrZ9YXbA3vDw/ACI4MEsnoDpn6ZNm7GnZgtAcONJyPhOP8tNJQavQ==}
|
||||||
|
|
||||||
|
[email protected]:
|
||||||
|
resolution: {integrity: sha512-sqpDWlsHn7Ac9GNZQMeUzPQSMzR6Wv0WKRNvQRg0KqHh02fpTz69Qc1QSseNX29bhz1ROIyNyxExfawVKTm1GQ==}
|
||||||
|
|
||||||
|
[email protected]:
|
||||||
|
resolution: {integrity: sha512-mKKb915TF+OC5ptj5bJ7WFRPdYtuHv0yTRxK2tJvi+BDqbkiG7h7u/9SI89nRAYcmap2xHQL9D+QG/6wSrTtXg==}
|
||||||
|
|
||||||
|
[email protected]:
|
||||||
|
resolution: {integrity: sha512-78UEvebzz/rJIxLvE7ZtDd/vIQ0RHv+3Mh5DR96p7cS7HsBhYIICDBCu8csTNWNO6tBWfqXPWekRuj2FNOGOZg==}
|
||||||
|
|
||||||
|
[email protected]:
|
||||||
|
resolution: {integrity: sha512-IrtvNvjxC1o06taBAVJznEnkiHxLFTzgonUdy8hzFVeDun0uTjxxrRGVaNFqkU1wJR3RBPEfsxmU6jDWPofrTQ==}
|
||||||
|
|
||||||
|
[email protected]:
|
||||||
|
resolution: {integrity: sha512-0ulfdQOM3ysHhCJ1p06l0b0VKlhU0wuQs3thxZQagjcjPrlFRqY215uZGHHJan9GEAXd9MbfPjFJz+qMkVR6zQ==}
|
||||||
|
|
||||||
|
[email protected]:
|
||||||
|
resolution: {integrity: sha512-J6f+9hUp+ldTZqKRSg7Vw5V6MqjATc+3E4gf3CFNcuZNWD8XdyI6zQ8GqH7f8169MM6P7hMBRDVGnn7oHB9kXQ==}
|
||||||
|
|
||||||
|
[email protected]:
|
||||||
|
resolution: {integrity: sha512-lj/z8v0r6ZtsN/cGNNtemmmfoLAFZnjMbNyLzBafjzikOM+glrjNHPlf6lQDOTccj9n5b0PPihEBbhneMyGs1Q==}
|
||||||
|
|
||||||
|
[email protected]:
|
||||||
|
resolution: {integrity: sha512-EcmOpxsZ96CvlP03NghtH1EsLtr0n9Tm4lPUJUBccV9RwUOneqSycg19n5HGzCf+10LozMRSObtVr3ee1WoHtg==}
|
||||||
|
|
||||||
|
[email protected]:
|
||||||
|
resolution: {integrity: sha512-TqICwyvJJpBwvGAMZjj4J2n0X8QWp21b9l0o7eXyVJ25YNWYbJDVIyD1bZXE6WtV6RmKJVYmQAKWa0zWOABz2w==}
|
||||||
|
|
||||||
|
[email protected]:
|
||||||
|
resolution: {integrity: sha512-cctsq2wp5vTsLIcaymblUriiTcZd0CwWtCbLvrOzYCDZoWyMNV8sZ7krj09FSnsiJi3WVsHLM4k6Dq/yaPyCXA==}
|
||||||
|
|
||||||
|
[email protected]:
|
||||||
|
resolution: {integrity: sha512-xj68wMTvGXVOKonmog6LwyJKrYXZPvlwabaryTjLh9LuvovB/KAH+kvi8Gjj+7rJjsFi23nkUxRQv1KqSroMqA==}
|
||||||
|
|
||||||
|
[email protected]:
|
||||||
|
resolution: {integrity: sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==}
|
||||||
|
|
||||||
[email protected]:
|
[email protected]:
|
||||||
resolution: {integrity: sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g==}
|
resolution: {integrity: sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g==}
|
||||||
|
|
||||||
@@ -3953,6 +4104,90 @@ packages:
|
|||||||
resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==}
|
resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==}
|
||||||
engines: {node: '>= 8'}
|
engines: {node: '>= 8'}
|
||||||
|
|
||||||
|
[email protected]:
|
||||||
|
resolution: {integrity: sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg==}
|
||||||
|
|
||||||
|
[email protected]:
|
||||||
|
resolution: {integrity: sha512-oOg7knzhicgQ3t4QCjCWgTmfNhvQbDDnJeVu9v81r7NltNCVmhPy1fJRX27pISafdjL+SVc4d3l48Gb6pbRypw==}
|
||||||
|
|
||||||
|
[email protected]:
|
||||||
|
resolution: {integrity: sha512-/yPhxI1ntnDNsiHtzLKYnE3vf9JZ6cAisqVDauhp4CEHxlb4uoOTxOCJ+9s51bIB8U1N1FJ1RXOKTIlD5B/gqw==}
|
||||||
|
|
||||||
|
[email protected]:
|
||||||
|
resolution: {integrity: sha512-ADVjpOOkjz1hhkZLlBiYA9cR2Anf8F4HqZUO6e5eDcPQd0Txw5fxLzzxnEkSkfnD0wziSGiv7sYhk/ktvbf1uw==}
|
||||||
|
|
||||||
|
[email protected]:
|
||||||
|
resolution: {integrity: sha512-t2OU/dXXioARrC6yWfJ4hqB7rct14e8f7m0cbI5hUmDyyIlwv5vEtooptH8INkbLzOatzKuVbQmAYcbWoyz6Dg==}
|
||||||
|
|
||||||
|
[email protected]:
|
||||||
|
resolution: {integrity: sha512-xHlTOmuCSotIA8TW1mDIM6X2O1SiX5P9IuDtqGonFhEK0qgRI4yeC6vMxEV2dgyr2TiD+2PQ10o+cOhdVAcwfg==}
|
||||||
|
|
||||||
|
[email protected]:
|
||||||
|
resolution: {integrity: sha512-qIBZhqxqI6fjLDYFTBIa4eivDMnP+OZqsNwmQ3xNLE4Cxwc+zfQEfbs6tzAo2Hjq+bh6q5F+Z8/cksrLFYWQQw==}
|
||||||
|
|
||||||
|
[email protected]:
|
||||||
|
resolution: {integrity: sha512-vsKArQsicm7t0z2GugkCKtZehqUm31oeGBV/KVSorWSy8ZlNAv7ytjFhvaryUiCUJYqs+NoE6AFhpQvBTM6Q4w==}
|
||||||
|
|
||||||
|
[email protected]:
|
||||||
|
resolution: {integrity: sha512-Xe6rDdJlkmbFRExpTOmRj9N3MaWmbAgdpSrBQvCFqhezUn4AHqJHbaEnfbVYYiexVSs//tqOdY/DxhjdCiJnIA==}
|
||||||
|
|
||||||
|
[email protected]:
|
||||||
|
resolution: {integrity: sha512-VFMekyQExqIW7xIChcXn4ok29YE3rnuyveW3wZQWWqF4Nv9Wk5rgJ99KzPvHjkmPXF93FXIbBp6YdW3t71/7Vg==}
|
||||||
|
|
||||||
|
[email protected]:
|
||||||
|
resolution: {integrity: sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg==}
|
||||||
|
|
||||||
|
[email protected]:
|
||||||
|
resolution: {integrity: sha512-5bZ+3CjhAd9eChYTHsjy6TGxpOFSKgKKJPJxr293jTbfry2KDoWkhBb6TcPVB4NmzaPhMs1Frm9AZH7OD4Cjzw==}
|
||||||
|
|
||||||
|
[email protected]:
|
||||||
|
resolution: {integrity: sha512-Ob0nuZ3PKt/n0hORHyvoD9uZhr+Za8sFoP+OnMcnWK5lngSzALgQYKMr9RJVOWLqQYuyn6ulqGWSXdwf6F80lQ==}
|
||||||
|
|
||||||
|
[email protected]:
|
||||||
|
resolution: {integrity: sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==}
|
||||||
|
|
||||||
|
[email protected]:
|
||||||
|
resolution: {integrity: sha512-QUNFEOPELfmvv+4xiNg2sRYeS/P84pTW0TCgP5zc9FpXetHY0ab7SxKyAQCNCc1eK0459uoLI1y5oO5Vc1dbhA==}
|
||||||
|
|
||||||
|
[email protected]:
|
||||||
|
resolution: {integrity: sha512-K0kHzM6afW/MbeWYWLjoHQv1sgg2Q9EccHEDzSkxiP/EaagNzCm7T/WMKZ3rjMbvIpvBiZgwR3dKMygtA4mG1Q==}
|
||||||
|
|
||||||
|
[email protected]:
|
||||||
|
resolution: {integrity: sha512-OnAnH8Ujmy59JcyZw8JSbK9cGpdVY44NKgSM7E9Eh7DiLS2E9RNQf0dONaGDzEG9yjEl5hcqeIsj4hfRkLH/Bg==}
|
||||||
|
|
||||||
|
[email protected]:
|
||||||
|
resolution: {integrity: sha512-ccUbYk6CwVdkmCQMyr64dXz42EfHGkPQlBj5p7YVGzq8I7CtjXZJrubAYezf7Rp+bjPseiROqe7G6foFd+lEuw==}
|
||||||
|
|
||||||
|
[email protected]:
|
||||||
|
resolution: {integrity: sha512-nDV/77Fj6eH1ynwscYTOsbK7rR//Uj0bZXBwJZRfaLEJ1iGBR6kIfNmlNqaqJf649EP0F3NWNdeJi03elllNUQ==}
|
||||||
|
|
||||||
|
[email protected]:
|
||||||
|
resolution: {integrity: sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==}
|
||||||
|
|
||||||
|
[email protected]:
|
||||||
|
resolution: {integrity: sha512-2cNEiYDhCWKI+Gs9T0Tiysk136SnR13hhO8yW6BGNyhOC4qYFnwF1nKfD3HFAIXA5c45RrIG1ub11GiXeYd1xA==}
|
||||||
|
|
||||||
|
[email protected]:
|
||||||
|
resolution: {integrity: sha512-sxPqmo70LyARJs0w2UclACPUUEqltCkJ6PhKdMIDuJ3gSf/Q+/GIe3WKl0Ijb/GyH9lOpUkRAO2wp0GVkLvS9Q==}
|
||||||
|
|
||||||
|
[email protected]:
|
||||||
|
resolution: {integrity: sha512-VdQyxFWFT2/FGJgwQnJYbe1jjQoNTS4RjglmSjTUlpUMa95Htx9NHeYW4rGDJzbjvCsl9eLjMQwGeElsqmzcHg==}
|
||||||
|
|
||||||
|
[email protected]:
|
||||||
|
resolution: {integrity: sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==}
|
||||||
|
|
||||||
|
[email protected]:
|
||||||
|
resolution: {integrity: sha512-XQLu552iSctvnEcgXw6+Sx75GflAPNED1qx7eBJ+wydBb2KCbRZe+NwvIEEMM83uml1+2WSXpBAcp9IUCgCYWA==}
|
||||||
|
|
||||||
|
[email protected]:
|
||||||
|
resolution: {integrity: sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==}
|
||||||
|
|
||||||
|
[email protected]:
|
||||||
|
resolution: {integrity: sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA==}
|
||||||
|
|
||||||
|
[email protected]:
|
||||||
|
resolution: {integrity: sha512-zpe98Q6kvavpCr1NPVSCMebCKfD7CA2NqZ+rykeNhONIJBpc1tFKt9hucLGwha3jNTNI8lHpctWJWoimVF4PfA==}
|
||||||
|
|
||||||
[email protected]:
|
[email protected]:
|
||||||
resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==}
|
resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==}
|
||||||
engines: {node: '>=8.6'}
|
engines: {node: '>=8.6'}
|
||||||
@@ -4236,6 +4471,9 @@ packages:
|
|||||||
resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==}
|
resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==}
|
||||||
engines: {node: '>=6'}
|
engines: {node: '>=6'}
|
||||||
|
|
||||||
|
[email protected]:
|
||||||
|
resolution: {integrity: sha512-GG2AQYWoLgL877gQIKeRPGO1xF9+eG1ujIb5soS5gPvLQ1y2o8FL90w2QWNdf9I361Mpp7726c+lj3U0qK1uGw==}
|
||||||
|
|
||||||
[email protected]:
|
[email protected]:
|
||||||
resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==}
|
resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==}
|
||||||
engines: {node: '>=8'}
|
engines: {node: '>=8'}
|
||||||
@@ -4355,6 +4593,10 @@ packages:
|
|||||||
peerDependencies:
|
peerDependencies:
|
||||||
postcss: ^8.2.14
|
postcss: ^8.2.14
|
||||||
|
|
||||||
|
[email protected]:
|
||||||
|
resolution: {integrity: sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w==}
|
||||||
|
engines: {node: '>=4'}
|
||||||
|
|
||||||
[email protected]:
|
[email protected]:
|
||||||
resolution: {integrity: sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==}
|
resolution: {integrity: sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==}
|
||||||
engines: {node: '>=4'}
|
engines: {node: '>=4'}
|
||||||
@@ -4458,6 +4700,9 @@ packages:
|
|||||||
[email protected]:
|
[email protected]:
|
||||||
resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==}
|
resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==}
|
||||||
|
|
||||||
|
[email protected]:
|
||||||
|
resolution: {integrity: sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ==}
|
||||||
|
|
||||||
[email protected]:
|
[email protected]:
|
||||||
resolution: {integrity: sha512-JZd3gMVBAVQkSs6HdNZo9Sdo0LNcQeMNP3CozBJb3JYC/QUYZTnKxP+f8oWRX4rHP5EurWxqAHTSwUCjlNKa1w==}
|
resolution: {integrity: sha512-JZd3gMVBAVQkSs6HdNZo9Sdo0LNcQeMNP3CozBJb3JYC/QUYZTnKxP+f8oWRX4rHP5EurWxqAHTSwUCjlNKa1w==}
|
||||||
|
|
||||||
@@ -4505,6 +4750,12 @@ packages:
|
|||||||
[email protected]:
|
[email protected]:
|
||||||
resolution: {integrity: sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==}
|
resolution: {integrity: sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==}
|
||||||
|
|
||||||
|
[email protected]:
|
||||||
|
resolution: {integrity: sha512-qKxVopLT/TyA6BX3Ue5NwabOsAzm0Q7kAPwq6L+wWDwisYs7R8vZ0nRXqq6rkueboxpkjvLGU9fWifiX/ZZFxQ==}
|
||||||
|
peerDependencies:
|
||||||
|
'@types/react': '>=18'
|
||||||
|
react: '>=18'
|
||||||
|
|
||||||
[email protected]:
|
[email protected]:
|
||||||
resolution: {integrity: sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==}
|
resolution: {integrity: sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==}
|
||||||
engines: {node: '>=0.10.0'}
|
engines: {node: '>=0.10.0'}
|
||||||
@@ -4564,6 +4815,18 @@ packages:
|
|||||||
resolution: {integrity: sha512-cnE+y8bz4NhMjISKbgeVJtqNbtf5QpjZP+Bslo+UqkIt9QPnX9q095eiRRASJG1/tz6dlNr6Z5NsBiWYokp6EQ==}
|
resolution: {integrity: sha512-cnE+y8bz4NhMjISKbgeVJtqNbtf5QpjZP+Bslo+UqkIt9QPnX9q095eiRRASJG1/tz6dlNr6Z5NsBiWYokp6EQ==}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
|
|
||||||
|
[email protected]:
|
||||||
|
resolution: {integrity: sha512-1quofZ2RQ9EWdeN34S79+KExV1764+wCUGop5CPL1WGdD0ocPpu91lzPGbwWMECpEpd42kJGQwzRfyov9j4yNg==}
|
||||||
|
|
||||||
|
[email protected]:
|
||||||
|
resolution: {integrity: sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA==}
|
||||||
|
|
||||||
|
[email protected]:
|
||||||
|
resolution: {integrity: sha512-Dh7l57ianaEoIpzbp0PC9UKAdCSVklD8E5Rpw7ETfbTl3FqcOOgq5q2LVDhgGCkaBv7p24JXikPdvhhmHvKMsw==}
|
||||||
|
|
||||||
|
[email protected]:
|
||||||
|
resolution: {integrity: sha512-1OSmLd3awB/t8qdoEOMazZkNsfVTeY4fTsgzcQFdXNq8ToTN4ZGwrMnlda4K6smTFKD+GRV6O48i6Z4iKgPPpw==}
|
||||||
|
|
||||||
[email protected]:
|
[email protected]:
|
||||||
resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==}
|
resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==}
|
||||||
engines: {node: '>=0.10.0'}
|
engines: {node: '>=0.10.0'}
|
||||||
@@ -4819,6 +5082,9 @@ packages:
|
|||||||
resolution: {integrity: sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==}
|
resolution: {integrity: sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==}
|
||||||
deprecated: Please use @jridgewell/sourcemap-codec instead
|
deprecated: Please use @jridgewell/sourcemap-codec instead
|
||||||
|
|
||||||
|
[email protected]:
|
||||||
|
resolution: {integrity: sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==}
|
||||||
|
|
||||||
[email protected]:
|
[email protected]:
|
||||||
resolution: {integrity: sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==}
|
resolution: {integrity: sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==}
|
||||||
|
|
||||||
@@ -4897,6 +5163,9 @@ packages:
|
|||||||
[email protected]:
|
[email protected]:
|
||||||
resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==}
|
resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==}
|
||||||
|
|
||||||
|
[email protected]:
|
||||||
|
resolution: {integrity: sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==}
|
||||||
|
|
||||||
[email protected]:
|
[email protected]:
|
||||||
resolution: {integrity: sha512-rHqiFh1elqCQ9WPLIC8I0Q/g/wj5J1eMkyoiD6eoQApWHP0FtlK7rqnhmabL5VUY9JQCcqwwvlOaSuutekgyrw==}
|
resolution: {integrity: sha512-rHqiFh1elqCQ9WPLIC8I0Q/g/wj5J1eMkyoiD6eoQApWHP0FtlK7rqnhmabL5VUY9JQCcqwwvlOaSuutekgyrw==}
|
||||||
engines: {node: '>=4'}
|
engines: {node: '>=4'}
|
||||||
@@ -4936,6 +5205,12 @@ packages:
|
|||||||
resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==}
|
resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==}
|
||||||
engines: {node: '>=8'}
|
engines: {node: '>=8'}
|
||||||
|
|
||||||
|
[email protected]:
|
||||||
|
resolution: {integrity: sha512-RjQetxJrrUJLQPHbLku6U/ocGtzyjbJMP9lCNK7Ag0CNh690nSH8woqWH9u16nMjYBAok+i7JO1NP2pOy8IsPQ==}
|
||||||
|
|
||||||
|
[email protected]:
|
||||||
|
resolution: {integrity: sha512-LIN7rULI0jBscWQYaSswptyderlarFkjQ+t79nzty8tcIAceVomEVlLzH5VP4Cmsv6MtKhs7qaAiwlcp+Mgaxw==}
|
||||||
|
|
||||||
[email protected]:
|
[email protected]:
|
||||||
resolution: {integrity: sha512-pW7uC1l4mBZ8ugbiZrcIsiIvVx1UmTfw7UkC3Um2tmfUq9Bhk8IiyEIPl6F8agHgjzku6j0xQEZbfA5uSgSaCw==}
|
resolution: {integrity: sha512-pW7uC1l4mBZ8ugbiZrcIsiIvVx1UmTfw7UkC3Um2tmfUq9Bhk8IiyEIPl6F8agHgjzku6j0xQEZbfA5uSgSaCw==}
|
||||||
engines: {node: '>= 12.0.0'}
|
engines: {node: '>= 12.0.0'}
|
||||||
@@ -5089,10 +5364,16 @@ packages:
|
|||||||
resolution: {integrity: sha512-15Ih7phfcdP5YxqiB+iDtLoaTz4Nd35+IiAv0kQ5FNKHzXgdWqPoTIqEDDJmXceQt4JZk6lVPT8lnDlPpGDppw==}
|
resolution: {integrity: sha512-15Ih7phfcdP5YxqiB+iDtLoaTz4Nd35+IiAv0kQ5FNKHzXgdWqPoTIqEDDJmXceQt4JZk6lVPT8lnDlPpGDppw==}
|
||||||
engines: {node: '>=8'}
|
engines: {node: '>=8'}
|
||||||
|
|
||||||
|
[email protected]:
|
||||||
|
resolution: {integrity: sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==}
|
||||||
|
|
||||||
[email protected]:
|
[email protected]:
|
||||||
resolution: {integrity: sha512-c1PTsA3tYrIsLGkJkzHF+w9F2EyxfXGo4UyJc4pFL++FMjnq0HJS69T3M7d//gKrFKwy429bouPescbjecU+Zw==}
|
resolution: {integrity: sha512-c1PTsA3tYrIsLGkJkzHF+w9F2EyxfXGo4UyJc4pFL++FMjnq0HJS69T3M7d//gKrFKwy429bouPescbjecU+Zw==}
|
||||||
engines: {node: '>=8'}
|
engines: {node: '>=8'}
|
||||||
|
|
||||||
|
[email protected]:
|
||||||
|
resolution: {integrity: sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw==}
|
||||||
|
|
||||||
[email protected]:
|
[email protected]:
|
||||||
resolution: {integrity: sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==}
|
resolution: {integrity: sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==}
|
||||||
|
|
||||||
@@ -5222,10 +5503,28 @@ packages:
|
|||||||
resolution: {integrity: sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w==}
|
resolution: {integrity: sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w==}
|
||||||
engines: {node: '>=4'}
|
engines: {node: '>=4'}
|
||||||
|
|
||||||
|
[email protected]:
|
||||||
|
resolution: {integrity: sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA==}
|
||||||
|
|
||||||
[email protected]:
|
[email protected]:
|
||||||
resolution: {integrity: sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg==}
|
resolution: {integrity: sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg==}
|
||||||
engines: {node: '>=8'}
|
engines: {node: '>=8'}
|
||||||
|
|
||||||
|
[email protected]:
|
||||||
|
resolution: {integrity: sha512-LsiILbtBETkDz8I9p1dQ0uyRUWuaQzd/cuEeS1hoRSyW5E5XGmTzlwY1OrNzzakGowI9Dr/I8HVaw4hTtnxy8g==}
|
||||||
|
|
||||||
|
[email protected]:
|
||||||
|
resolution: {integrity: sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==}
|
||||||
|
|
||||||
|
[email protected]:
|
||||||
|
resolution: {integrity: sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==}
|
||||||
|
|
||||||
|
[email protected]:
|
||||||
|
resolution: {integrity: sha512-goh1s1TBrqSqukSc8wrjwWhL0hiJxgA8m4kFxGlQ+8FYQ3C/m11FcTs4YYem7V664AhHVvgoQLk890Ssdsr2IQ==}
|
||||||
|
|
||||||
|
[email protected]:
|
||||||
|
resolution: {integrity: sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg==}
|
||||||
|
|
||||||
[email protected]:
|
[email protected]:
|
||||||
resolution: {integrity: sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==}
|
resolution: {integrity: sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==}
|
||||||
engines: {node: '>= 4.0.0'}
|
engines: {node: '>= 4.0.0'}
|
||||||
@@ -5288,6 +5587,12 @@ packages:
|
|||||||
resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==}
|
resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==}
|
||||||
engines: {node: '>= 0.8'}
|
engines: {node: '>= 0.8'}
|
||||||
|
|
||||||
|
[email protected]:
|
||||||
|
resolution: {integrity: sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw==}
|
||||||
|
|
||||||
|
[email protected]:
|
||||||
|
resolution: {integrity: sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==}
|
||||||
|
|
||||||
[email protected]:
|
[email protected]:
|
||||||
resolution: {integrity: sha512-pI2aixBuOpu/LSnRgNJ40tU/KFW+x1X+O2bW1hz946ZZShDM5oqRXF9pavDOuckHAHPgUN9HYUr9vUNTBUPF1Q==}
|
resolution: {integrity: sha512-pI2aixBuOpu/LSnRgNJ40tU/KFW+x1X+O2bW1hz946ZZShDM5oqRXF9pavDOuckHAHPgUN9HYUr9vUNTBUPF1Q==}
|
||||||
engines: {node: '>=16'}
|
engines: {node: '>=16'}
|
||||||
@@ -5560,6 +5865,9 @@ packages:
|
|||||||
[email protected]:
|
[email protected]:
|
||||||
resolution: {integrity: sha512-idA2YXwpCdqUSKRCACDE6ItZD9TZzy3OZMtpfLoh6oPR47lipysRrJfjzMqFxQ3uJuUPyUeWe1r9vLH33xO/Qw==}
|
resolution: {integrity: sha512-idA2YXwpCdqUSKRCACDE6ItZD9TZzy3OZMtpfLoh6oPR47lipysRrJfjzMqFxQ3uJuUPyUeWe1r9vLH33xO/Qw==}
|
||||||
|
|
||||||
|
[email protected]:
|
||||||
|
resolution: {integrity: sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==}
|
||||||
|
|
||||||
snapshots:
|
snapshots:
|
||||||
|
|
||||||
'@adobe/[email protected]': {}
|
'@adobe/[email protected]': {}
|
||||||
@@ -7126,6 +7434,11 @@ snapshots:
|
|||||||
mini-svg-data-uri: 1.4.4
|
mini-svg-data-uri: 1.4.4
|
||||||
tailwindcss: 3.4.17([email protected](@types/[email protected])([email protected]))
|
tailwindcss: 3.4.17([email protected](@types/[email protected])([email protected]))
|
||||||
|
|
||||||
|
'@tailwindcss/[email protected]([email protected]([email protected](@types/[email protected])([email protected])))':
|
||||||
|
dependencies:
|
||||||
|
postcss-selector-parser: 6.0.10
|
||||||
|
tailwindcss: 3.4.17([email protected](@types/[email protected])([email protected]))
|
||||||
|
|
||||||
'@tanstack/[email protected]([email protected]([email protected]))([email protected])':
|
'@tanstack/[email protected]([email protected]([email protected]))([email protected])':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@tanstack/virtual-core': 3.13.10
|
'@tanstack/virtual-core': 3.13.10
|
||||||
@@ -7217,6 +7530,10 @@ snapshots:
|
|||||||
|
|
||||||
'@types/[email protected]': {}
|
'@types/[email protected]': {}
|
||||||
|
|
||||||
|
'@types/[email protected]':
|
||||||
|
dependencies:
|
||||||
|
'@types/ms': 2.1.0
|
||||||
|
|
||||||
'@types/[email protected]':
|
'@types/[email protected]':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/eslint': 9.6.1
|
'@types/eslint': 9.6.1
|
||||||
@@ -7227,6 +7544,10 @@ snapshots:
|
|||||||
'@types/estree': 1.0.8
|
'@types/estree': 1.0.8
|
||||||
'@types/json-schema': 7.0.15
|
'@types/json-schema': 7.0.15
|
||||||
|
|
||||||
|
'@types/[email protected]':
|
||||||
|
dependencies:
|
||||||
|
'@types/estree': 1.0.8
|
||||||
|
|
||||||
'@types/[email protected]': {}
|
'@types/[email protected]': {}
|
||||||
|
|
||||||
'@types/[email protected]': {}
|
'@types/[email protected]': {}
|
||||||
@@ -7240,6 +7561,10 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
'@types/node': 24.0.3
|
'@types/node': 24.0.3
|
||||||
|
|
||||||
|
'@types/[email protected]':
|
||||||
|
dependencies:
|
||||||
|
'@types/unist': 3.0.3
|
||||||
|
|
||||||
'@types/[email protected]': {}
|
'@types/[email protected]': {}
|
||||||
|
|
||||||
'@types/[email protected]': {}
|
'@types/[email protected]': {}
|
||||||
@@ -7261,12 +7586,18 @@ snapshots:
|
|||||||
|
|
||||||
'@types/[email protected]': {}
|
'@types/[email protected]': {}
|
||||||
|
|
||||||
|
'@types/[email protected]':
|
||||||
|
dependencies:
|
||||||
|
'@types/unist': 3.0.3
|
||||||
|
|
||||||
'@types/[email protected]': {}
|
'@types/[email protected]': {}
|
||||||
|
|
||||||
'@types/[email protected]': {}
|
'@types/[email protected]': {}
|
||||||
|
|
||||||
'@types/[email protected]': {}
|
'@types/[email protected]': {}
|
||||||
|
|
||||||
|
'@types/[email protected]': {}
|
||||||
|
|
||||||
'@types/[email protected]':
|
'@types/[email protected]':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/node': 24.0.3
|
'@types/node': 24.0.3
|
||||||
@@ -7311,6 +7642,10 @@ snapshots:
|
|||||||
|
|
||||||
'@types/[email protected]': {}
|
'@types/[email protected]': {}
|
||||||
|
|
||||||
|
'@types/[email protected]': {}
|
||||||
|
|
||||||
|
'@types/[email protected]': {}
|
||||||
|
|
||||||
'@types/[email protected]': {}
|
'@types/[email protected]': {}
|
||||||
|
|
||||||
'@types/[email protected]':
|
'@types/[email protected]':
|
||||||
@@ -7898,6 +8233,8 @@ snapshots:
|
|||||||
babel-plugin-jest-hoist: 27.5.1
|
babel-plugin-jest-hoist: 27.5.1
|
||||||
babel-preset-current-node-syntax: 1.1.0(@babel/[email protected])
|
babel-preset-current-node-syntax: 1.1.0(@babel/[email protected])
|
||||||
|
|
||||||
|
[email protected]: {}
|
||||||
|
|
||||||
[email protected]: {}
|
[email protected]: {}
|
||||||
|
|
||||||
[email protected]: {}
|
[email protected]: {}
|
||||||
@@ -7993,6 +8330,8 @@ snapshots:
|
|||||||
|
|
||||||
[email protected]: {}
|
[email protected]: {}
|
||||||
|
|
||||||
|
[email protected]: {}
|
||||||
|
|
||||||
[email protected]:
|
[email protected]:
|
||||||
dependencies:
|
dependencies:
|
||||||
ansi-styles: 4.3.0
|
ansi-styles: 4.3.0
|
||||||
@@ -8007,6 +8346,14 @@ snapshots:
|
|||||||
|
|
||||||
[email protected]: {}
|
[email protected]: {}
|
||||||
|
|
||||||
|
[email protected]: {}
|
||||||
|
|
||||||
|
[email protected]: {}
|
||||||
|
|
||||||
|
[email protected]: {}
|
||||||
|
|
||||||
|
[email protected]: {}
|
||||||
|
|
||||||
[email protected]: {}
|
[email protected]: {}
|
||||||
|
|
||||||
[email protected]:
|
[email protected]:
|
||||||
@@ -8113,6 +8460,8 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
delayed-stream: 1.0.0
|
delayed-stream: 1.0.0
|
||||||
|
|
||||||
|
[email protected]: {}
|
||||||
|
|
||||||
[email protected]: {}
|
[email protected]: {}
|
||||||
|
|
||||||
[email protected]: {}
|
[email protected]: {}
|
||||||
@@ -8299,6 +8648,10 @@ snapshots:
|
|||||||
|
|
||||||
[email protected]: {}
|
[email protected]: {}
|
||||||
|
|
||||||
|
[email protected]:
|
||||||
|
dependencies:
|
||||||
|
character-entities: 2.0.2
|
||||||
|
|
||||||
[email protected]: {}
|
[email protected]: {}
|
||||||
|
|
||||||
[email protected]: {}
|
[email protected]: {}
|
||||||
@@ -8333,6 +8686,10 @@ snapshots:
|
|||||||
|
|
||||||
[email protected]: {}
|
[email protected]: {}
|
||||||
|
|
||||||
|
[email protected]:
|
||||||
|
dependencies:
|
||||||
|
dequal: 2.0.3
|
||||||
|
|
||||||
[email protected]: {}
|
[email protected]: {}
|
||||||
|
|
||||||
[email protected]: {}
|
[email protected]: {}
|
||||||
@@ -8571,6 +8928,8 @@ snapshots:
|
|||||||
|
|
||||||
[email protected]: {}
|
[email protected]: {}
|
||||||
|
|
||||||
|
[email protected]: {}
|
||||||
|
|
||||||
[email protected]:
|
[email protected]:
|
||||||
dependencies:
|
dependencies:
|
||||||
esprima: 4.0.1
|
esprima: 4.0.1
|
||||||
@@ -8806,6 +9165,8 @@ snapshots:
|
|||||||
|
|
||||||
[email protected]: {}
|
[email protected]: {}
|
||||||
|
|
||||||
|
[email protected]: {}
|
||||||
|
|
||||||
[email protected]: {}
|
[email protected]: {}
|
||||||
|
|
||||||
[email protected]: {}
|
[email protected]: {}
|
||||||
@@ -8842,6 +9203,8 @@ snapshots:
|
|||||||
jest-mock: 30.0.0
|
jest-mock: 30.0.0
|
||||||
jest-util: 30.0.0
|
jest-util: 30.0.0
|
||||||
|
|
||||||
|
[email protected]: {}
|
||||||
|
|
||||||
[email protected]: {}
|
[email protected]: {}
|
||||||
|
|
||||||
[email protected]:
|
[email protected]:
|
||||||
@@ -9125,6 +9488,30 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
function-bind: 1.1.2
|
function-bind: 1.1.2
|
||||||
|
|
||||||
|
[email protected]:
|
||||||
|
dependencies:
|
||||||
|
'@types/estree': 1.0.8
|
||||||
|
'@types/hast': 3.0.4
|
||||||
|
'@types/unist': 3.0.3
|
||||||
|
comma-separated-tokens: 2.0.3
|
||||||
|
devlop: 1.1.0
|
||||||
|
estree-util-is-identifier-name: 3.0.0
|
||||||
|
hast-util-whitespace: 3.0.0
|
||||||
|
mdast-util-mdx-expression: 2.0.1
|
||||||
|
mdast-util-mdx-jsx: 3.2.0
|
||||||
|
mdast-util-mdxjs-esm: 2.0.1
|
||||||
|
property-information: 7.1.0
|
||||||
|
space-separated-tokens: 2.0.2
|
||||||
|
style-to-js: 1.1.21
|
||||||
|
unist-util-position: 5.0.0
|
||||||
|
vfile-message: 4.0.3
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- supports-color
|
||||||
|
|
||||||
|
[email protected]:
|
||||||
|
dependencies:
|
||||||
|
'@types/hast': 3.0.4
|
||||||
|
|
||||||
[email protected]: {}
|
[email protected]: {}
|
||||||
|
|
||||||
[email protected]: {}
|
[email protected]: {}
|
||||||
@@ -9141,6 +9528,8 @@ snapshots:
|
|||||||
|
|
||||||
[email protected]: {}
|
[email protected]: {}
|
||||||
|
|
||||||
|
[email protected]: {}
|
||||||
|
|
||||||
[email protected]:
|
[email protected]:
|
||||||
dependencies:
|
dependencies:
|
||||||
domelementtype: 2.3.0
|
domelementtype: 2.3.0
|
||||||
@@ -9209,6 +9598,8 @@ snapshots:
|
|||||||
|
|
||||||
[email protected]: {}
|
[email protected]: {}
|
||||||
|
|
||||||
|
[email protected]: {}
|
||||||
|
|
||||||
[email protected]:
|
[email protected]:
|
||||||
dependencies:
|
dependencies:
|
||||||
es-errors: 1.3.0
|
es-errors: 1.3.0
|
||||||
@@ -9217,6 +9608,13 @@ snapshots:
|
|||||||
|
|
||||||
[email protected]: {}
|
[email protected]: {}
|
||||||
|
|
||||||
|
[email protected]: {}
|
||||||
|
|
||||||
|
[email protected]:
|
||||||
|
dependencies:
|
||||||
|
is-alphabetical: 2.0.1
|
||||||
|
is-decimal: 2.0.1
|
||||||
|
|
||||||
[email protected]:
|
[email protected]:
|
||||||
dependencies:
|
dependencies:
|
||||||
call-bound: 1.0.4
|
call-bound: 1.0.4
|
||||||
@@ -9274,6 +9672,8 @@ snapshots:
|
|||||||
call-bound: 1.0.4
|
call-bound: 1.0.4
|
||||||
has-tostringtag: 1.0.2
|
has-tostringtag: 1.0.2
|
||||||
|
|
||||||
|
[email protected]: {}
|
||||||
|
|
||||||
[email protected]: {}
|
[email protected]: {}
|
||||||
|
|
||||||
[email protected]:
|
[email protected]:
|
||||||
@@ -9305,6 +9705,8 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
is-extglob: 2.1.1
|
is-extglob: 2.1.1
|
||||||
|
|
||||||
|
[email protected]: {}
|
||||||
|
|
||||||
[email protected]: {}
|
[email protected]: {}
|
||||||
|
|
||||||
[email protected]: {}
|
[email protected]: {}
|
||||||
@@ -9341,6 +9743,8 @@ snapshots:
|
|||||||
|
|
||||||
[email protected]: {}
|
[email protected]: {}
|
||||||
|
|
||||||
|
[email protected]: {}
|
||||||
|
|
||||||
[email protected]: {}
|
[email protected]: {}
|
||||||
|
|
||||||
[email protected]:
|
[email protected]:
|
||||||
@@ -10072,6 +10476,8 @@ snapshots:
|
|||||||
slice-ansi: 4.0.0
|
slice-ansi: 4.0.0
|
||||||
wrap-ansi: 6.2.0
|
wrap-ansi: 6.2.0
|
||||||
|
|
||||||
|
[email protected]: {}
|
||||||
|
|
||||||
[email protected]:
|
[email protected]:
|
||||||
dependencies:
|
dependencies:
|
||||||
js-tokens: 4.0.0
|
js-tokens: 4.0.0
|
||||||
@@ -10118,6 +10524,8 @@ snapshots:
|
|||||||
|
|
||||||
[email protected]: {}
|
[email protected]: {}
|
||||||
|
|
||||||
|
[email protected]: {}
|
||||||
|
|
||||||
[email protected]: {}
|
[email protected]: {}
|
||||||
|
|
||||||
[email protected]:
|
[email protected]:
|
||||||
@@ -10131,6 +10539,159 @@ snapshots:
|
|||||||
crypt: 0.0.2
|
crypt: 0.0.2
|
||||||
is-buffer: 1.1.6
|
is-buffer: 1.1.6
|
||||||
|
|
||||||
|
[email protected]:
|
||||||
|
dependencies:
|
||||||
|
'@types/mdast': 4.0.4
|
||||||
|
escape-string-regexp: 5.0.0
|
||||||
|
unist-util-is: 6.0.1
|
||||||
|
unist-util-visit-parents: 6.0.2
|
||||||
|
|
||||||
|
[email protected]:
|
||||||
|
dependencies:
|
||||||
|
'@types/mdast': 4.0.4
|
||||||
|
'@types/unist': 3.0.3
|
||||||
|
decode-named-character-reference: 1.2.0
|
||||||
|
devlop: 1.1.0
|
||||||
|
mdast-util-to-string: 4.0.0
|
||||||
|
micromark: 4.0.2
|
||||||
|
micromark-util-decode-numeric-character-reference: 2.0.2
|
||||||
|
micromark-util-decode-string: 2.0.1
|
||||||
|
micromark-util-normalize-identifier: 2.0.1
|
||||||
|
micromark-util-symbol: 2.0.1
|
||||||
|
micromark-util-types: 2.0.2
|
||||||
|
unist-util-stringify-position: 4.0.0
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- supports-color
|
||||||
|
|
||||||
|
[email protected]:
|
||||||
|
dependencies:
|
||||||
|
'@types/mdast': 4.0.4
|
||||||
|
ccount: 2.0.1
|
||||||
|
devlop: 1.1.0
|
||||||
|
mdast-util-find-and-replace: 3.0.2
|
||||||
|
micromark-util-character: 2.1.1
|
||||||
|
|
||||||
|
[email protected]:
|
||||||
|
dependencies:
|
||||||
|
'@types/mdast': 4.0.4
|
||||||
|
devlop: 1.1.0
|
||||||
|
mdast-util-from-markdown: 2.0.2
|
||||||
|
mdast-util-to-markdown: 2.1.2
|
||||||
|
micromark-util-normalize-identifier: 2.0.1
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- supports-color
|
||||||
|
|
||||||
|
[email protected]:
|
||||||
|
dependencies:
|
||||||
|
'@types/mdast': 4.0.4
|
||||||
|
mdast-util-from-markdown: 2.0.2
|
||||||
|
mdast-util-to-markdown: 2.1.2
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- supports-color
|
||||||
|
|
||||||
|
[email protected]:
|
||||||
|
dependencies:
|
||||||
|
'@types/mdast': 4.0.4
|
||||||
|
devlop: 1.1.0
|
||||||
|
markdown-table: 3.0.4
|
||||||
|
mdast-util-from-markdown: 2.0.2
|
||||||
|
mdast-util-to-markdown: 2.1.2
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- supports-color
|
||||||
|
|
||||||
|
[email protected]:
|
||||||
|
dependencies:
|
||||||
|
'@types/mdast': 4.0.4
|
||||||
|
devlop: 1.1.0
|
||||||
|
mdast-util-from-markdown: 2.0.2
|
||||||
|
mdast-util-to-markdown: 2.1.2
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- supports-color
|
||||||
|
|
||||||
|
[email protected]:
|
||||||
|
dependencies:
|
||||||
|
mdast-util-from-markdown: 2.0.2
|
||||||
|
mdast-util-gfm-autolink-literal: 2.0.1
|
||||||
|
mdast-util-gfm-footnote: 2.1.0
|
||||||
|
mdast-util-gfm-strikethrough: 2.0.0
|
||||||
|
mdast-util-gfm-table: 2.0.0
|
||||||
|
mdast-util-gfm-task-list-item: 2.0.0
|
||||||
|
mdast-util-to-markdown: 2.1.2
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- supports-color
|
||||||
|
|
||||||
|
[email protected]:
|
||||||
|
dependencies:
|
||||||
|
'@types/estree-jsx': 1.0.5
|
||||||
|
'@types/hast': 3.0.4
|
||||||
|
'@types/mdast': 4.0.4
|
||||||
|
devlop: 1.1.0
|
||||||
|
mdast-util-from-markdown: 2.0.2
|
||||||
|
mdast-util-to-markdown: 2.1.2
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- supports-color
|
||||||
|
|
||||||
|
[email protected]:
|
||||||
|
dependencies:
|
||||||
|
'@types/estree-jsx': 1.0.5
|
||||||
|
'@types/hast': 3.0.4
|
||||||
|
'@types/mdast': 4.0.4
|
||||||
|
'@types/unist': 3.0.3
|
||||||
|
ccount: 2.0.1
|
||||||
|
devlop: 1.1.0
|
||||||
|
mdast-util-from-markdown: 2.0.2
|
||||||
|
mdast-util-to-markdown: 2.1.2
|
||||||
|
parse-entities: 4.0.2
|
||||||
|
stringify-entities: 4.0.4
|
||||||
|
unist-util-stringify-position: 4.0.0
|
||||||
|
vfile-message: 4.0.3
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- supports-color
|
||||||
|
|
||||||
|
[email protected]:
|
||||||
|
dependencies:
|
||||||
|
'@types/estree-jsx': 1.0.5
|
||||||
|
'@types/hast': 3.0.4
|
||||||
|
'@types/mdast': 4.0.4
|
||||||
|
devlop: 1.1.0
|
||||||
|
mdast-util-from-markdown: 2.0.2
|
||||||
|
mdast-util-to-markdown: 2.1.2
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- supports-color
|
||||||
|
|
||||||
|
[email protected]:
|
||||||
|
dependencies:
|
||||||
|
'@types/mdast': 4.0.4
|
||||||
|
unist-util-is: 6.0.1
|
||||||
|
|
||||||
|
[email protected]:
|
||||||
|
dependencies:
|
||||||
|
'@types/hast': 3.0.4
|
||||||
|
'@types/mdast': 4.0.4
|
||||||
|
'@ungap/structured-clone': 1.3.0
|
||||||
|
devlop: 1.1.0
|
||||||
|
micromark-util-sanitize-uri: 2.0.1
|
||||||
|
trim-lines: 3.0.1
|
||||||
|
unist-util-position: 5.0.0
|
||||||
|
unist-util-visit: 5.0.0
|
||||||
|
vfile: 6.0.3
|
||||||
|
|
||||||
|
[email protected]:
|
||||||
|
dependencies:
|
||||||
|
'@types/mdast': 4.0.4
|
||||||
|
'@types/unist': 3.0.3
|
||||||
|
longest-streak: 3.1.0
|
||||||
|
mdast-util-phrasing: 4.1.0
|
||||||
|
mdast-util-to-string: 4.0.0
|
||||||
|
micromark-util-classify-character: 2.0.1
|
||||||
|
micromark-util-decode-string: 2.0.1
|
||||||
|
unist-util-visit: 5.0.0
|
||||||
|
zwitch: 2.0.4
|
||||||
|
|
||||||
|
[email protected]:
|
||||||
|
dependencies:
|
||||||
|
'@types/mdast': 4.0.4
|
||||||
|
|
||||||
[email protected]: {}
|
[email protected]: {}
|
||||||
|
|
||||||
[email protected]: {}
|
[email protected]: {}
|
||||||
@@ -10159,6 +10720,197 @@ snapshots:
|
|||||||
|
|
||||||
[email protected]: {}
|
[email protected]: {}
|
||||||
|
|
||||||
|
[email protected]:
|
||||||
|
dependencies:
|
||||||
|
decode-named-character-reference: 1.2.0
|
||||||
|
devlop: 1.1.0
|
||||||
|
micromark-factory-destination: 2.0.1
|
||||||
|
micromark-factory-label: 2.0.1
|
||||||
|
micromark-factory-space: 2.0.1
|
||||||
|
micromark-factory-title: 2.0.1
|
||||||
|
micromark-factory-whitespace: 2.0.1
|
||||||
|
micromark-util-character: 2.1.1
|
||||||
|
micromark-util-chunked: 2.0.1
|
||||||
|
micromark-util-classify-character: 2.0.1
|
||||||
|
micromark-util-html-tag-name: 2.0.1
|
||||||
|
micromark-util-normalize-identifier: 2.0.1
|
||||||
|
micromark-util-resolve-all: 2.0.1
|
||||||
|
micromark-util-subtokenize: 2.1.0
|
||||||
|
micromark-util-symbol: 2.0.1
|
||||||
|
micromark-util-types: 2.0.2
|
||||||
|
|
||||||
|
[email protected]:
|
||||||
|
dependencies:
|
||||||
|
micromark-util-character: 2.1.1
|
||||||
|
micromark-util-sanitize-uri: 2.0.1
|
||||||
|
micromark-util-symbol: 2.0.1
|
||||||
|
micromark-util-types: 2.0.2
|
||||||
|
|
||||||
|
[email protected]:
|
||||||
|
dependencies:
|
||||||
|
devlop: 1.1.0
|
||||||
|
micromark-core-commonmark: 2.0.3
|
||||||
|
micromark-factory-space: 2.0.1
|
||||||
|
micromark-util-character: 2.1.1
|
||||||
|
micromark-util-normalize-identifier: 2.0.1
|
||||||
|
micromark-util-sanitize-uri: 2.0.1
|
||||||
|
micromark-util-symbol: 2.0.1
|
||||||
|
micromark-util-types: 2.0.2
|
||||||
|
|
||||||
|
[email protected]:
|
||||||
|
dependencies:
|
||||||
|
devlop: 1.1.0
|
||||||
|
micromark-util-chunked: 2.0.1
|
||||||
|
micromark-util-classify-character: 2.0.1
|
||||||
|
micromark-util-resolve-all: 2.0.1
|
||||||
|
micromark-util-symbol: 2.0.1
|
||||||
|
micromark-util-types: 2.0.2
|
||||||
|
|
||||||
|
[email protected]:
|
||||||
|
dependencies:
|
||||||
|
devlop: 1.1.0
|
||||||
|
micromark-factory-space: 2.0.1
|
||||||
|
micromark-util-character: 2.1.1
|
||||||
|
micromark-util-symbol: 2.0.1
|
||||||
|
micromark-util-types: 2.0.2
|
||||||
|
|
||||||
|
[email protected]:
|
||||||
|
dependencies:
|
||||||
|
micromark-util-types: 2.0.2
|
||||||
|
|
||||||
|
[email protected]:
|
||||||
|
dependencies:
|
||||||
|
devlop: 1.1.0
|
||||||
|
micromark-factory-space: 2.0.1
|
||||||
|
micromark-util-character: 2.1.1
|
||||||
|
micromark-util-symbol: 2.0.1
|
||||||
|
micromark-util-types: 2.0.2
|
||||||
|
|
||||||
|
[email protected]:
|
||||||
|
dependencies:
|
||||||
|
micromark-extension-gfm-autolink-literal: 2.1.0
|
||||||
|
micromark-extension-gfm-footnote: 2.1.0
|
||||||
|
micromark-extension-gfm-strikethrough: 2.1.0
|
||||||
|
micromark-extension-gfm-table: 2.1.1
|
||||||
|
micromark-extension-gfm-tagfilter: 2.0.0
|
||||||
|
micromark-extension-gfm-task-list-item: 2.1.0
|
||||||
|
micromark-util-combine-extensions: 2.0.1
|
||||||
|
micromark-util-types: 2.0.2
|
||||||
|
|
||||||
|
[email protected]:
|
||||||
|
dependencies:
|
||||||
|
micromark-util-character: 2.1.1
|
||||||
|
micromark-util-symbol: 2.0.1
|
||||||
|
micromark-util-types: 2.0.2
|
||||||
|
|
||||||
|
[email protected]:
|
||||||
|
dependencies:
|
||||||
|
devlop: 1.1.0
|
||||||
|
micromark-util-character: 2.1.1
|
||||||
|
micromark-util-symbol: 2.0.1
|
||||||
|
micromark-util-types: 2.0.2
|
||||||
|
|
||||||
|
[email protected]:
|
||||||
|
dependencies:
|
||||||
|
micromark-util-character: 2.1.1
|
||||||
|
micromark-util-types: 2.0.2
|
||||||
|
|
||||||
|
[email protected]:
|
||||||
|
dependencies:
|
||||||
|
micromark-factory-space: 2.0.1
|
||||||
|
micromark-util-character: 2.1.1
|
||||||
|
micromark-util-symbol: 2.0.1
|
||||||
|
micromark-util-types: 2.0.2
|
||||||
|
|
||||||
|
[email protected]:
|
||||||
|
dependencies:
|
||||||
|
micromark-factory-space: 2.0.1
|
||||||
|
micromark-util-character: 2.1.1
|
||||||
|
micromark-util-symbol: 2.0.1
|
||||||
|
micromark-util-types: 2.0.2
|
||||||
|
|
||||||
|
[email protected]:
|
||||||
|
dependencies:
|
||||||
|
micromark-util-symbol: 2.0.1
|
||||||
|
micromark-util-types: 2.0.2
|
||||||
|
|
||||||
|
[email protected]:
|
||||||
|
dependencies:
|
||||||
|
micromark-util-symbol: 2.0.1
|
||||||
|
|
||||||
|
[email protected]:
|
||||||
|
dependencies:
|
||||||
|
micromark-util-character: 2.1.1
|
||||||
|
micromark-util-symbol: 2.0.1
|
||||||
|
micromark-util-types: 2.0.2
|
||||||
|
|
||||||
|
[email protected]:
|
||||||
|
dependencies:
|
||||||
|
micromark-util-chunked: 2.0.1
|
||||||
|
micromark-util-types: 2.0.2
|
||||||
|
|
||||||
|
[email protected]:
|
||||||
|
dependencies:
|
||||||
|
micromark-util-symbol: 2.0.1
|
||||||
|
|
||||||
|
[email protected]:
|
||||||
|
dependencies:
|
||||||
|
decode-named-character-reference: 1.2.0
|
||||||
|
micromark-util-character: 2.1.1
|
||||||
|
micromark-util-decode-numeric-character-reference: 2.0.2
|
||||||
|
micromark-util-symbol: 2.0.1
|
||||||
|
|
||||||
|
[email protected]: {}
|
||||||
|
|
||||||
|
[email protected]: {}
|
||||||
|
|
||||||
|
[email protected]:
|
||||||
|
dependencies:
|
||||||
|
micromark-util-symbol: 2.0.1
|
||||||
|
|
||||||
|
[email protected]:
|
||||||
|
dependencies:
|
||||||
|
micromark-util-types: 2.0.2
|
||||||
|
|
||||||
|
[email protected]:
|
||||||
|
dependencies:
|
||||||
|
micromark-util-character: 2.1.1
|
||||||
|
micromark-util-encode: 2.0.1
|
||||||
|
micromark-util-symbol: 2.0.1
|
||||||
|
|
||||||
|
[email protected]:
|
||||||
|
dependencies:
|
||||||
|
devlop: 1.1.0
|
||||||
|
micromark-util-chunked: 2.0.1
|
||||||
|
micromark-util-symbol: 2.0.1
|
||||||
|
micromark-util-types: 2.0.2
|
||||||
|
|
||||||
|
[email protected]: {}
|
||||||
|
|
||||||
|
[email protected]: {}
|
||||||
|
|
||||||
|
[email protected]:
|
||||||
|
dependencies:
|
||||||
|
'@types/debug': 4.1.12
|
||||||
|
debug: 4.4.1([email protected])
|
||||||
|
decode-named-character-reference: 1.2.0
|
||||||
|
devlop: 1.1.0
|
||||||
|
micromark-core-commonmark: 2.0.3
|
||||||
|
micromark-factory-space: 2.0.1
|
||||||
|
micromark-util-character: 2.1.1
|
||||||
|
micromark-util-chunked: 2.0.1
|
||||||
|
micromark-util-combine-extensions: 2.0.1
|
||||||
|
micromark-util-decode-numeric-character-reference: 2.0.2
|
||||||
|
micromark-util-encode: 2.0.1
|
||||||
|
micromark-util-normalize-identifier: 2.0.1
|
||||||
|
micromark-util-resolve-all: 2.0.1
|
||||||
|
micromark-util-sanitize-uri: 2.0.1
|
||||||
|
micromark-util-subtokenize: 2.1.0
|
||||||
|
micromark-util-symbol: 2.0.1
|
||||||
|
micromark-util-types: 2.0.2
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- supports-color
|
||||||
|
|
||||||
[email protected]:
|
[email protected]:
|
||||||
dependencies:
|
dependencies:
|
||||||
braces: 3.0.3
|
braces: 3.0.3
|
||||||
@@ -10460,6 +11212,16 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
callsites: 3.1.0
|
callsites: 3.1.0
|
||||||
|
|
||||||
|
[email protected]:
|
||||||
|
dependencies:
|
||||||
|
'@types/unist': 2.0.11
|
||||||
|
character-entities-legacy: 3.0.0
|
||||||
|
character-reference-invalid: 2.0.1
|
||||||
|
decode-named-character-reference: 1.2.0
|
||||||
|
is-alphanumerical: 2.0.1
|
||||||
|
is-decimal: 2.0.1
|
||||||
|
is-hexadecimal: 2.0.1
|
||||||
|
|
||||||
[email protected]:
|
[email protected]:
|
||||||
dependencies:
|
dependencies:
|
||||||
'@babel/code-frame': 7.27.1
|
'@babel/code-frame': 7.27.1
|
||||||
@@ -10552,6 +11314,11 @@ snapshots:
|
|||||||
postcss: 8.5.6
|
postcss: 8.5.6
|
||||||
postcss-selector-parser: 6.1.2
|
postcss-selector-parser: 6.1.2
|
||||||
|
|
||||||
|
[email protected]:
|
||||||
|
dependencies:
|
||||||
|
cssesc: 3.0.0
|
||||||
|
util-deprecate: 1.0.2
|
||||||
|
|
||||||
[email protected]:
|
[email protected]:
|
||||||
dependencies:
|
dependencies:
|
||||||
cssesc: 3.0.0
|
cssesc: 3.0.0
|
||||||
@@ -10608,6 +11375,8 @@ snapshots:
|
|||||||
object-assign: 4.1.1
|
object-assign: 4.1.1
|
||||||
react-is: 16.13.1
|
react-is: 16.13.1
|
||||||
|
|
||||||
|
[email protected]: {}
|
||||||
|
|
||||||
[email protected]:
|
[email protected]:
|
||||||
dependencies:
|
dependencies:
|
||||||
punycode: 2.3.1
|
punycode: 2.3.1
|
||||||
@@ -10642,6 +11411,24 @@ snapshots:
|
|||||||
|
|
||||||
[email protected]: {}
|
[email protected]: {}
|
||||||
|
|
||||||
|
[email protected](@types/[email protected])([email protected]):
|
||||||
|
dependencies:
|
||||||
|
'@types/hast': 3.0.4
|
||||||
|
'@types/mdast': 4.0.4
|
||||||
|
'@types/react': 18.3.23
|
||||||
|
devlop: 1.1.0
|
||||||
|
hast-util-to-jsx-runtime: 2.3.6
|
||||||
|
html-url-attributes: 3.0.1
|
||||||
|
mdast-util-to-hast: 13.2.1
|
||||||
|
react: 18.3.1
|
||||||
|
remark-parse: 11.0.0
|
||||||
|
remark-rehype: 11.1.2
|
||||||
|
unified: 11.0.5
|
||||||
|
unist-util-visit: 5.0.0
|
||||||
|
vfile: 6.0.3
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- supports-color
|
||||||
|
|
||||||
[email protected]:
|
[email protected]:
|
||||||
dependencies:
|
dependencies:
|
||||||
loose-envify: 1.4.0
|
loose-envify: 1.4.0
|
||||||
@@ -10730,6 +11517,40 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
jsesc: 3.0.2
|
jsesc: 3.0.2
|
||||||
|
|
||||||
|
[email protected]:
|
||||||
|
dependencies:
|
||||||
|
'@types/mdast': 4.0.4
|
||||||
|
mdast-util-gfm: 3.1.0
|
||||||
|
micromark-extension-gfm: 3.0.0
|
||||||
|
remark-parse: 11.0.0
|
||||||
|
remark-stringify: 11.0.0
|
||||||
|
unified: 11.0.5
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- supports-color
|
||||||
|
|
||||||
|
[email protected]:
|
||||||
|
dependencies:
|
||||||
|
'@types/mdast': 4.0.4
|
||||||
|
mdast-util-from-markdown: 2.0.2
|
||||||
|
micromark-util-types: 2.0.2
|
||||||
|
unified: 11.0.5
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- supports-color
|
||||||
|
|
||||||
|
[email protected]:
|
||||||
|
dependencies:
|
||||||
|
'@types/hast': 3.0.4
|
||||||
|
'@types/mdast': 4.0.4
|
||||||
|
mdast-util-to-hast: 13.2.1
|
||||||
|
unified: 11.0.5
|
||||||
|
vfile: 6.0.3
|
||||||
|
|
||||||
|
[email protected]:
|
||||||
|
dependencies:
|
||||||
|
'@types/mdast': 4.0.4
|
||||||
|
mdast-util-to-markdown: 2.1.2
|
||||||
|
unified: 11.0.5
|
||||||
|
|
||||||
[email protected]: {}
|
[email protected]: {}
|
||||||
|
|
||||||
[email protected]: {}
|
[email protected]: {}
|
||||||
@@ -11022,6 +11843,8 @@ snapshots:
|
|||||||
|
|
||||||
[email protected]: {}
|
[email protected]: {}
|
||||||
|
|
||||||
|
[email protected]: {}
|
||||||
|
|
||||||
[email protected]:
|
[email protected]:
|
||||||
dependencies:
|
dependencies:
|
||||||
spdx-expression-parse: 3.0.1
|
spdx-expression-parse: 3.0.1
|
||||||
@@ -11130,6 +11953,11 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
safe-buffer: 5.2.1
|
safe-buffer: 5.2.1
|
||||||
|
|
||||||
|
[email protected]:
|
||||||
|
dependencies:
|
||||||
|
character-entities-html4: 2.1.0
|
||||||
|
character-entities-legacy: 3.0.0
|
||||||
|
|
||||||
[email protected]:
|
[email protected]:
|
||||||
dependencies:
|
dependencies:
|
||||||
get-own-enumerable-property-symbols: 3.0.2
|
get-own-enumerable-property-symbols: 3.0.2
|
||||||
@@ -11162,6 +11990,14 @@ snapshots:
|
|||||||
|
|
||||||
[email protected]: {}
|
[email protected]: {}
|
||||||
|
|
||||||
|
[email protected]:
|
||||||
|
dependencies:
|
||||||
|
style-to-object: 1.0.14
|
||||||
|
|
||||||
|
[email protected]:
|
||||||
|
dependencies:
|
||||||
|
inline-style-parser: 0.2.7
|
||||||
|
|
||||||
[email protected](@babel/[email protected])([email protected]):
|
[email protected](@babel/[email protected])([email protected]):
|
||||||
dependencies:
|
dependencies:
|
||||||
client-only: 0.0.1
|
client-only: 0.0.1
|
||||||
@@ -11329,8 +12165,12 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
punycode: 2.3.1
|
punycode: 2.3.1
|
||||||
|
|
||||||
|
[email protected]: {}
|
||||||
|
|
||||||
[email protected]: {}
|
[email protected]: {}
|
||||||
|
|
||||||
|
[email protected]: {}
|
||||||
|
|
||||||
[email protected]: {}
|
[email protected]: {}
|
||||||
|
|
||||||
[email protected](@types/[email protected])([email protected]):
|
[email protected](@types/[email protected])([email protected]):
|
||||||
@@ -11456,10 +12296,43 @@ snapshots:
|
|||||||
|
|
||||||
[email protected]: {}
|
[email protected]: {}
|
||||||
|
|
||||||
|
[email protected]:
|
||||||
|
dependencies:
|
||||||
|
'@types/unist': 3.0.3
|
||||||
|
bail: 2.0.2
|
||||||
|
devlop: 1.1.0
|
||||||
|
extend: 3.0.2
|
||||||
|
is-plain-obj: 4.1.0
|
||||||
|
trough: 2.2.0
|
||||||
|
vfile: 6.0.3
|
||||||
|
|
||||||
[email protected]:
|
[email protected]:
|
||||||
dependencies:
|
dependencies:
|
||||||
crypto-random-string: 2.0.0
|
crypto-random-string: 2.0.0
|
||||||
|
|
||||||
|
[email protected]:
|
||||||
|
dependencies:
|
||||||
|
'@types/unist': 3.0.3
|
||||||
|
|
||||||
|
[email protected]:
|
||||||
|
dependencies:
|
||||||
|
'@types/unist': 3.0.3
|
||||||
|
|
||||||
|
[email protected]:
|
||||||
|
dependencies:
|
||||||
|
'@types/unist': 3.0.3
|
||||||
|
|
||||||
|
[email protected]:
|
||||||
|
dependencies:
|
||||||
|
'@types/unist': 3.0.3
|
||||||
|
unist-util-is: 6.0.1
|
||||||
|
|
||||||
|
[email protected]:
|
||||||
|
dependencies:
|
||||||
|
'@types/unist': 3.0.3
|
||||||
|
unist-util-is: 6.0.1
|
||||||
|
unist-util-visit-parents: 6.0.2
|
||||||
|
|
||||||
[email protected]: {}
|
[email protected]: {}
|
||||||
|
|
||||||
[email protected]: {}
|
[email protected]: {}
|
||||||
@@ -11542,6 +12415,16 @@ snapshots:
|
|||||||
|
|
||||||
[email protected]: {}
|
[email protected]: {}
|
||||||
|
|
||||||
|
[email protected]:
|
||||||
|
dependencies:
|
||||||
|
'@types/unist': 3.0.3
|
||||||
|
unist-util-stringify-position: 4.0.0
|
||||||
|
|
||||||
|
[email protected]:
|
||||||
|
dependencies:
|
||||||
|
'@types/unist': 3.0.3
|
||||||
|
vfile-message: 4.0.3
|
||||||
|
|
||||||
[email protected]:
|
[email protected]:
|
||||||
dependencies:
|
dependencies:
|
||||||
maverick.js: 0.37.0
|
maverick.js: 0.37.0
|
||||||
@@ -11909,3 +12792,5 @@ snapshots:
|
|||||||
[email protected]: {}
|
[email protected]: {}
|
||||||
|
|
||||||
[email protected]: {}
|
[email protected]: {}
|
||||||
|
|
||||||
|
[email protected]: {}
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ import { CSS } from '@dnd-kit/utilities';
|
|||||||
import {
|
import {
|
||||||
AlertCircle,
|
AlertCircle,
|
||||||
AlertTriangle,
|
AlertTriangle,
|
||||||
|
Bot,
|
||||||
Check,
|
Check,
|
||||||
CheckCircle,
|
CheckCircle,
|
||||||
ChevronDown,
|
ChevronDown,
|
||||||
@@ -7641,6 +7642,454 @@ const CustomAdFilterConfig = ({
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// AI配置组件
|
||||||
|
const AIConfigComponent = ({
|
||||||
|
config,
|
||||||
|
refreshConfig,
|
||||||
|
}: {
|
||||||
|
config: AdminConfig | null;
|
||||||
|
refreshConfig: () => Promise<void>;
|
||||||
|
}) => {
|
||||||
|
const { alertModal, showAlert, hideAlert } = useAlertModal();
|
||||||
|
const { isLoading, withLoading } = useLoadingState();
|
||||||
|
|
||||||
|
// 状态管理
|
||||||
|
const [enabled, setEnabled] = useState(false);
|
||||||
|
|
||||||
|
// 自定义配置
|
||||||
|
const [customApiKey, setCustomApiKey] = useState('');
|
||||||
|
const [customBaseURL, setCustomBaseURL] = useState('');
|
||||||
|
const [customModel, setCustomModel] = useState('');
|
||||||
|
|
||||||
|
// 决策模型配置
|
||||||
|
const [decisionCustomModel, setDecisionCustomModel] = useState('');
|
||||||
|
|
||||||
|
// 联网搜索配置
|
||||||
|
const [enableWebSearch, setEnableWebSearch] = useState(false);
|
||||||
|
const [webSearchProvider, setWebSearchProvider] = useState<'tavily' | 'serper' | 'serpapi'>('tavily');
|
||||||
|
const [tavilyApiKey, setTavilyApiKey] = useState('');
|
||||||
|
const [serperApiKey, setSerperApiKey] = useState('');
|
||||||
|
const [serpApiKey, setSerpApiKey] = useState('');
|
||||||
|
|
||||||
|
// 功能开关
|
||||||
|
const [enableHomepageEntry, setEnableHomepageEntry] = useState(true);
|
||||||
|
const [enableVideoCardEntry, setEnableVideoCardEntry] = useState(true);
|
||||||
|
const [enablePlayPageEntry, setEnablePlayPageEntry] = useState(true);
|
||||||
|
|
||||||
|
// 高级设置
|
||||||
|
const [temperature, setTemperature] = useState(0.7);
|
||||||
|
const [maxTokens, setMaxTokens] = useState(1000);
|
||||||
|
const [systemPrompt, setSystemPrompt] = useState('');
|
||||||
|
|
||||||
|
// 从配置加载数据
|
||||||
|
useEffect(() => {
|
||||||
|
if (config?.AIConfig) {
|
||||||
|
setEnabled(config.AIConfig.Enabled || false);
|
||||||
|
setCustomApiKey(config.AIConfig.CustomApiKey || '');
|
||||||
|
setCustomBaseURL(config.AIConfig.CustomBaseURL || '');
|
||||||
|
setCustomModel(config.AIConfig.CustomModel || '');
|
||||||
|
setDecisionCustomModel(config.AIConfig.DecisionCustomModel || '');
|
||||||
|
setEnableWebSearch(config.AIConfig.EnableWebSearch || false);
|
||||||
|
setWebSearchProvider(config.AIConfig.WebSearchProvider || 'tavily');
|
||||||
|
setTavilyApiKey(config.AIConfig.TavilyApiKey || '');
|
||||||
|
setSerperApiKey(config.AIConfig.SerperApiKey || '');
|
||||||
|
setSerpApiKey(config.AIConfig.SerpApiKey || '');
|
||||||
|
setEnableHomepageEntry(config.AIConfig.EnableHomepageEntry !== false);
|
||||||
|
setEnableVideoCardEntry(config.AIConfig.EnableVideoCardEntry !== false);
|
||||||
|
setEnablePlayPageEntry(config.AIConfig.EnablePlayPageEntry !== false);
|
||||||
|
setTemperature(config.AIConfig.Temperature ?? 0.7);
|
||||||
|
setMaxTokens(config.AIConfig.MaxTokens ?? 1000);
|
||||||
|
setSystemPrompt(config.AIConfig.SystemPrompt || '');
|
||||||
|
}
|
||||||
|
}, [config]);
|
||||||
|
|
||||||
|
const handleSave = async () => {
|
||||||
|
await withLoading('saveAIConfig', async () => {
|
||||||
|
try {
|
||||||
|
const response = await fetch('/api/admin/ai', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
body: JSON.stringify({
|
||||||
|
Enabled: enabled,
|
||||||
|
Provider: 'custom',
|
||||||
|
CustomApiKey: customApiKey,
|
||||||
|
CustomBaseURL: customBaseURL,
|
||||||
|
CustomModel: customModel,
|
||||||
|
EnableDecisionModel: true,
|
||||||
|
DecisionProvider: 'custom',
|
||||||
|
DecisionCustomModel: decisionCustomModel,
|
||||||
|
EnableWebSearch: enableWebSearch,
|
||||||
|
WebSearchProvider: webSearchProvider,
|
||||||
|
TavilyApiKey: tavilyApiKey,
|
||||||
|
SerperApiKey: serperApiKey,
|
||||||
|
SerpApiKey: serpApiKey,
|
||||||
|
EnableHomepageEntry: enableHomepageEntry,
|
||||||
|
EnableVideoCardEntry: enableVideoCardEntry,
|
||||||
|
EnablePlayPageEntry: enablePlayPageEntry,
|
||||||
|
Temperature: temperature,
|
||||||
|
MaxTokens: maxTokens,
|
||||||
|
SystemPrompt: systemPrompt,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
const data = await response.json();
|
||||||
|
throw new Error(data.error || '保存失败');
|
||||||
|
}
|
||||||
|
|
||||||
|
showSuccess('AI配置保存成功', showAlert);
|
||||||
|
await refreshConfig();
|
||||||
|
} catch (error) {
|
||||||
|
showError(error instanceof Error ? error.message : '保存失败', showAlert);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className='space-y-6'>
|
||||||
|
{/* 使用说明 */}
|
||||||
|
<div className='bg-blue-50 dark:bg-blue-900/20 border border-blue-200 dark:border-blue-800 rounded-lg p-4'>
|
||||||
|
<div className='flex items-center gap-2 mb-2'>
|
||||||
|
<svg
|
||||||
|
className='w-5 h-5 text-blue-600 dark:text-blue-400'
|
||||||
|
fill='none'
|
||||||
|
stroke='currentColor'
|
||||||
|
viewBox='0 0 24 24'
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
strokeLinecap='round'
|
||||||
|
strokeLinejoin='round'
|
||||||
|
strokeWidth={2}
|
||||||
|
d='M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z'
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
<span className='text-sm font-medium text-blue-800 dark:text-blue-300'>
|
||||||
|
使用说明
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div className='text-sm text-blue-700 dark:text-blue-400 space-y-1'>
|
||||||
|
<p>• AI问片功能可以让用户通过AI对话获取影视推荐和信息查询</p>
|
||||||
|
<p>• 支持 OpenAI、Claude 和自定义兼容 OpenAI 格式的 API</p>
|
||||||
|
<p>• 启用决策模型后,AI会智能判断是否需要联网搜索/豆瓣/TMDB数据</p>
|
||||||
|
<p>• 开启联网搜索后,AI可以获取最新的影视资讯和信息</p>
|
||||||
|
<p>• 配置后可在首页、视频卡片和播放页启用AI问片入口</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* 功能开关 */}
|
||||||
|
<div className='flex items-center justify-between p-4 bg-gray-50 dark:bg-gray-800 rounded-lg border border-gray-200 dark:border-gray-700'>
|
||||||
|
<div>
|
||||||
|
<h3 className='text-sm font-medium text-gray-900 dark:text-gray-100'>
|
||||||
|
启用AI问片功能
|
||||||
|
</h3>
|
||||||
|
<p className='text-xs text-gray-500 dark:text-gray-400 mt-1'>
|
||||||
|
关闭后所有AI问片入口将不可用
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<label className='relative inline-flex items-center cursor-pointer'>
|
||||||
|
<input
|
||||||
|
type='checkbox'
|
||||||
|
checked={enabled}
|
||||||
|
onChange={(e) => setEnabled(e.target.checked)}
|
||||||
|
className='sr-only peer'
|
||||||
|
/>
|
||||||
|
<div className="w-14 h-7 bg-gray-200 peer-focus:outline-none peer-focus:ring-4 peer-focus:ring-green-300 dark:peer-focus:ring-green-800 rounded-full peer dark:bg-gray-700 peer-checked:after:translate-x-full rtl:peer-checked:after:-translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-0.5 after:start-[4px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-6 after:w-6 after:transition-all dark:border-gray-600 peer-checked:bg-green-600"></div>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* AI模型配置 */}
|
||||||
|
<div className='space-y-4'>
|
||||||
|
<h3 className='text-base font-semibold text-gray-900 dark:text-gray-100'>
|
||||||
|
AI模型配置
|
||||||
|
</h3>
|
||||||
|
<p className='text-sm text-gray-500 dark:text-gray-400'>
|
||||||
|
请配置兼容OpenAI格式的API
|
||||||
|
</p>
|
||||||
|
<div className='space-y-4 p-4 bg-gray-50 dark:bg-gray-800/50 rounded-lg'>
|
||||||
|
<h4 className='text-sm font-semibold text-gray-900 dark:text-gray-100'>
|
||||||
|
自定义 API 配置
|
||||||
|
</h4>
|
||||||
|
<div>
|
||||||
|
<label className='block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2'>
|
||||||
|
API Key <span className='text-red-500'>*</span>
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
type='password'
|
||||||
|
value={customApiKey}
|
||||||
|
onChange={(e) => setCustomApiKey(e.target.value)}
|
||||||
|
placeholder='your-api-key'
|
||||||
|
className='w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100'
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label className='block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2'>
|
||||||
|
Base URL <span className='text-red-500'>*</span>
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
type='text'
|
||||||
|
value={customBaseURL}
|
||||||
|
onChange={(e) => setCustomBaseURL(e.target.value)}
|
||||||
|
placeholder='https://your-api.example.com/v1'
|
||||||
|
className='w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100'
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label className='block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2'>
|
||||||
|
模型名称 <span className='text-red-500'>*</span>
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
type='text'
|
||||||
|
value={customModel}
|
||||||
|
onChange={(e) => setCustomModel(e.target.value)}
|
||||||
|
placeholder='model-name'
|
||||||
|
className='w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100'
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* 决策模型配置 */}
|
||||||
|
<div className='space-y-4 p-4 border border-gray-200 dark:border-gray-700 rounded-lg'>
|
||||||
|
<div>
|
||||||
|
<h4 className='text-sm font-semibold text-gray-900 dark:text-gray-100'>
|
||||||
|
AI决策模型配置
|
||||||
|
</h4>
|
||||||
|
<p className='text-xs text-gray-500 dark:text-gray-400 mt-1'>
|
||||||
|
使用AI智能判断是否需要联网搜索、豆瓣或TMDB数据,并优化搜索关键词(复用主模型的API配置)
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className='space-y-3 p-3 bg-purple-50/50 dark:bg-purple-900/10 rounded-lg'>
|
||||||
|
<div>
|
||||||
|
<label className='block text-xs font-medium text-gray-700 dark:text-gray-300 mb-1'>
|
||||||
|
决策模型名称
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
type='text'
|
||||||
|
value={decisionCustomModel}
|
||||||
|
onChange={(e) => setDecisionCustomModel(e.target.value)}
|
||||||
|
placeholder='gpt-4o-mini (建议使用成本较低的小模型)'
|
||||||
|
className='w-full px-3 py-2 text-sm border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100'
|
||||||
|
/>
|
||||||
|
<p className='text-xs text-gray-500 dark:text-gray-400 mt-1'>
|
||||||
|
留空则使用传统关键词匹配方式,不进行AI决策
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className='bg-blue-50 dark:bg-blue-900/20 border border-blue-200 dark:border-blue-800 rounded-lg p-3'>
|
||||||
|
<p className='text-xs text-blue-700 dark:text-blue-400'>
|
||||||
|
💡 <strong>提示:</strong> 决策模型用于智能判断是否需要调用各个数据源,建议使用成本较低的小模型(如 gpt-4o-mini)。会复用主模型的API Key和Base URL配置。
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* 联网搜索配置 */}
|
||||||
|
<div className='space-y-4 p-4 border border-gray-200 dark:border-gray-700 rounded-lg'>
|
||||||
|
<div className='flex items-center justify-between'>
|
||||||
|
<div>
|
||||||
|
<h4 className='text-sm font-semibold text-gray-900 dark:text-gray-100'>
|
||||||
|
启用联网搜索
|
||||||
|
</h4>
|
||||||
|
<p className='text-xs text-gray-500 dark:text-gray-400 mt-1'>
|
||||||
|
AI可以搜索最新的影视资讯和信息
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<label className='relative inline-flex items-center cursor-pointer'>
|
||||||
|
<input
|
||||||
|
type='checkbox'
|
||||||
|
checked={enableWebSearch}
|
||||||
|
onChange={(e) => setEnableWebSearch(e.target.checked)}
|
||||||
|
className='sr-only peer'
|
||||||
|
/>
|
||||||
|
<div className="w-11 h-6 bg-gray-200 peer-focus:outline-none peer-focus:ring-4 peer-focus:ring-blue-300 dark:peer-focus:ring-blue-800 rounded-full peer dark:bg-gray-700 peer-checked:after:translate-x-full rtl:peer-checked:after:-translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:start-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all dark:border-gray-600 peer-checked:bg-blue-600"></div>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{enableWebSearch && (
|
||||||
|
<div className='space-y-4 mt-4'>
|
||||||
|
<div>
|
||||||
|
<label className='block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2'>
|
||||||
|
搜索服务提供商
|
||||||
|
</label>
|
||||||
|
<select
|
||||||
|
value={webSearchProvider}
|
||||||
|
onChange={(e) => setWebSearchProvider(e.target.value as any)}
|
||||||
|
className='w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100'
|
||||||
|
>
|
||||||
|
<option value='tavily'>Tavily (推荐)</option>
|
||||||
|
<option value='serper'>Serper.dev</option>
|
||||||
|
<option value='serpapi'>SerpAPI</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{webSearchProvider === 'tavily' && (
|
||||||
|
<div>
|
||||||
|
<label className='block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2'>
|
||||||
|
Tavily API Key
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
type='password'
|
||||||
|
value={tavilyApiKey}
|
||||||
|
onChange={(e) => setTavilyApiKey(e.target.value)}
|
||||||
|
placeholder='tvly-...'
|
||||||
|
className='w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100'
|
||||||
|
/>
|
||||||
|
<p className='text-xs text-gray-500 dark:text-gray-400 mt-1'>
|
||||||
|
在 <a href='https://tavily.com' target='_blank' className='text-blue-600 hover:underline'>tavily.com</a> 注册获取
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{webSearchProvider === 'serper' && (
|
||||||
|
<div>
|
||||||
|
<label className='block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2'>
|
||||||
|
Serper API Key
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
type='password'
|
||||||
|
value={serperApiKey}
|
||||||
|
onChange={(e) => setSerperApiKey(e.target.value)}
|
||||||
|
placeholder='your-serper-key'
|
||||||
|
className='w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100'
|
||||||
|
/>
|
||||||
|
<p className='text-xs text-gray-500 dark:text-gray-400 mt-1'>
|
||||||
|
在 <a href='https://serper.dev' target='_blank' className='text-blue-600 hover:underline'>serper.dev</a> 注册获取
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{webSearchProvider === 'serpapi' && (
|
||||||
|
<div>
|
||||||
|
<label className='block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2'>
|
||||||
|
SerpAPI Key
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
type='password'
|
||||||
|
value={serpApiKey}
|
||||||
|
onChange={(e) => setSerpApiKey(e.target.value)}
|
||||||
|
placeholder='your-serpapi-key'
|
||||||
|
className='w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100'
|
||||||
|
/>
|
||||||
|
<p className='text-xs text-gray-500 dark:text-gray-400 mt-1'>
|
||||||
|
在 <a href='https://serpapi.com' target='_blank' className='text-blue-600 hover:underline'>serpapi.com</a> 注册获取
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* 入口开关 */}
|
||||||
|
<div className='space-y-3 p-4 border border-gray-200 dark:border-gray-700 rounded-lg'>
|
||||||
|
<h4 className='text-sm font-semibold text-gray-900 dark:text-gray-100 mb-3'>
|
||||||
|
功能入口设置
|
||||||
|
</h4>
|
||||||
|
|
||||||
|
{[
|
||||||
|
{ key: 'homepage', label: '首页入口', desc: '在首页显示AI问片入口', state: enableHomepageEntry, setState: setEnableHomepageEntry },
|
||||||
|
{ key: 'videocard', label: '视频卡片入口', desc: '在视频卡片菜单中显示AI问片选项', state: enableVideoCardEntry, setState: setEnableVideoCardEntry },
|
||||||
|
{ key: 'playpage', label: '播放页入口', desc: '在视频播放页显示AI问片功能', state: enablePlayPageEntry, setState: setEnablePlayPageEntry },
|
||||||
|
].map((item) => (
|
||||||
|
<div key={item.key} className='flex items-center justify-between py-2'>
|
||||||
|
<div>
|
||||||
|
<div className='text-sm font-medium text-gray-900 dark:text-gray-100'>
|
||||||
|
{item.label}
|
||||||
|
</div>
|
||||||
|
<div className='text-xs text-gray-500 dark:text-gray-400'>
|
||||||
|
{item.desc}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<label className='relative inline-flex items-center cursor-pointer'>
|
||||||
|
<input
|
||||||
|
type='checkbox'
|
||||||
|
checked={item.state}
|
||||||
|
onChange={(e) => item.setState(e.target.checked)}
|
||||||
|
className='sr-only peer'
|
||||||
|
/>
|
||||||
|
<div className="w-11 h-6 bg-gray-200 peer-focus:outline-none peer-focus:ring-4 peer-focus:ring-green-300 dark:peer-focus:ring-green-800 rounded-full peer dark:bg-gray-700 peer-checked:after:translate-x-full rtl:peer-checked:after:-translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:start-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all dark:border-gray-600 peer-checked:bg-green-600"></div>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* 高级设置 */}
|
||||||
|
<details className='p-4 border border-gray-200 dark:border-gray-700 rounded-lg'>
|
||||||
|
<summary className='text-sm font-semibold text-gray-900 dark:text-gray-100 cursor-pointer'>
|
||||||
|
高级设置 (可选)
|
||||||
|
</summary>
|
||||||
|
<div className='mt-4 space-y-4'>
|
||||||
|
<div>
|
||||||
|
<label className='block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2'>
|
||||||
|
Temperature ({temperature})
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
type='range'
|
||||||
|
min='0'
|
||||||
|
max='2'
|
||||||
|
step='0.1'
|
||||||
|
value={temperature}
|
||||||
|
onChange={(e) => setTemperature(parseFloat(e.target.value))}
|
||||||
|
className='w-full'
|
||||||
|
/>
|
||||||
|
<p className='text-xs text-gray-500 dark:text-gray-400 mt-1'>
|
||||||
|
控制回复的创造性,0=保守,2=创造
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<label className='block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2'>
|
||||||
|
最大回复Token数
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
type='number'
|
||||||
|
value={maxTokens}
|
||||||
|
onChange={(e) => setMaxTokens(parseInt(e.target.value) || 1000)}
|
||||||
|
className='w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100'
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<label className='block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2'>
|
||||||
|
自定义系统提示词
|
||||||
|
</label>
|
||||||
|
<textarea
|
||||||
|
value={systemPrompt}
|
||||||
|
onChange={(e) => setSystemPrompt(e.target.value)}
|
||||||
|
rows={4}
|
||||||
|
placeholder='可自定义AI的角色和行为规则...'
|
||||||
|
className='w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100'
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</details>
|
||||||
|
|
||||||
|
{/* 保存按钮 */}
|
||||||
|
<div className='flex justify-end'>
|
||||||
|
<button
|
||||||
|
onClick={handleSave}
|
||||||
|
disabled={isLoading('saveAIConfig')}
|
||||||
|
className={isLoading('saveAIConfig') ? buttonStyles.disabled : buttonStyles.success}
|
||||||
|
>
|
||||||
|
{isLoading('saveAIConfig') ? '保存中...' : '保存配置'}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* 通用弹窗组件 */}
|
||||||
|
<AlertModal
|
||||||
|
isOpen={alertModal.isOpen}
|
||||||
|
onClose={hideAlert}
|
||||||
|
type={alertModal.type}
|
||||||
|
title={alertModal.title}
|
||||||
|
message={alertModal.message}
|
||||||
|
timer={alertModal.timer}
|
||||||
|
showConfirm={alertModal.showConfirm}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
// 直播源配置组件
|
// 直播源配置组件
|
||||||
const LiveSourceConfig = ({
|
const LiveSourceConfig = ({
|
||||||
config,
|
config,
|
||||||
@@ -8276,6 +8725,7 @@ function AdminPageClient() {
|
|||||||
userConfig: false,
|
userConfig: false,
|
||||||
videoSource: false,
|
videoSource: false,
|
||||||
openListConfig: false,
|
openListConfig: false,
|
||||||
|
aiConfig: false,
|
||||||
liveSource: false,
|
liveSource: false,
|
||||||
siteConfig: false,
|
siteConfig: false,
|
||||||
registrationConfig: false,
|
registrationConfig: false,
|
||||||
@@ -8570,6 +9020,18 @@ function AdminPageClient() {
|
|||||||
<OpenListConfigComponent config={config} refreshConfig={fetchConfig} />
|
<OpenListConfigComponent config={config} refreshConfig={fetchConfig} />
|
||||||
</CollapsibleTab>
|
</CollapsibleTab>
|
||||||
|
|
||||||
|
{/* AI配置标签 */}
|
||||||
|
<CollapsibleTab
|
||||||
|
title='AI设定'
|
||||||
|
icon={
|
||||||
|
<Bot size={20} className='text-gray-600 dark:text-gray-400' />
|
||||||
|
}
|
||||||
|
isExpanded={expandedTabs.aiConfig}
|
||||||
|
onToggle={() => toggleTab('aiConfig')}
|
||||||
|
>
|
||||||
|
<AIConfigComponent config={config} refreshConfig={fetchConfig} />
|
||||||
|
</CollapsibleTab>
|
||||||
|
|
||||||
{/* 分类配置标签 */}
|
{/* 分类配置标签 */}
|
||||||
<CollapsibleTab
|
<CollapsibleTab
|
||||||
title='分类配置'
|
title='分类配置'
|
||||||
|
|||||||
@@ -0,0 +1,200 @@
|
|||||||
|
/* eslint-disable @typescript-eslint/no-explicit-any,no-console */
|
||||||
|
|
||||||
|
import { NextRequest, NextResponse } from 'next/server';
|
||||||
|
|
||||||
|
import { getAuthInfoFromCookie } from '@/lib/auth';
|
||||||
|
import { getConfig } from '@/lib/config';
|
||||||
|
import { db } from '@/lib/db';
|
||||||
|
|
||||||
|
export const runtime = 'nodejs';
|
||||||
|
|
||||||
|
export async function POST(request: NextRequest) {
|
||||||
|
const storageType = process.env.NEXT_PUBLIC_STORAGE_TYPE || 'localstorage';
|
||||||
|
if (storageType === 'localstorage') {
|
||||||
|
return NextResponse.json(
|
||||||
|
{
|
||||||
|
error: '不支持本地存储进行管理员配置',
|
||||||
|
},
|
||||||
|
{ status: 400 }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const body = await request.json();
|
||||||
|
|
||||||
|
const authInfo = getAuthInfoFromCookie(request);
|
||||||
|
if (!authInfo || !authInfo.username) {
|
||||||
|
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
|
||||||
|
}
|
||||||
|
const username = authInfo.username;
|
||||||
|
|
||||||
|
const {
|
||||||
|
Enabled,
|
||||||
|
Provider,
|
||||||
|
OpenAIApiKey,
|
||||||
|
OpenAIBaseURL,
|
||||||
|
OpenAIModel,
|
||||||
|
ClaudeApiKey,
|
||||||
|
ClaudeModel,
|
||||||
|
CustomApiKey,
|
||||||
|
CustomBaseURL,
|
||||||
|
CustomModel,
|
||||||
|
EnableDecisionModel,
|
||||||
|
DecisionProvider,
|
||||||
|
DecisionOpenAIApiKey,
|
||||||
|
DecisionOpenAIBaseURL,
|
||||||
|
DecisionOpenAIModel,
|
||||||
|
DecisionClaudeApiKey,
|
||||||
|
DecisionClaudeModel,
|
||||||
|
DecisionCustomApiKey,
|
||||||
|
DecisionCustomBaseURL,
|
||||||
|
DecisionCustomModel,
|
||||||
|
EnableWebSearch,
|
||||||
|
WebSearchProvider,
|
||||||
|
TavilyApiKey,
|
||||||
|
SerperApiKey,
|
||||||
|
SerpApiKey,
|
||||||
|
EnableHomepageEntry,
|
||||||
|
EnableVideoCardEntry,
|
||||||
|
EnablePlayPageEntry,
|
||||||
|
Temperature,
|
||||||
|
MaxTokens,
|
||||||
|
SystemPrompt,
|
||||||
|
} = body as {
|
||||||
|
Enabled: boolean;
|
||||||
|
Provider: 'openai' | 'claude' | 'custom';
|
||||||
|
OpenAIApiKey?: string;
|
||||||
|
OpenAIBaseURL?: string;
|
||||||
|
OpenAIModel?: string;
|
||||||
|
ClaudeApiKey?: string;
|
||||||
|
ClaudeModel?: string;
|
||||||
|
CustomApiKey?: string;
|
||||||
|
CustomBaseURL?: string;
|
||||||
|
CustomModel?: string;
|
||||||
|
EnableDecisionModel: boolean;
|
||||||
|
DecisionProvider?: 'openai' | 'claude' | 'custom';
|
||||||
|
DecisionOpenAIApiKey?: string;
|
||||||
|
DecisionOpenAIBaseURL?: string;
|
||||||
|
DecisionOpenAIModel?: string;
|
||||||
|
DecisionClaudeApiKey?: string;
|
||||||
|
DecisionClaudeModel?: string;
|
||||||
|
DecisionCustomApiKey?: string;
|
||||||
|
DecisionCustomBaseURL?: string;
|
||||||
|
DecisionCustomModel?: string;
|
||||||
|
EnableWebSearch: boolean;
|
||||||
|
WebSearchProvider?: 'tavily' | 'serper' | 'serpapi';
|
||||||
|
TavilyApiKey?: string;
|
||||||
|
SerperApiKey?: string;
|
||||||
|
SerpApiKey?: string;
|
||||||
|
EnableHomepageEntry: boolean;
|
||||||
|
EnableVideoCardEntry: boolean;
|
||||||
|
EnablePlayPageEntry: boolean;
|
||||||
|
Temperature?: number;
|
||||||
|
MaxTokens?: number;
|
||||||
|
SystemPrompt?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
// 参数校验
|
||||||
|
if (
|
||||||
|
typeof Enabled !== 'boolean' ||
|
||||||
|
(Provider !== undefined && !['openai', 'claude', 'custom'].includes(Provider)) ||
|
||||||
|
(OpenAIApiKey !== undefined && typeof OpenAIApiKey !== 'string') ||
|
||||||
|
(OpenAIBaseURL !== undefined && typeof OpenAIBaseURL !== 'string') ||
|
||||||
|
(OpenAIModel !== undefined && typeof OpenAIModel !== 'string') ||
|
||||||
|
(ClaudeApiKey !== undefined && typeof ClaudeApiKey !== 'string') ||
|
||||||
|
(ClaudeModel !== undefined && typeof ClaudeModel !== 'string') ||
|
||||||
|
(CustomApiKey !== undefined && typeof CustomApiKey !== 'string') ||
|
||||||
|
(CustomBaseURL !== undefined && typeof CustomBaseURL !== 'string') ||
|
||||||
|
(CustomModel !== undefined && typeof CustomModel !== 'string') ||
|
||||||
|
typeof EnableDecisionModel !== 'boolean' ||
|
||||||
|
(DecisionProvider !== undefined && !['openai', 'claude', 'custom'].includes(DecisionProvider)) ||
|
||||||
|
(DecisionOpenAIApiKey !== undefined && typeof DecisionOpenAIApiKey !== 'string') ||
|
||||||
|
(DecisionOpenAIBaseURL !== undefined && typeof DecisionOpenAIBaseURL !== 'string') ||
|
||||||
|
(DecisionOpenAIModel !== undefined && typeof DecisionOpenAIModel !== 'string') ||
|
||||||
|
(DecisionClaudeApiKey !== undefined && typeof DecisionClaudeApiKey !== 'string') ||
|
||||||
|
(DecisionClaudeModel !== undefined && typeof DecisionClaudeModel !== 'string') ||
|
||||||
|
(DecisionCustomApiKey !== undefined && typeof DecisionCustomApiKey !== 'string') ||
|
||||||
|
(DecisionCustomBaseURL !== undefined && typeof DecisionCustomBaseURL !== 'string') ||
|
||||||
|
(DecisionCustomModel !== undefined && typeof DecisionCustomModel !== 'string') ||
|
||||||
|
typeof EnableWebSearch !== 'boolean' ||
|
||||||
|
(WebSearchProvider !== undefined && !['tavily', 'serper', 'serpapi'].includes(WebSearchProvider)) ||
|
||||||
|
(TavilyApiKey !== undefined && typeof TavilyApiKey !== 'string') ||
|
||||||
|
(SerperApiKey !== undefined && typeof SerperApiKey !== 'string') ||
|
||||||
|
(SerpApiKey !== undefined && typeof SerpApiKey !== 'string') ||
|
||||||
|
typeof EnableHomepageEntry !== 'boolean' ||
|
||||||
|
typeof EnableVideoCardEntry !== 'boolean' ||
|
||||||
|
typeof EnablePlayPageEntry !== 'boolean' ||
|
||||||
|
(Temperature !== undefined && typeof Temperature !== 'number') ||
|
||||||
|
(MaxTokens !== undefined && typeof MaxTokens !== 'number') ||
|
||||||
|
(SystemPrompt !== undefined && typeof SystemPrompt !== 'string')
|
||||||
|
) {
|
||||||
|
return NextResponse.json({ error: '参数格式错误' }, { status: 400 });
|
||||||
|
}
|
||||||
|
|
||||||
|
const adminConfig = await getConfig();
|
||||||
|
|
||||||
|
// 权限校验 - 使用v2用户系统
|
||||||
|
if (username !== process.env.USERNAME) {
|
||||||
|
const userInfo = await db.getUserInfoV2(username);
|
||||||
|
if (!userInfo || userInfo.role !== 'admin' || userInfo.banned) {
|
||||||
|
return NextResponse.json({ error: '权限不足' }, { status: 401 });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新缓存中的AI配置
|
||||||
|
adminConfig.AIConfig = {
|
||||||
|
Enabled,
|
||||||
|
Provider,
|
||||||
|
OpenAIApiKey,
|
||||||
|
OpenAIBaseURL,
|
||||||
|
OpenAIModel,
|
||||||
|
ClaudeApiKey,
|
||||||
|
ClaudeModel,
|
||||||
|
CustomApiKey,
|
||||||
|
CustomBaseURL,
|
||||||
|
CustomModel,
|
||||||
|
EnableDecisionModel,
|
||||||
|
DecisionProvider,
|
||||||
|
DecisionOpenAIApiKey,
|
||||||
|
DecisionOpenAIBaseURL,
|
||||||
|
DecisionOpenAIModel,
|
||||||
|
DecisionClaudeApiKey,
|
||||||
|
DecisionClaudeModel,
|
||||||
|
DecisionCustomApiKey,
|
||||||
|
DecisionCustomBaseURL,
|
||||||
|
DecisionCustomModel,
|
||||||
|
EnableWebSearch,
|
||||||
|
WebSearchProvider,
|
||||||
|
TavilyApiKey,
|
||||||
|
SerperApiKey,
|
||||||
|
SerpApiKey,
|
||||||
|
EnableHomepageEntry,
|
||||||
|
EnableVideoCardEntry,
|
||||||
|
EnablePlayPageEntry,
|
||||||
|
Temperature,
|
||||||
|
MaxTokens,
|
||||||
|
SystemPrompt,
|
||||||
|
};
|
||||||
|
|
||||||
|
// 写入数据库
|
||||||
|
await db.saveAdminConfig(adminConfig);
|
||||||
|
|
||||||
|
return NextResponse.json(
|
||||||
|
{ ok: true },
|
||||||
|
{
|
||||||
|
headers: {
|
||||||
|
'Cache-Control': 'no-store', // 不缓存结果
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('更新AI配置失败:', error);
|
||||||
|
return NextResponse.json(
|
||||||
|
{
|
||||||
|
error: '更新AI配置失败',
|
||||||
|
details: (error as Error).message,
|
||||||
|
},
|
||||||
|
{ status: 500 }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,279 @@
|
|||||||
|
/* eslint-disable @typescript-eslint/no-explicit-any,no-console */
|
||||||
|
|
||||||
|
import { NextRequest, NextResponse } from 'next/server';
|
||||||
|
|
||||||
|
import { getAuthInfoFromCookie } from '@/lib/auth';
|
||||||
|
import {
|
||||||
|
orchestrateDataSources,
|
||||||
|
VideoContext,
|
||||||
|
} from '@/lib/ai-orchestrator';
|
||||||
|
import { getConfig } from '@/lib/config';
|
||||||
|
|
||||||
|
export const runtime = 'nodejs';
|
||||||
|
|
||||||
|
interface ChatMessage {
|
||||||
|
role: 'user' | 'assistant';
|
||||||
|
content: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ChatRequest {
|
||||||
|
message: string;
|
||||||
|
context?: VideoContext;
|
||||||
|
history?: ChatMessage[];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* OpenAI兼容的流式聊天请求
|
||||||
|
*/
|
||||||
|
async function streamOpenAIChat(
|
||||||
|
messages: ChatMessage[],
|
||||||
|
config: {
|
||||||
|
apiKey: string;
|
||||||
|
baseURL: string;
|
||||||
|
model: string;
|
||||||
|
temperature: number;
|
||||||
|
maxTokens: number;
|
||||||
|
}
|
||||||
|
): Promise<ReadableStream> {
|
||||||
|
const response = await fetch(`${config.baseURL}/chat/completions`, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
Authorization: `Bearer ${config.apiKey}`,
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
model: config.model,
|
||||||
|
messages,
|
||||||
|
temperature: config.temperature,
|
||||||
|
max_tokens: config.maxTokens,
|
||||||
|
stream: true,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error(
|
||||||
|
`OpenAI API error: ${response.status} ${response.statusText}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return response.body!;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Claude API流式聊天请求
|
||||||
|
*/
|
||||||
|
async function streamClaudeChat(
|
||||||
|
messages: ChatMessage[],
|
||||||
|
systemPrompt: string,
|
||||||
|
config: {
|
||||||
|
apiKey: string;
|
||||||
|
model: string;
|
||||||
|
temperature: number;
|
||||||
|
maxTokens: number;
|
||||||
|
}
|
||||||
|
): Promise<ReadableStream> {
|
||||||
|
// Claude API格式: 移除system消息,单独传递
|
||||||
|
const userMessages = messages.filter((m) => m.role !== 'system');
|
||||||
|
|
||||||
|
const response = await fetch('https://api.anthropic.com/v1/messages', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
'x-api-key': config.apiKey,
|
||||||
|
'anthropic-version': '2023-06-01',
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
model: config.model,
|
||||||
|
max_tokens: config.maxTokens,
|
||||||
|
temperature: config.temperature,
|
||||||
|
system: systemPrompt,
|
||||||
|
messages: userMessages,
|
||||||
|
stream: true,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error(
|
||||||
|
`Claude API error: ${response.status} ${response.statusText}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return response.body!;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 转换流为SSE格式
|
||||||
|
*/
|
||||||
|
function transformToSSE(
|
||||||
|
stream: ReadableStream,
|
||||||
|
provider: 'openai' | 'claude' | 'custom'
|
||||||
|
): ReadableStream {
|
||||||
|
const reader = stream.getReader();
|
||||||
|
const decoder = new TextDecoder();
|
||||||
|
|
||||||
|
return new ReadableStream({
|
||||||
|
async start(controller) {
|
||||||
|
try {
|
||||||
|
while (true) {
|
||||||
|
const { done, value } = await reader.read();
|
||||||
|
if (done) break;
|
||||||
|
|
||||||
|
const chunk = decoder.decode(value, { stream: true });
|
||||||
|
const lines = chunk.split('\n').filter((line) => line.trim() !== '');
|
||||||
|
|
||||||
|
for (const line of lines) {
|
||||||
|
if (line.startsWith('data: ')) {
|
||||||
|
const data = line.slice(6);
|
||||||
|
|
||||||
|
if (data === '[DONE]') {
|
||||||
|
controller.enqueue(
|
||||||
|
new TextEncoder().encode('data: [DONE]\n\n')
|
||||||
|
);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const json = JSON.parse(data);
|
||||||
|
|
||||||
|
// 提取文本内容
|
||||||
|
let text = '';
|
||||||
|
if (provider === 'claude') {
|
||||||
|
// Claude格式
|
||||||
|
if (json.type === 'content_block_delta') {
|
||||||
|
text = json.delta?.text || '';
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// OpenAI格式
|
||||||
|
text = json.choices?.[0]?.delta?.content || '';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (text) {
|
||||||
|
controller.enqueue(
|
||||||
|
new TextEncoder().encode(`data: ${JSON.stringify({ text })}\n\n`)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.error('Parse stream chunk error:', e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Stream error:', error);
|
||||||
|
controller.error(error);
|
||||||
|
} finally {
|
||||||
|
controller.close();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function POST(request: NextRequest) {
|
||||||
|
try {
|
||||||
|
// 1. 验证用户登录
|
||||||
|
const authInfo = getAuthInfoFromCookie(request);
|
||||||
|
if (!authInfo || !authInfo.username) {
|
||||||
|
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. 获取AI配置
|
||||||
|
const adminConfig = await getConfig();
|
||||||
|
const aiConfig = adminConfig.AIConfig;
|
||||||
|
|
||||||
|
if (!aiConfig || !aiConfig.Enabled) {
|
||||||
|
return NextResponse.json(
|
||||||
|
{ error: 'AI功能未启用' },
|
||||||
|
{ status: 400 }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. 解析请求参数
|
||||||
|
const body = (await request.json()) as ChatRequest;
|
||||||
|
const { message, context, history = [] } = body;
|
||||||
|
|
||||||
|
if (!message || typeof message !== 'string') {
|
||||||
|
return NextResponse.json(
|
||||||
|
{ error: '消息内容不能为空' },
|
||||||
|
{ status: 400 }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('📨 收到AI聊天请求:', {
|
||||||
|
message: message.slice(0, 50),
|
||||||
|
context,
|
||||||
|
historyLength: history.length,
|
||||||
|
});
|
||||||
|
|
||||||
|
// 4. 使用orchestrator协调数据源
|
||||||
|
const orchestrationResult = await orchestrateDataSources(
|
||||||
|
message,
|
||||||
|
context,
|
||||||
|
{
|
||||||
|
enableWebSearch: aiConfig.EnableWebSearch,
|
||||||
|
webSearchProvider: aiConfig.WebSearchProvider,
|
||||||
|
tavilyApiKey: aiConfig.TavilyApiKey,
|
||||||
|
serperApiKey: aiConfig.SerperApiKey,
|
||||||
|
serpApiKey: aiConfig.SerpApiKey,
|
||||||
|
// 决策模型配置(固定使用自定义provider)
|
||||||
|
enableDecisionModel: aiConfig.EnableDecisionModel,
|
||||||
|
decisionProvider: 'custom',
|
||||||
|
decisionApiKey: aiConfig.DecisionCustomApiKey,
|
||||||
|
decisionBaseURL: aiConfig.DecisionCustomBaseURL,
|
||||||
|
decisionModel: aiConfig.DecisionCustomModel,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
console.log('🎯 数据协调完成, systemPrompt长度:', orchestrationResult.systemPrompt.length);
|
||||||
|
|
||||||
|
// 5. 构建消息列表
|
||||||
|
const systemPrompt = aiConfig.SystemPrompt
|
||||||
|
? `${aiConfig.SystemPrompt}\n\n${orchestrationResult.systemPrompt}`
|
||||||
|
: orchestrationResult.systemPrompt;
|
||||||
|
|
||||||
|
const messages: ChatMessage[] = [
|
||||||
|
{ role: 'user', content: systemPrompt },
|
||||||
|
{ role: 'assistant', content: '明白了,我会按照要求回答用户的问题。' },
|
||||||
|
...history,
|
||||||
|
{ role: 'user', content: message },
|
||||||
|
];
|
||||||
|
|
||||||
|
// 6. 调用自定义API
|
||||||
|
const temperature = aiConfig.Temperature ?? 0.7;
|
||||||
|
const maxTokens = aiConfig.MaxTokens ?? 1000;
|
||||||
|
|
||||||
|
if (!aiConfig.CustomApiKey || !aiConfig.CustomBaseURL) {
|
||||||
|
return NextResponse.json(
|
||||||
|
{ error: '自定义API配置不完整' },
|
||||||
|
{ status: 400 }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const stream = await streamOpenAIChat(messages, {
|
||||||
|
apiKey: aiConfig.CustomApiKey,
|
||||||
|
baseURL: aiConfig.CustomBaseURL,
|
||||||
|
model: aiConfig.CustomModel || 'gpt-3.5-turbo',
|
||||||
|
temperature,
|
||||||
|
maxTokens,
|
||||||
|
});
|
||||||
|
|
||||||
|
// 7. 转换为SSE格式并返回
|
||||||
|
const sseStream = transformToSSE(stream, 'openai');
|
||||||
|
|
||||||
|
return new NextResponse(sseStream, {
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'text/event-stream',
|
||||||
|
'Cache-Control': 'no-cache',
|
||||||
|
Connection: 'keep-alive',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
console.error('❌ AI聊天API错误:', error);
|
||||||
|
return NextResponse.json(
|
||||||
|
{
|
||||||
|
error: 'AI聊天请求失败',
|
||||||
|
details: (error as Error).message,
|
||||||
|
},
|
||||||
|
{ status: 500 }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -49,6 +49,11 @@ export async function GET(request: NextRequest) {
|
|||||||
OIDCButtonText: config.SiteConfig.OIDCButtonText || '',
|
OIDCButtonText: config.SiteConfig.OIDCButtonText || '',
|
||||||
loginBackgroundImage: config.ThemeConfig?.loginBackgroundImage || '',
|
loginBackgroundImage: config.ThemeConfig?.loginBackgroundImage || '',
|
||||||
registerBackgroundImage: config.ThemeConfig?.registerBackgroundImage || '',
|
registerBackgroundImage: config.ThemeConfig?.registerBackgroundImage || '',
|
||||||
|
// AI配置(只暴露功能开关,不暴露API密钥等敏感信息)
|
||||||
|
AIEnabled: config.AIConfig?.Enabled || false,
|
||||||
|
AIEnableHomepageEntry: config.AIConfig?.EnableHomepageEntry || false,
|
||||||
|
AIEnableVideoCardEntry: config.AIConfig?.EnableVideoCardEntry || false,
|
||||||
|
AIEnablePlayPageEntry: config.AIConfig?.EnablePlayPageEntry || false,
|
||||||
};
|
};
|
||||||
return NextResponse.json(result);
|
return NextResponse.json(result);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -73,6 +73,10 @@ export default async function RootLayout({
|
|||||||
let enableOIDCLogin = false;
|
let enableOIDCLogin = false;
|
||||||
let enableOIDCRegistration = false;
|
let enableOIDCRegistration = false;
|
||||||
let oidcButtonText = '';
|
let oidcButtonText = '';
|
||||||
|
let aiEnabled = false;
|
||||||
|
let aiEnableHomepageEntry = false;
|
||||||
|
let aiEnableVideoCardEntry = false;
|
||||||
|
let aiEnablePlayPageEntry = false;
|
||||||
let customCategories = [] as {
|
let customCategories = [] as {
|
||||||
name: string;
|
name: string;
|
||||||
type: 'movie' | 'tv';
|
type: 'movie' | 'tv';
|
||||||
@@ -108,6 +112,11 @@ export default async function RootLayout({
|
|||||||
enableOIDCLogin = config.SiteConfig.EnableOIDCLogin || false;
|
enableOIDCLogin = config.SiteConfig.EnableOIDCLogin || false;
|
||||||
enableOIDCRegistration = config.SiteConfig.EnableOIDCRegistration || false;
|
enableOIDCRegistration = config.SiteConfig.EnableOIDCRegistration || false;
|
||||||
oidcButtonText = config.SiteConfig.OIDCButtonText || '';
|
oidcButtonText = config.SiteConfig.OIDCButtonText || '';
|
||||||
|
// AI配置
|
||||||
|
aiEnabled = config.AIConfig?.Enabled || false;
|
||||||
|
aiEnableHomepageEntry = config.AIConfig?.EnableHomepageEntry || false;
|
||||||
|
aiEnableVideoCardEntry = config.AIConfig?.EnableVideoCardEntry || false;
|
||||||
|
aiEnablePlayPageEntry = config.AIConfig?.EnablePlayPageEntry || false;
|
||||||
// 检查是否启用了 OpenList 功能
|
// 检查是否启用了 OpenList 功能
|
||||||
openListEnabled = !!(
|
openListEnabled = !!(
|
||||||
config.OpenListConfig?.Enabled &&
|
config.OpenListConfig?.Enabled &&
|
||||||
@@ -142,6 +151,10 @@ export default async function RootLayout({
|
|||||||
ENABLE_OIDC_LOGIN: enableOIDCLogin,
|
ENABLE_OIDC_LOGIN: enableOIDCLogin,
|
||||||
ENABLE_OIDC_REGISTRATION: enableOIDCRegistration,
|
ENABLE_OIDC_REGISTRATION: enableOIDCRegistration,
|
||||||
OIDC_BUTTON_TEXT: oidcButtonText,
|
OIDC_BUTTON_TEXT: oidcButtonText,
|
||||||
|
AI_ENABLED: aiEnabled,
|
||||||
|
AI_ENABLE_HOMEPAGE_ENTRY: aiEnableHomepageEntry,
|
||||||
|
AI_ENABLE_VIDEOCARD_ENTRY: aiEnableVideoCardEntry,
|
||||||
|
AI_ENABLE_PLAYPAGE_ENTRY: aiEnablePlayPageEntry,
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
+38
-3
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import { ChevronRight } from 'lucide-react';
|
import { ChevronRight, Bot } from 'lucide-react';
|
||||||
import Link from 'next/link';
|
import Link from 'next/link';
|
||||||
import { Suspense, useCallback, useEffect, useRef, useState } from 'react';
|
import { Suspense, useCallback, useEffect, useRef, useState } from 'react';
|
||||||
|
|
||||||
@@ -21,6 +21,7 @@ import { useSite } from '@/components/SiteProvider';
|
|||||||
import VideoCard from '@/components/VideoCard';
|
import VideoCard from '@/components/VideoCard';
|
||||||
import HttpWarningDialog from '@/components/HttpWarningDialog';
|
import HttpWarningDialog from '@/components/HttpWarningDialog';
|
||||||
import BannerCarousel from '@/components/BannerCarousel';
|
import BannerCarousel from '@/components/BannerCarousel';
|
||||||
|
import AIChatPanel from '@/components/AIChatPanel';
|
||||||
|
|
||||||
function HomeClient() {
|
function HomeClient() {
|
||||||
// 移除了 activeTab 状态,收藏夹功能已移到 UserMenu
|
// 移除了 activeTab 状态,收藏夹功能已移到 UserMenu
|
||||||
@@ -37,6 +38,18 @@ function HomeClient() {
|
|||||||
|
|
||||||
const [showAnnouncement, setShowAnnouncement] = useState(false);
|
const [showAnnouncement, setShowAnnouncement] = useState(false);
|
||||||
const [showHttpWarning, setShowHttpWarning] = useState(true);
|
const [showHttpWarning, setShowHttpWarning] = useState(true);
|
||||||
|
const [showAIChat, setShowAIChat] = useState(false);
|
||||||
|
const [aiEnabled, setAiEnabled] = useState(false);
|
||||||
|
|
||||||
|
// 检查AI功能是否启用
|
||||||
|
useEffect(() => {
|
||||||
|
if (typeof window !== 'undefined') {
|
||||||
|
const enabled =
|
||||||
|
(window as any).RUNTIME_CONFIG?.AI_ENABLED &&
|
||||||
|
(window as any).RUNTIME_CONFIG?.AI_ENABLE_HOMEPAGE_ENTRY;
|
||||||
|
setAiEnabled(enabled);
|
||||||
|
}
|
||||||
|
}, []);
|
||||||
|
|
||||||
// 检查公告弹窗状态
|
// 检查公告弹窗状态
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -133,14 +146,27 @@ function HomeClient() {
|
|||||||
return (
|
return (
|
||||||
<PageLayout>
|
<PageLayout>
|
||||||
{/* TMDB 热门轮播图 */}
|
{/* TMDB 热门轮播图 */}
|
||||||
<div className='w-full mb-6 sm:mb-8'>
|
<div className='w-full mb-4'>
|
||||||
<BannerCarousel />
|
<BannerCarousel />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className='px-2 sm:px-10 py-4 sm:py-8 overflow-visible'>
|
<div className='px-2 sm:px-10 pb-4 sm:pb-8 overflow-visible'>
|
||||||
<div className='max-w-[95%] mx-auto'>
|
<div className='max-w-[95%] mx-auto'>
|
||||||
{/* 首页内容 */}
|
{/* 首页内容 */}
|
||||||
<>
|
<>
|
||||||
|
{/* AI问片入口 */}
|
||||||
|
{aiEnabled && (
|
||||||
|
<div className='flex items-center justify-end mb-4'>
|
||||||
|
<button
|
||||||
|
onClick={() => setShowAIChat(true)}
|
||||||
|
className='p-2 rounded-lg bg-purple-500 text-white hover:bg-purple-600 transition-colors'
|
||||||
|
title='AI问片'
|
||||||
|
>
|
||||||
|
<Bot size={20} />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
{/* 继续观看 */}
|
{/* 继续观看 */}
|
||||||
<ContinueWatching />
|
<ContinueWatching />
|
||||||
|
|
||||||
@@ -432,6 +458,15 @@ function HomeClient() {
|
|||||||
<HttpWarningDialog onClose={() => setShowHttpWarning(false)} />
|
<HttpWarningDialog onClose={() => setShowHttpWarning(false)} />
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{/* AI问片面板 */}
|
||||||
|
{aiEnabled && (
|
||||||
|
<AIChatPanel
|
||||||
|
isOpen={showAIChat}
|
||||||
|
onClose={() => setShowAIChat(false)}
|
||||||
|
welcomeMessage='你好!我是MoonTVPlus的AI影视助手。想看什么电影或剧集?需要推荐吗?'
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
{/* 公告弹窗 */}
|
{/* 公告弹窗 */}
|
||||||
{showAnnouncement && (
|
{showAnnouncement && (
|
||||||
<div className='fixed inset-0 bg-black bg-opacity-50 z-50 flex items-center justify-center p-4'>
|
<div className='fixed inset-0 bg-black bg-opacity-50 z-50 flex items-center justify-center p-4'>
|
||||||
|
|||||||
+49
-4
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import { Heart, Search, X, Cloud } from 'lucide-react';
|
import { Heart, Search, X, Cloud, Sparkles } from 'lucide-react';
|
||||||
import { useRouter, useSearchParams } from 'next/navigation';
|
import { useRouter, useSearchParams } from 'next/navigation';
|
||||||
import { Suspense, useEffect, useRef, useState } from 'react';
|
import { Suspense, useEffect, useRef, useState } from 'react';
|
||||||
|
|
||||||
@@ -48,6 +48,7 @@ import DoubanComments from '@/components/DoubanComments';
|
|||||||
import SmartRecommendations from '@/components/SmartRecommendations';
|
import SmartRecommendations from '@/components/SmartRecommendations';
|
||||||
import DanmakuFilterSettings from '@/components/DanmakuFilterSettings';
|
import DanmakuFilterSettings from '@/components/DanmakuFilterSettings';
|
||||||
import Toast, { ToastProps } from '@/components/Toast';
|
import Toast, { ToastProps } from '@/components/Toast';
|
||||||
|
import AIChatPanel from '@/components/AIChatPanel';
|
||||||
import { useEnableComments } from '@/hooks/useEnableComments';
|
import { useEnableComments } from '@/hooks/useEnableComments';
|
||||||
import PansouSearch from '@/components/PansouSearch';
|
import PansouSearch from '@/components/PansouSearch';
|
||||||
|
|
||||||
@@ -104,6 +105,20 @@ function PlayPageClient() {
|
|||||||
// 网盘搜索弹窗状态
|
// 网盘搜索弹窗状态
|
||||||
const [showPansouDialog, setShowPansouDialog] = useState(false);
|
const [showPansouDialog, setShowPansouDialog] = useState(false);
|
||||||
|
|
||||||
|
// AI问片状态
|
||||||
|
const [showAIChat, setShowAIChat] = useState(false);
|
||||||
|
const [aiEnabled, setAiEnabled] = useState(false);
|
||||||
|
|
||||||
|
// 检查AI功能是否启用
|
||||||
|
useEffect(() => {
|
||||||
|
if (typeof window !== 'undefined') {
|
||||||
|
const enabled =
|
||||||
|
(window as any).RUNTIME_CONFIG?.AI_ENABLED &&
|
||||||
|
(window as any).RUNTIME_CONFIG?.AI_ENABLE_PLAYPAGE_ENTRY;
|
||||||
|
setAiEnabled(enabled);
|
||||||
|
}
|
||||||
|
}, []);
|
||||||
|
|
||||||
// 网页全屏状态 - 控制导航栏的显示隐藏
|
// 网页全屏状态 - 控制导航栏的显示隐藏
|
||||||
const [isWebFullscreen, setIsWebFullscreen] = useState(false);
|
const [isWebFullscreen, setIsWebFullscreen] = useState(false);
|
||||||
|
|
||||||
@@ -5039,10 +5054,10 @@ function PlayPageClient() {
|
|||||||
|
|
||||||
{/* 第三方应用打开按钮 - 观影室同步状态下隐藏 */}
|
{/* 第三方应用打开按钮 - 观影室同步状态下隐藏 */}
|
||||||
{videoUrl && !playSync.isInRoom && (
|
{videoUrl && !playSync.isInRoom && (
|
||||||
<div className='mt-3 px-2 lg:flex-shrink-0 flex justify-end'>
|
<div className='mt-3 px-2 lg:flex-shrink-0'>
|
||||||
<div className='bg-white/50 dark:bg-gray-800/50 backdrop-blur-sm rounded-lg p-2 border border-gray-200/50 dark:border-gray-700/50 w-full lg:w-auto overflow-x-auto'>
|
<div className='bg-white/50 dark:bg-gray-800/50 backdrop-blur-sm rounded-lg p-2 border border-gray-200/50 dark:border-gray-700/50 w-full lg:w-auto overflow-x-auto'>
|
||||||
<div className='flex gap-1.5 justify-between lg:flex-wrap items-center'>
|
<div className='flex gap-1.5 flex-nowrap lg:flex-wrap items-center'>
|
||||||
<div className='flex gap-1.5 lg:flex-wrap'>
|
<div className='flex gap-1.5 flex-nowrap lg:flex-wrap'>
|
||||||
{/* 下载按钮 */}
|
{/* 下载按钮 */}
|
||||||
<button
|
<button
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
@@ -5346,6 +5361,19 @@ function PlayPageClient() {
|
|||||||
>
|
>
|
||||||
<Cloud className='h-6 w-6 text-gray-700 dark:text-gray-300' />
|
<Cloud className='h-6 w-6 text-gray-700 dark:text-gray-300' />
|
||||||
</button>
|
</button>
|
||||||
|
{/* AI问片按钮 */}
|
||||||
|
{aiEnabled && detail && (
|
||||||
|
<button
|
||||||
|
onClick={(e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
setShowAIChat(true);
|
||||||
|
}}
|
||||||
|
className='flex-shrink-0 hover:opacity-80 transition-opacity'
|
||||||
|
title='AI问片'
|
||||||
|
>
|
||||||
|
<Sparkles className='h-6 w-6 text-gray-700 dark:text-gray-300' />
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
{/* 豆瓣评分显示 */}
|
{/* 豆瓣评分显示 */}
|
||||||
{doubanRating && doubanRating.value > 0 && (
|
{doubanRating && doubanRating.value > 0 && (
|
||||||
<div className='flex items-center gap-2 text-base font-normal'>
|
<div className='flex items-center gap-2 text-base font-normal'>
|
||||||
@@ -5603,6 +5631,23 @@ function PlayPageClient() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{/* AI问片面板 */}
|
||||||
|
{aiEnabled && showAIChat && detail && (
|
||||||
|
<AIChatPanel
|
||||||
|
isOpen={showAIChat}
|
||||||
|
onClose={() => setShowAIChat(false)}
|
||||||
|
context={{
|
||||||
|
title: detail.title,
|
||||||
|
year: detail.year,
|
||||||
|
douban_id: videoDoubanId !== 0 ? videoDoubanId : undefined,
|
||||||
|
tmdb_id: detail.tmdb_id,
|
||||||
|
type: detail.type === 'movie' ? 'movie' : 'tv',
|
||||||
|
currentEpisode: currentEpisodeIndex + 1,
|
||||||
|
}}
|
||||||
|
welcomeMessage={`想了解《${detail.title}》的更多信息吗?我可以帮你查询剧情、演员、评价等。`}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</PageLayout>
|
</PageLayout>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,359 @@
|
|||||||
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||||
|
'use client';
|
||||||
|
|
||||||
|
import React, { useState, useRef, useEffect } from 'react';
|
||||||
|
import { createPortal } from 'react-dom';
|
||||||
|
import { X, Send, Bot, Loader2, Sparkles } from 'lucide-react';
|
||||||
|
import ReactMarkdown from 'react-markdown';
|
||||||
|
import remarkGfm from 'remark-gfm';
|
||||||
|
import { VideoContext } from '@/lib/ai-orchestrator';
|
||||||
|
|
||||||
|
interface ChatMessage {
|
||||||
|
role: 'user' | 'assistant';
|
||||||
|
content: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface AIChatPanelProps {
|
||||||
|
isOpen: boolean;
|
||||||
|
onClose: () => void;
|
||||||
|
context?: VideoContext;
|
||||||
|
welcomeMessage?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function AIChatPanel({
|
||||||
|
isOpen,
|
||||||
|
onClose,
|
||||||
|
context,
|
||||||
|
welcomeMessage = '你好!我是MoonTVPlus的AI影视助手,有什么可以帮你的吗?',
|
||||||
|
}: AIChatPanelProps) {
|
||||||
|
const [messages, setMessages] = useState<ChatMessage[]>([
|
||||||
|
{ role: 'assistant', content: welcomeMessage },
|
||||||
|
]);
|
||||||
|
const [input, setInput] = useState('');
|
||||||
|
const [isStreaming, setIsStreaming] = useState(false);
|
||||||
|
const messagesEndRef = useRef<HTMLDivElement>(null);
|
||||||
|
const inputRef = useRef<HTMLTextAreaElement>(null);
|
||||||
|
|
||||||
|
// 自动滚动到底部
|
||||||
|
const scrollToBottom = () => {
|
||||||
|
messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' });
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
scrollToBottom();
|
||||||
|
}, [messages]);
|
||||||
|
|
||||||
|
// 自动聚焦输入框和防止背景滚动
|
||||||
|
useEffect(() => {
|
||||||
|
if (isOpen) {
|
||||||
|
// 聚焦输入框
|
||||||
|
if (inputRef.current) {
|
||||||
|
inputRef.current.focus();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 防止背景滚动
|
||||||
|
const originalOverflow = document.body.style.overflow;
|
||||||
|
const originalPaddingRight = document.body.style.paddingRight;
|
||||||
|
|
||||||
|
// 获取滚动条宽度
|
||||||
|
const scrollbarWidth = window.innerWidth - document.documentElement.clientWidth;
|
||||||
|
|
||||||
|
document.body.style.overflow = 'hidden';
|
||||||
|
document.body.style.paddingRight = `${scrollbarWidth}px`;
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
document.body.style.overflow = originalOverflow;
|
||||||
|
document.body.style.paddingRight = originalPaddingRight;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}, [isOpen]);
|
||||||
|
|
||||||
|
const handleSendMessage = async () => {
|
||||||
|
if (!input.trim() || isStreaming) return;
|
||||||
|
|
||||||
|
const userMessage = input.trim();
|
||||||
|
setInput('');
|
||||||
|
|
||||||
|
// 添加用户消息
|
||||||
|
setMessages((prev) => [...prev, { role: 'user', content: userMessage }]);
|
||||||
|
|
||||||
|
// 开始流式响应
|
||||||
|
setIsStreaming(true);
|
||||||
|
|
||||||
|
// 先添加一个空的助手消息用于流式更新或显示错误
|
||||||
|
setMessages((prev) => [...prev, { role: 'assistant', content: '' }]);
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await fetch('/api/ai/chat', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
message: userMessage,
|
||||||
|
context,
|
||||||
|
history: messages.filter((m) => m.role !== 'assistant' || m.content !== welcomeMessage),
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
const errorData = await response.json().catch(() => ({}));
|
||||||
|
const errorMsg = errorData.error || errorData.details || `请求失败 (${response.status})`;
|
||||||
|
throw new Error(errorMsg);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理流式响应
|
||||||
|
const reader = response.body?.getReader();
|
||||||
|
const decoder = new TextDecoder();
|
||||||
|
|
||||||
|
if (!reader) {
|
||||||
|
throw new Error('无法读取响应流');
|
||||||
|
}
|
||||||
|
|
||||||
|
let assistantMessage = '';
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
const { done, value } = await reader.read();
|
||||||
|
if (done) break;
|
||||||
|
|
||||||
|
const chunk = decoder.decode(value, { stream: true });
|
||||||
|
const lines = chunk.split('\n').filter((line) => line.trim() !== '');
|
||||||
|
|
||||||
|
for (const line of lines) {
|
||||||
|
if (line.startsWith('data: ')) {
|
||||||
|
const data = line.slice(6);
|
||||||
|
|
||||||
|
if (data === '[DONE]') {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const json = JSON.parse(data);
|
||||||
|
const text = json.text || '';
|
||||||
|
|
||||||
|
if (text) {
|
||||||
|
assistantMessage += text;
|
||||||
|
|
||||||
|
// 更新最后一条消息
|
||||||
|
setMessages((prev) => {
|
||||||
|
const newMessages = [...prev];
|
||||||
|
newMessages[newMessages.length - 1] = {
|
||||||
|
role: 'assistant',
|
||||||
|
content: assistantMessage,
|
||||||
|
};
|
||||||
|
return newMessages;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.error('解析SSE数据失败:', e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('发送消息失败:', error);
|
||||||
|
|
||||||
|
// 更新最后一条空消息为错误消息
|
||||||
|
setMessages((prev) => {
|
||||||
|
const newMessages = [...prev];
|
||||||
|
newMessages[newMessages.length - 1] = {
|
||||||
|
role: 'assistant',
|
||||||
|
content: `❌ 抱歉,出现了错误:\n\n${(error as Error).message}\n\n请检查:\n- AI服务配置是否正确\n- API密钥是否有效\n- 网络连接是否正常`,
|
||||||
|
};
|
||||||
|
return newMessages;
|
||||||
|
});
|
||||||
|
} finally {
|
||||||
|
setIsStreaming(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleKeyDown = (e: React.KeyboardEvent<HTMLTextAreaElement>) => {
|
||||||
|
if (e.key === 'Enter' && !e.shiftKey) {
|
||||||
|
e.preventDefault();
|
||||||
|
handleSendMessage();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!isOpen) return null;
|
||||||
|
|
||||||
|
const modalContent = (
|
||||||
|
<div
|
||||||
|
className='fixed inset-0 z-[1002] flex items-center justify-center bg-black/50 backdrop-blur-sm overflow-hidden'
|
||||||
|
onClick={(e) => {
|
||||||
|
// 点击遮罩层关闭弹窗
|
||||||
|
if (e.target === e.currentTarget) {
|
||||||
|
onClose();
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div className='relative mx-4 my-auto flex h-[85vh] sm:h-[80vh] max-h-[90vh] sm:max-h-[600px] w-full max-w-3xl flex-col rounded-2xl bg-white shadow-2xl dark:bg-gray-900'>
|
||||||
|
{/* 头部 */}
|
||||||
|
<div className='flex items-center justify-between border-b border-gray-200 p-4 dark:border-gray-700'>
|
||||||
|
<div className='flex items-center gap-3'>
|
||||||
|
<div className='flex h-10 w-10 items-center justify-center rounded-full bg-purple-500'>
|
||||||
|
<Sparkles size={20} className='text-white' />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<h2 className='text-lg font-semibold text-gray-900 dark:text-white'>
|
||||||
|
AI影视助手
|
||||||
|
</h2>
|
||||||
|
{context?.title && (
|
||||||
|
<p className='text-xs text-gray-500 dark:text-gray-400'>
|
||||||
|
正在讨论: {context.title}
|
||||||
|
{context.year && ` (${context.year})`}
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
onClick={onClose}
|
||||||
|
className='rounded-lg p-2 text-gray-500 transition-colors hover:bg-gray-100 dark:text-gray-400 dark:hover:bg-gray-800'
|
||||||
|
>
|
||||||
|
<X size={20} />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* 消息列表 */}
|
||||||
|
<div className='flex-1 overflow-y-auto p-4'>
|
||||||
|
<div className='space-y-4'>
|
||||||
|
{messages.map((message, index) => (
|
||||||
|
<div
|
||||||
|
key={index}
|
||||||
|
className={`flex ${message.role === 'user' ? 'justify-end' : 'justify-start'}`}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
className={`flex max-w-[80%] gap-3 ${message.role === 'user' ? 'flex-row-reverse' : 'flex-row'}`}
|
||||||
|
>
|
||||||
|
{/* 头像 */}
|
||||||
|
<div
|
||||||
|
className={`flex h-8 w-8 shrink-0 items-center justify-center rounded-full ${
|
||||||
|
message.role === 'user'
|
||||||
|
? 'bg-blue-500'
|
||||||
|
: 'bg-purple-500'
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
{message.role === 'user' ? (
|
||||||
|
<span className='text-xs font-semibold text-white'>
|
||||||
|
U
|
||||||
|
</span>
|
||||||
|
) : (
|
||||||
|
<Bot size={16} className='text-white' />
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* 消息内容 */}
|
||||||
|
<div
|
||||||
|
className={`rounded-2xl px-4 py-2 ${
|
||||||
|
message.role === 'user'
|
||||||
|
? 'bg-blue-500 text-white'
|
||||||
|
: 'bg-gray-100 text-gray-900 dark:bg-gray-800 dark:text-white'
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
{message.role === 'user' ? (
|
||||||
|
<p className='whitespace-pre-wrap break-words text-sm leading-relaxed'>
|
||||||
|
{message.content}
|
||||||
|
</p>
|
||||||
|
) : (
|
||||||
|
<div className='prose prose-sm max-w-none dark:prose-invert prose-p:my-2 prose-p:leading-relaxed prose-pre:bg-gray-800 prose-pre:text-gray-100 dark:prose-pre:bg-gray-900 prose-code:text-purple-600 dark:prose-code:text-purple-400 prose-code:bg-purple-50 dark:prose-code:bg-purple-900/20 prose-code:px-1 prose-code:py-0.5 prose-code:rounded prose-code:before:content-none prose-code:after:content-none prose-a:text-blue-600 dark:prose-a:text-blue-400 prose-strong:text-gray-900 dark:prose-strong:text-white prose-ul:my-2 prose-ol:my-2 prose-li:my-1'>
|
||||||
|
<ReactMarkdown remarkPlugins={[remarkGfm]}>
|
||||||
|
{message.content}
|
||||||
|
</ReactMarkdown>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
|
||||||
|
{/* 加载指示器 */}
|
||||||
|
{isStreaming && (
|
||||||
|
<div className='flex justify-start'>
|
||||||
|
<div className='flex max-w-[80%] gap-3'>
|
||||||
|
<div className='flex h-8 w-8 shrink-0 items-center justify-center rounded-full bg-purple-500'>
|
||||||
|
<Bot size={16} className='text-white' />
|
||||||
|
</div>
|
||||||
|
<div className='flex items-center gap-2 rounded-2xl bg-gray-100 px-4 py-2 dark:bg-gray-800'>
|
||||||
|
<Loader2 size={16} className='animate-spin text-gray-500' />
|
||||||
|
<span className='text-sm text-gray-500 dark:text-gray-400'>
|
||||||
|
AI正在思考...
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<div ref={messagesEndRef} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* 输入区域 */}
|
||||||
|
<div className='border-t border-gray-200 p-4 dark:border-gray-700'>
|
||||||
|
<div className='flex gap-2'>
|
||||||
|
<textarea
|
||||||
|
ref={inputRef}
|
||||||
|
value={input}
|
||||||
|
onChange={(e) => setInput(e.target.value)}
|
||||||
|
onKeyDown={handleKeyDown}
|
||||||
|
placeholder='输入你的问题... (Shift+Enter换行)'
|
||||||
|
disabled={isStreaming}
|
||||||
|
rows={1}
|
||||||
|
className='flex-1 resize-none rounded-xl border border-gray-300 bg-white px-4 py-3 text-sm text-gray-900 placeholder-gray-400 transition-colors focus:border-purple-500 focus:outline-none focus:ring-2 focus:ring-purple-500/20 disabled:cursor-not-allowed disabled:opacity-50 dark:border-gray-600 dark:bg-gray-800 dark:text-white dark:placeholder-gray-500 dark:focus:border-purple-400'
|
||||||
|
style={{
|
||||||
|
minHeight: '48px',
|
||||||
|
maxHeight: '120px',
|
||||||
|
}}
|
||||||
|
onInput={(e) => {
|
||||||
|
const target = e.target as HTMLTextAreaElement;
|
||||||
|
target.style.height = 'auto';
|
||||||
|
target.style.height = `${Math.min(target.scrollHeight, 120)}px`;
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<button
|
||||||
|
onClick={handleSendMessage}
|
||||||
|
disabled={!input.trim() || isStreaming}
|
||||||
|
className='flex h-12 w-12 shrink-0 items-center justify-center rounded-xl bg-purple-500 text-white transition-colors hover:bg-purple-600 disabled:cursor-not-allowed disabled:opacity-50'
|
||||||
|
>
|
||||||
|
{isStreaming ? (
|
||||||
|
<Loader2 size={20} className='animate-spin' />
|
||||||
|
) : (
|
||||||
|
<Send size={20} />
|
||||||
|
)}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* 快捷提示 */}
|
||||||
|
{messages.length === 1 && !isStreaming && (
|
||||||
|
<div className='mt-3 flex flex-wrap gap-2'>
|
||||||
|
<button
|
||||||
|
onClick={() => setInput('推荐一些高分电影')}
|
||||||
|
className='rounded-full bg-gray-100 px-3 py-1 text-xs text-gray-600 transition-colors hover:bg-gray-200 dark:bg-gray-800 dark:text-gray-400 dark:hover:bg-gray-700'
|
||||||
|
>
|
||||||
|
推荐高分电影
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
onClick={() => setInput('最近有什么新电影上映?')}
|
||||||
|
className='rounded-full bg-gray-100 px-3 py-1 text-xs text-gray-600 transition-colors hover:bg-gray-200 dark:bg-gray-800 dark:text-gray-400 dark:hover:bg-gray-700'
|
||||||
|
>
|
||||||
|
最新上映
|
||||||
|
</button>
|
||||||
|
{context?.title && (
|
||||||
|
<button
|
||||||
|
onClick={() =>
|
||||||
|
setInput(`${context.title}讲的是什么故事?`)
|
||||||
|
}
|
||||||
|
className='rounded-full bg-gray-100 px-3 py-1 text-xs text-gray-600 transition-colors hover:bg-gray-200 dark:bg-gray-800 dark:text-gray-400 dark:hover:bg-gray-700'
|
||||||
|
>
|
||||||
|
剧情介绍
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
|
return typeof window !== 'undefined'
|
||||||
|
? createPortal(modalContent, document.body)
|
||||||
|
: null;
|
||||||
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
/* eslint-disable @typescript-eslint/no-explicit-any,react-hooks/exhaustive-deps,@typescript-eslint/no-empty-function */
|
/* eslint-disable @typescript-eslint/no-explicit-any,react-hooks/exhaustive-deps,@typescript-eslint/no-empty-function */
|
||||||
|
|
||||||
import { ExternalLink, Heart, Link, PlayCircleIcon, Radio, Trash2 } from 'lucide-react';
|
import { ExternalLink, Heart, Link, PlayCircleIcon, Radio, Sparkles, Trash2 } from 'lucide-react';
|
||||||
import Image from 'next/image';
|
import Image from 'next/image';
|
||||||
import { useRouter } from 'next/navigation';
|
import { useRouter } from 'next/navigation';
|
||||||
import React, {
|
import React, {
|
||||||
@@ -26,6 +26,7 @@ import { useLongPress } from '@/hooks/useLongPress';
|
|||||||
|
|
||||||
import { ImagePlaceholder } from '@/components/ImagePlaceholder';
|
import { ImagePlaceholder } from '@/components/ImagePlaceholder';
|
||||||
import MobileActionSheet from '@/components/MobileActionSheet';
|
import MobileActionSheet from '@/components/MobileActionSheet';
|
||||||
|
import AIChatPanel from '@/components/AIChatPanel';
|
||||||
|
|
||||||
export interface VideoCardProps {
|
export interface VideoCardProps {
|
||||||
id?: string;
|
id?: string;
|
||||||
@@ -100,6 +101,18 @@ const VideoCard = forwardRef<VideoCardHandle, VideoCardProps>(function VideoCard
|
|||||||
const [isLoading, setIsLoading] = useState(false);
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
const [showMobileActions, setShowMobileActions] = useState(false);
|
const [showMobileActions, setShowMobileActions] = useState(false);
|
||||||
const [searchFavorited, setSearchFavorited] = useState<boolean | null>(null); // 搜索结果的收藏状态
|
const [searchFavorited, setSearchFavorited] = useState<boolean | null>(null); // 搜索结果的收藏状态
|
||||||
|
const [showAIChat, setShowAIChat] = useState(false);
|
||||||
|
const [aiEnabled, setAiEnabled] = useState(false);
|
||||||
|
|
||||||
|
// 检查AI功能是否启用
|
||||||
|
useEffect(() => {
|
||||||
|
if (typeof window !== 'undefined') {
|
||||||
|
const enabled =
|
||||||
|
(window as any).RUNTIME_CONFIG?.AI_ENABLED &&
|
||||||
|
(window as any).RUNTIME_CONFIG?.AI_ENABLE_VIDEOCARD_ENTRY;
|
||||||
|
setAiEnabled(enabled);
|
||||||
|
}
|
||||||
|
}, []);
|
||||||
|
|
||||||
// 可外部修改的可控字段
|
// 可外部修改的可控字段
|
||||||
const [dynamicEpisodes, setDynamicEpisodes] = useState<number | undefined>(
|
const [dynamicEpisodes, setDynamicEpisodes] = useState<number | undefined>(
|
||||||
@@ -540,6 +553,20 @@ const VideoCard = forwardRef<VideoCardHandle, VideoCardProps>(function VideoCard
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// AI问片功能
|
||||||
|
if (aiEnabled && actualTitle) {
|
||||||
|
actions.push({
|
||||||
|
id: 'ai-chat',
|
||||||
|
label: 'AI问片',
|
||||||
|
icon: <Sparkles size={20} />,
|
||||||
|
onClick: () => {
|
||||||
|
setShowMobileActions(false); // 关闭菜单
|
||||||
|
setShowAIChat(true);
|
||||||
|
},
|
||||||
|
color: 'default' as const,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
return actions;
|
return actions;
|
||||||
}, [
|
}, [
|
||||||
config,
|
config,
|
||||||
@@ -555,6 +582,9 @@ const VideoCard = forwardRef<VideoCardHandle, VideoCardProps>(function VideoCard
|
|||||||
handleClick,
|
handleClick,
|
||||||
handleToggleFavorite,
|
handleToggleFavorite,
|
||||||
handleDeleteRecord,
|
handleDeleteRecord,
|
||||||
|
handlePlayInNewTab,
|
||||||
|
aiEnabled,
|
||||||
|
actualTitle,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -1355,6 +1385,23 @@ const VideoCard = forwardRef<VideoCardHandle, VideoCardProps>(function VideoCard
|
|||||||
totalEpisodes={actualEpisodes}
|
totalEpisodes={actualEpisodes}
|
||||||
origin={origin}
|
origin={origin}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
{/* AI问片面板 */}
|
||||||
|
{aiEnabled && showAIChat && (
|
||||||
|
<AIChatPanel
|
||||||
|
isOpen={showAIChat}
|
||||||
|
onClose={() => setShowAIChat(false)}
|
||||||
|
context={{
|
||||||
|
title: actualTitle,
|
||||||
|
year: actualYear,
|
||||||
|
douban_id: actualDoubanId,
|
||||||
|
tmdb_id,
|
||||||
|
type: actualSearchType as 'movie' | 'tv',
|
||||||
|
currentEpisode,
|
||||||
|
}}
|
||||||
|
welcomeMessage={`想了解《${actualTitle}》的更多信息吗?我可以帮你查询剧情、演员、评价等。`}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -112,6 +112,46 @@ export interface AdminConfig {
|
|||||||
ResourceCount?: number; // 资源数量
|
ResourceCount?: number; // 资源数量
|
||||||
ScanInterval?: number; // 定时扫描间隔(分钟),0表示关闭,最低60分钟
|
ScanInterval?: number; // 定时扫描间隔(分钟),0表示关闭,最低60分钟
|
||||||
};
|
};
|
||||||
|
AIConfig?: {
|
||||||
|
Enabled: boolean; // 是否启用AI问片功能
|
||||||
|
Provider: 'openai' | 'claude' | 'custom'; // AI服务提供商
|
||||||
|
// OpenAI配置
|
||||||
|
OpenAIApiKey?: string;
|
||||||
|
OpenAIBaseURL?: string; // 自定义API地址(如Azure、国内代理等)
|
||||||
|
OpenAIModel?: string; // 模型名称,如gpt-4, gpt-3.5-turbo
|
||||||
|
// Claude配置
|
||||||
|
ClaudeApiKey?: string;
|
||||||
|
ClaudeModel?: string; // 模型名称,如claude-3-opus-20240229
|
||||||
|
// 自定义配置(兼容OpenAI格式的API)
|
||||||
|
CustomApiKey?: string;
|
||||||
|
CustomBaseURL?: string;
|
||||||
|
CustomModel?: string;
|
||||||
|
// 决策模型配置
|
||||||
|
EnableDecisionModel: boolean; // 是否启用决策模型(用AI判断是否需要联网/数据源)
|
||||||
|
DecisionProvider?: 'openai' | 'claude' | 'custom'; // 决策模型提供商
|
||||||
|
DecisionOpenAIApiKey?: string;
|
||||||
|
DecisionOpenAIBaseURL?: string;
|
||||||
|
DecisionOpenAIModel?: string;
|
||||||
|
DecisionClaudeApiKey?: string;
|
||||||
|
DecisionClaudeModel?: string;
|
||||||
|
DecisionCustomApiKey?: string;
|
||||||
|
DecisionCustomBaseURL?: string;
|
||||||
|
DecisionCustomModel?: string;
|
||||||
|
// 联网搜索配置
|
||||||
|
EnableWebSearch: boolean; // 是否启用联网搜索
|
||||||
|
WebSearchProvider?: 'tavily' | 'serper' | 'serpapi'; // 搜索服务提供商
|
||||||
|
TavilyApiKey?: string; // Tavily API密钥
|
||||||
|
SerperApiKey?: string; // Serper.dev API密钥
|
||||||
|
SerpApiKey?: string; // SerpAPI密钥
|
||||||
|
// 功能开关
|
||||||
|
EnableHomepageEntry: boolean; // 首页入口开关
|
||||||
|
EnableVideoCardEntry: boolean; // VideoCard入口开关
|
||||||
|
EnablePlayPageEntry: boolean; // 播放页入口开关
|
||||||
|
// 高级设置
|
||||||
|
Temperature?: number; // AI温度参数(0-2),默认0.7
|
||||||
|
MaxTokens?: number; // 最大回复token数,默认1000
|
||||||
|
SystemPrompt?: string; // 自定义系统提示词
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface AdminConfigResult {
|
export interface AdminConfigResult {
|
||||||
|
|||||||
@@ -0,0 +1,694 @@
|
|||||||
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||||
|
/**
|
||||||
|
* AI数据源协调器
|
||||||
|
* 负责协调AI与联网搜索、豆瓣API、TMDB API之间的数据交互
|
||||||
|
*/
|
||||||
|
|
||||||
|
export interface VideoContext {
|
||||||
|
title?: string;
|
||||||
|
year?: string;
|
||||||
|
douban_id?: number;
|
||||||
|
tmdb_id?: number;
|
||||||
|
type?: 'movie' | 'tv';
|
||||||
|
currentEpisode?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IntentAnalysisResult {
|
||||||
|
type: 'recommendation' | 'query' | 'detail' | 'general';
|
||||||
|
mediaType?: 'movie' | 'tv' | 'variety' | 'anime';
|
||||||
|
genre?: string;
|
||||||
|
needWebSearch: boolean;
|
||||||
|
needDouban: boolean;
|
||||||
|
needTMDB: boolean;
|
||||||
|
keywords: string[];
|
||||||
|
entities: Array<{ type: string; value: string }>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface DecisionResult {
|
||||||
|
needWebSearch: boolean;
|
||||||
|
needDouban: boolean;
|
||||||
|
needTMDB: boolean;
|
||||||
|
webSearchQuery?: string;
|
||||||
|
doubanQuery?: string;
|
||||||
|
reasoning?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface OrchestrationResult {
|
||||||
|
systemPrompt: string;
|
||||||
|
webSearchResults?: any;
|
||||||
|
doubanData?: any;
|
||||||
|
tmdbData?: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 分析用户意图
|
||||||
|
*/
|
||||||
|
export function analyzeIntent(
|
||||||
|
message: string,
|
||||||
|
context?: VideoContext
|
||||||
|
): IntentAnalysisResult {
|
||||||
|
const lowerMessage = message.toLowerCase();
|
||||||
|
|
||||||
|
// 时效性关键词 - 需要最新信息的问题
|
||||||
|
const timeKeywords = [
|
||||||
|
'最新', '今年', '2024', '2025', '即将', '上映', '新出',
|
||||||
|
'什么时候', '何时', '几时', '播出', '更新', '下一季',
|
||||||
|
'第二季', '第三季', '续集', '下季', '下部'
|
||||||
|
];
|
||||||
|
const hasTimeKeyword = timeKeywords.some((k) => message.includes(k));
|
||||||
|
|
||||||
|
// 推荐类关键词
|
||||||
|
const recommendKeywords = ['推荐', '有什么', '好看', '值得', '介绍'];
|
||||||
|
const isRecommendation = recommendKeywords.some((k) => message.includes(k));
|
||||||
|
|
||||||
|
// 演员/导演关键词
|
||||||
|
const personKeywords = ['演员', '导演', '主演', '出演', '作品'];
|
||||||
|
const isPerson = personKeywords.some((k) => message.includes(k));
|
||||||
|
|
||||||
|
// 剧情相关关键词
|
||||||
|
const plotKeywords = ['讲什么', '剧情', '故事', '内容', '讲的是'];
|
||||||
|
const isPlotQuery = plotKeywords.some((k) => message.includes(k));
|
||||||
|
|
||||||
|
// 媒体类型判断
|
||||||
|
let mediaType: 'movie' | 'tv' | 'variety' | 'anime' | undefined;
|
||||||
|
if (message.includes('电影')) mediaType = 'movie';
|
||||||
|
else if (message.includes('电视剧') || message.includes('剧集'))
|
||||||
|
mediaType = 'tv';
|
||||||
|
else if (message.includes('综艺')) mediaType = 'variety';
|
||||||
|
else if (message.includes('动漫') || message.includes('动画'))
|
||||||
|
mediaType = 'anime';
|
||||||
|
else if (context?.type) mediaType = context.type;
|
||||||
|
|
||||||
|
// 类型判断
|
||||||
|
let type: IntentAnalysisResult['type'] = 'general';
|
||||||
|
if (isRecommendation) type = 'recommendation';
|
||||||
|
else if (context?.title && (isPlotQuery || lowerMessage.includes('这部')))
|
||||||
|
type = 'detail';
|
||||||
|
else if (isPerson || hasTimeKeyword) type = 'query';
|
||||||
|
|
||||||
|
// 决定是否需要各个数据源
|
||||||
|
// 联网搜索: 对于推荐、查询、时效性问题、演员信息等都应该启用
|
||||||
|
// 当用户在观看视频时提问(有context),默认也应该联网以获取最新信息
|
||||||
|
const needWebSearch =
|
||||||
|
hasTimeKeyword ||
|
||||||
|
isPerson ||
|
||||||
|
message.includes('新闻') ||
|
||||||
|
isRecommendation ||
|
||||||
|
type === 'query' ||
|
||||||
|
(context?.title !== undefined); // 有上下文时默认联网
|
||||||
|
const needDouban =
|
||||||
|
isRecommendation ||
|
||||||
|
type === 'detail' ||
|
||||||
|
(context?.douban_id !== undefined && context.douban_id > 0);
|
||||||
|
const needTMDB =
|
||||||
|
type === 'detail' ||
|
||||||
|
(context?.tmdb_id !== undefined && context.tmdb_id > 0);
|
||||||
|
|
||||||
|
return {
|
||||||
|
type,
|
||||||
|
mediaType,
|
||||||
|
needWebSearch,
|
||||||
|
needDouban,
|
||||||
|
needTMDB,
|
||||||
|
keywords: timeKeywords.filter((k) => message.includes(k)),
|
||||||
|
entities: extractEntities(message),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 提取实体(简化版,基于关键词匹配)
|
||||||
|
*/
|
||||||
|
function extractEntities(message: string): Array<{ type: string; value: string }> {
|
||||||
|
const entities: Array<{ type: string; value: string }> = [];
|
||||||
|
|
||||||
|
// 简单的人名匹配(中文2-4字)
|
||||||
|
const personPattern = /([一-龥]{2,4})(的|是|演|导)/g;
|
||||||
|
let match;
|
||||||
|
while ((match = personPattern.exec(message)) !== null) {
|
||||||
|
entities.push({ type: 'person', value: match[1] });
|
||||||
|
}
|
||||||
|
|
||||||
|
return entities;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取联网搜索结果
|
||||||
|
*/
|
||||||
|
async function fetchWebSearch(
|
||||||
|
query: string,
|
||||||
|
provider: 'tavily' | 'serper' | 'serpapi',
|
||||||
|
apiKey: string
|
||||||
|
): Promise<any> {
|
||||||
|
try {
|
||||||
|
if (provider === 'tavily') {
|
||||||
|
const response = await fetch('https://api.tavily.com/search', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
api_key: apiKey,
|
||||||
|
query,
|
||||||
|
search_depth: 'basic',
|
||||||
|
include_domains: ['douban.com', 'imdb.com', 'themoviedb.org', 'mtime.com'],
|
||||||
|
max_results: 5,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error(`Tavily API error: ${response.status}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
return await response.json();
|
||||||
|
} else if (provider === 'serper') {
|
||||||
|
const response = await fetch('https://google.serper.dev/search', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'X-API-KEY': apiKey,
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
q: query,
|
||||||
|
num: 5,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error(`Serper API error: ${response.status}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
return await response.json();
|
||||||
|
} else if (provider === 'serpapi') {
|
||||||
|
const response = await fetch(
|
||||||
|
`https://serpapi.com/search?engine=google&q=${encodeURIComponent(query)}&api_key=${apiKey}&num=5`
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error(`SerpAPI error: ${response.status}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
return await response.json();
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Web search error:', error);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取豆瓣数据
|
||||||
|
*/
|
||||||
|
async function fetchDoubanData(params: {
|
||||||
|
id?: number;
|
||||||
|
query?: string;
|
||||||
|
kind?: string;
|
||||||
|
category?: string;
|
||||||
|
type?: string;
|
||||||
|
}): Promise<any> {
|
||||||
|
try {
|
||||||
|
if (params.id) {
|
||||||
|
// 获取详情
|
||||||
|
const response = await fetch(`/api/douban/detail?id=${params.id}`);
|
||||||
|
if (response.ok) {
|
||||||
|
return await response.json();
|
||||||
|
}
|
||||||
|
} else if (params.query) {
|
||||||
|
// 搜索
|
||||||
|
const response = await fetch('/api/douban/search', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
body: JSON.stringify({
|
||||||
|
query: params.query,
|
||||||
|
type: params.kind || 'movie',
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
if (response.ok) {
|
||||||
|
return await response.json();
|
||||||
|
}
|
||||||
|
} else if (params.kind && params.category) {
|
||||||
|
// 分类列表
|
||||||
|
const response = await fetch('/api/douban/categories', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
body: JSON.stringify({
|
||||||
|
kind: params.kind,
|
||||||
|
category: params.category,
|
||||||
|
type: params.type || '全部',
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
if (response.ok) {
|
||||||
|
return await response.json();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Douban API error:', error);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取TMDB数据
|
||||||
|
*/
|
||||||
|
async function fetchTMDBData(params: {
|
||||||
|
id?: number;
|
||||||
|
type?: 'movie' | 'tv';
|
||||||
|
}): Promise<any> {
|
||||||
|
try {
|
||||||
|
if (params.id && params.type) {
|
||||||
|
const response = await fetch(
|
||||||
|
`/api/tmdb/detail?id=${params.id}&type=${params.type}`
|
||||||
|
);
|
||||||
|
if (response.ok) {
|
||||||
|
return await response.json();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('TMDB API error:', error);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 格式化搜索结果为文本
|
||||||
|
*/
|
||||||
|
function formatSearchResults(
|
||||||
|
results: any,
|
||||||
|
provider: 'tavily' | 'serper' | 'serpapi'
|
||||||
|
): string {
|
||||||
|
if (!results) return '';
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (provider === 'tavily' && results.results) {
|
||||||
|
return results.results
|
||||||
|
.map(
|
||||||
|
(r: any) => `
|
||||||
|
标题: ${r.title}
|
||||||
|
内容: ${r.content}
|
||||||
|
来源: ${r.url}
|
||||||
|
`
|
||||||
|
)
|
||||||
|
.join('\n');
|
||||||
|
} else if (provider === 'serper' && results.organic) {
|
||||||
|
return results.organic
|
||||||
|
.map(
|
||||||
|
(r: any) => `
|
||||||
|
标题: ${r.title}
|
||||||
|
摘要: ${r.snippet}
|
||||||
|
来源: ${r.link}
|
||||||
|
`
|
||||||
|
)
|
||||||
|
.join('\n');
|
||||||
|
} else if (provider === 'serpapi' && results.organic_results) {
|
||||||
|
return results.organic_results
|
||||||
|
.map(
|
||||||
|
(r: any) => `
|
||||||
|
标题: ${r.title}
|
||||||
|
摘要: ${r.snippet}
|
||||||
|
来源: ${r.link}
|
||||||
|
`
|
||||||
|
)
|
||||||
|
.join('\n');
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Format search results error:', error);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 使用决策模型判断是否需要调用各个数据源
|
||||||
|
*/
|
||||||
|
async function callDecisionModel(
|
||||||
|
userMessage: string,
|
||||||
|
context: VideoContext | undefined,
|
||||||
|
config: {
|
||||||
|
provider: 'openai' | 'claude' | 'custom';
|
||||||
|
apiKey: string;
|
||||||
|
baseURL?: string;
|
||||||
|
model: string;
|
||||||
|
}
|
||||||
|
): Promise<DecisionResult> {
|
||||||
|
const systemPrompt = `你是一个影视问答决策系统。请分析用户的问题,判断需要调用哪些数据源来回答。
|
||||||
|
|
||||||
|
可用的数据源:
|
||||||
|
1. **联网搜索** - 获取最新的实时信息(新闻、上映时间、续集信息等)
|
||||||
|
2. **豆瓣API** - 获取中文影视数据(评分、演员、简介、用户评论等)
|
||||||
|
3. **TMDB API** - 获取国际影视数据(详细元数据、相似推荐等)
|
||||||
|
|
||||||
|
请以JSON格式返回决策结果,包含以下字段:
|
||||||
|
{
|
||||||
|
"needWebSearch": boolean, // 是否需要联网搜索
|
||||||
|
"needDouban": boolean, // 是否需要豆瓣数据
|
||||||
|
"needTMDB": boolean, // 是否需要TMDB数据
|
||||||
|
"webSearchQuery": string, // 如果需要联网,用什么关键词搜索(可选)
|
||||||
|
"doubanQuery": string, // 如果需要豆瓣,用什么关键词搜索(可选)
|
||||||
|
"reasoning": string // 简要说明决策理由
|
||||||
|
}
|
||||||
|
|
||||||
|
决策原则:
|
||||||
|
- 时效性问题(最新、上映时间、续集、播出等)→ 需要联网搜索
|
||||||
|
- 推荐类问题 → 优先豆瓣
|
||||||
|
- 剧情、演员、评分等静态信息 → 豆瓣或TMDB
|
||||||
|
- 当前视频的详细信息 → 豆瓣+TMDB
|
||||||
|
- 有疑问时倾向于多调用数据源
|
||||||
|
|
||||||
|
只返回JSON,不要其他内容。`;
|
||||||
|
|
||||||
|
let contextInfo = '';
|
||||||
|
if (context?.title) {
|
||||||
|
contextInfo = `\n\n当前视频上下文:\n- 标题:${context.title}`;
|
||||||
|
if (context.year) contextInfo += `\n- 年份:${context.year}`;
|
||||||
|
if (context.type) contextInfo += `\n- 类型:${context.type === 'movie' ? '电影' : '电视剧'}`;
|
||||||
|
if (context.currentEpisode) contextInfo += `\n- 当前集数:第${context.currentEpisode}集`;
|
||||||
|
}
|
||||||
|
|
||||||
|
const userPrompt = `用户问题:${userMessage}${contextInfo}`;
|
||||||
|
|
||||||
|
try {
|
||||||
|
let response: Response;
|
||||||
|
|
||||||
|
if (config.provider === 'claude') {
|
||||||
|
response = await fetch('https://api.anthropic.com/v1/messages', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
'x-api-key': config.apiKey,
|
||||||
|
'anthropic-version': '2023-06-01',
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
model: config.model,
|
||||||
|
max_tokens: 500,
|
||||||
|
temperature: 0,
|
||||||
|
system: systemPrompt,
|
||||||
|
messages: [{ role: 'user', content: userPrompt }],
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error(`Claude API error: ${response.status}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = await response.json();
|
||||||
|
const content = data.content?.[0]?.text || '';
|
||||||
|
|
||||||
|
// 提取JSON
|
||||||
|
const jsonMatch = content.match(/\{[\s\S]*\}/);
|
||||||
|
if (jsonMatch) {
|
||||||
|
return JSON.parse(jsonMatch[0]);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// OpenAI 或 自定义 (OpenAI兼容格式)
|
||||||
|
const baseURL = config.baseURL || 'https://api.openai.com/v1';
|
||||||
|
response = await fetch(`${baseURL}/chat/completions`, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
Authorization: `Bearer ${config.apiKey}`,
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
model: config.model,
|
||||||
|
messages: [
|
||||||
|
{ role: 'system', content: systemPrompt },
|
||||||
|
{ role: 'user', content: userPrompt },
|
||||||
|
],
|
||||||
|
temperature: 0,
|
||||||
|
max_tokens: 500,
|
||||||
|
response_format: { type: 'json_object' },
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error(`OpenAI API error: ${response.status}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = await response.json();
|
||||||
|
const content = data.choices?.[0]?.message?.content || '{}';
|
||||||
|
return JSON.parse(content);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('❌ 决策模型调用失败:', error);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 失败时返回默认决策(保守策略:都调用)
|
||||||
|
return {
|
||||||
|
needWebSearch: true,
|
||||||
|
needDouban: true,
|
||||||
|
needTMDB: context?.tmdb_id !== undefined,
|
||||||
|
reasoning: '决策模型调用失败,使用默认策略',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 主协调函数
|
||||||
|
*/
|
||||||
|
export async function orchestrateDataSources(
|
||||||
|
userMessage: string,
|
||||||
|
context?: VideoContext,
|
||||||
|
config?: {
|
||||||
|
enableWebSearch: boolean;
|
||||||
|
webSearchProvider?: 'tavily' | 'serper' | 'serpapi';
|
||||||
|
tavilyApiKey?: string;
|
||||||
|
serperApiKey?: string;
|
||||||
|
serpApiKey?: string;
|
||||||
|
// 决策模型配置
|
||||||
|
enableDecisionModel?: boolean;
|
||||||
|
decisionProvider?: 'openai' | 'claude' | 'custom';
|
||||||
|
decisionApiKey?: string;
|
||||||
|
decisionBaseURL?: string;
|
||||||
|
decisionModel?: string;
|
||||||
|
}
|
||||||
|
): Promise<OrchestrationResult> {
|
||||||
|
let intent: IntentAnalysisResult;
|
||||||
|
|
||||||
|
// 1. 使用决策模型或传统意图分析
|
||||||
|
if (config?.enableDecisionModel && config.decisionProvider && config.decisionApiKey && config.decisionModel) {
|
||||||
|
console.log('🤖 使用决策模型分析...');
|
||||||
|
|
||||||
|
const decision = await callDecisionModel(userMessage, context, {
|
||||||
|
provider: config.decisionProvider,
|
||||||
|
apiKey: config.decisionApiKey,
|
||||||
|
baseURL: config.decisionBaseURL,
|
||||||
|
model: config.decisionModel,
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log('🎯 决策模型结果:', decision);
|
||||||
|
|
||||||
|
// 将决策结果转换为 IntentAnalysisResult 格式
|
||||||
|
intent = {
|
||||||
|
type: decision.needDouban ? 'detail' : 'general',
|
||||||
|
needWebSearch: decision.needWebSearch,
|
||||||
|
needDouban: decision.needDouban,
|
||||||
|
needTMDB: decision.needTMDB,
|
||||||
|
keywords: decision.webSearchQuery ? [decision.webSearchQuery] : [],
|
||||||
|
entities: [],
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
// 传统关键词匹配分析
|
||||||
|
intent = analyzeIntent(userMessage, context);
|
||||||
|
console.log('📊 意图分析结果:', intent);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. 并行获取所需的数据源
|
||||||
|
const dataPromises: Promise<any>[] = [];
|
||||||
|
|
||||||
|
let webSearchPromise: Promise<any> | null = null;
|
||||||
|
let doubanPromise: Promise<any> | null = null;
|
||||||
|
let tmdbPromise: Promise<any> | null = null;
|
||||||
|
|
||||||
|
// 联网搜索
|
||||||
|
if (
|
||||||
|
intent.needWebSearch &&
|
||||||
|
config?.enableWebSearch &&
|
||||||
|
config.webSearchProvider
|
||||||
|
) {
|
||||||
|
const provider = config.webSearchProvider;
|
||||||
|
const apiKey =
|
||||||
|
provider === 'tavily'
|
||||||
|
? config.tavilyApiKey
|
||||||
|
: provider === 'serper'
|
||||||
|
? config.serperApiKey
|
||||||
|
: config.serpApiKey;
|
||||||
|
|
||||||
|
if (apiKey) {
|
||||||
|
webSearchPromise = fetchWebSearch(userMessage, provider, apiKey);
|
||||||
|
dataPromises.push(webSearchPromise);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 豆瓣数据
|
||||||
|
if (intent.needDouban) {
|
||||||
|
if (context?.douban_id) {
|
||||||
|
doubanPromise = fetchDoubanData({ id: context.douban_id });
|
||||||
|
} else if (intent.type === 'recommendation') {
|
||||||
|
doubanPromise = fetchDoubanData({
|
||||||
|
kind: intent.mediaType || 'movie',
|
||||||
|
category: '热门',
|
||||||
|
type: intent.genre || '全部',
|
||||||
|
});
|
||||||
|
} else if (context?.title) {
|
||||||
|
doubanPromise = fetchDoubanData({
|
||||||
|
query: context.title,
|
||||||
|
kind: context.type,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (doubanPromise) {
|
||||||
|
dataPromises.push(doubanPromise);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TMDB数据
|
||||||
|
if (intent.needTMDB && context?.tmdb_id && context?.type) {
|
||||||
|
tmdbPromise = fetchTMDBData({
|
||||||
|
id: context.tmdb_id,
|
||||||
|
type: context.type,
|
||||||
|
});
|
||||||
|
dataPromises.push(tmdbPromise);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. 等待所有数据获取完成
|
||||||
|
const results = await Promise.allSettled(dataPromises);
|
||||||
|
|
||||||
|
let webSearchData = null;
|
||||||
|
let doubanData = null;
|
||||||
|
let tmdbData = null;
|
||||||
|
|
||||||
|
let resultIndex = 0;
|
||||||
|
if (webSearchPromise) {
|
||||||
|
const result = results[resultIndex++];
|
||||||
|
if (result.status === 'fulfilled') {
|
||||||
|
webSearchData = result.value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (doubanPromise) {
|
||||||
|
const result = results[resultIndex++];
|
||||||
|
if (result.status === 'fulfilled') {
|
||||||
|
doubanData = result.value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (tmdbPromise) {
|
||||||
|
const result = results[resultIndex++];
|
||||||
|
if (result.status === 'fulfilled') {
|
||||||
|
tmdbData = result.value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. 构建系统提示词
|
||||||
|
let systemPrompt = `你是 MoonTVPlus 的 AI 影视助手,专门帮助用户发现和了解影视内容。
|
||||||
|
|
||||||
|
## 你的能力
|
||||||
|
- 提供影视推荐(基于豆瓣热门榜单和TMDB数据)
|
||||||
|
- 回答影视相关问题(剧情、演员、评分等)
|
||||||
|
- 搜索最新影视资讯(如果启用了联网搜索)
|
||||||
|
|
||||||
|
## 回复要求
|
||||||
|
1. 语言风格:友好、专业、简洁
|
||||||
|
2. 信息来源:优先使用提供的数据,诚实告知数据不足
|
||||||
|
3. 推荐理由:说明为什么值得看,包括评分、类型、特色等
|
||||||
|
4. 格式清晰:使用分段、列表等让内容易读
|
||||||
|
|
||||||
|
`;
|
||||||
|
|
||||||
|
// 添加联网搜索结果
|
||||||
|
if (webSearchData && config?.webSearchProvider) {
|
||||||
|
const formattedSearch = formatSearchResults(
|
||||||
|
webSearchData,
|
||||||
|
config.webSearchProvider
|
||||||
|
);
|
||||||
|
if (formattedSearch) {
|
||||||
|
systemPrompt += `\n## 【联网搜索结果】(最新实时信息)\n${formattedSearch}\n`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 添加豆瓣数据
|
||||||
|
if (doubanData) {
|
||||||
|
systemPrompt += `\n## 【豆瓣数据】(权威中文评分和信息)\n`;
|
||||||
|
if (doubanData.list) {
|
||||||
|
// 列表数据
|
||||||
|
systemPrompt += `推荐列表(${doubanData.list.length}部):\n${JSON.stringify(
|
||||||
|
doubanData.list.slice(0, 10).map((item: any) => ({
|
||||||
|
title: item.title,
|
||||||
|
rating: item.rating,
|
||||||
|
year: item.year,
|
||||||
|
genres: item.genres,
|
||||||
|
directors: item.directors,
|
||||||
|
actors: item.actors,
|
||||||
|
})),
|
||||||
|
null,
|
||||||
|
2
|
||||||
|
)}\n`;
|
||||||
|
} else if (doubanData.items) {
|
||||||
|
// 搜索结果
|
||||||
|
systemPrompt += `搜索结果:\n${JSON.stringify(
|
||||||
|
doubanData.items.slice(0, 5),
|
||||||
|
null,
|
||||||
|
2
|
||||||
|
)}\n`;
|
||||||
|
} else {
|
||||||
|
// 详情数据
|
||||||
|
systemPrompt += JSON.stringify(
|
||||||
|
{
|
||||||
|
title: doubanData.title,
|
||||||
|
rating: doubanData.rating,
|
||||||
|
year: doubanData.year,
|
||||||
|
genres: doubanData.genres,
|
||||||
|
directors: doubanData.directors,
|
||||||
|
actors: doubanData.actors,
|
||||||
|
intro: doubanData.intro,
|
||||||
|
reviews: doubanData.reviews?.slice(0, 2),
|
||||||
|
},
|
||||||
|
null,
|
||||||
|
2
|
||||||
|
);
|
||||||
|
systemPrompt += '\n';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 添加TMDB数据
|
||||||
|
if (tmdbData) {
|
||||||
|
systemPrompt += `\n## 【TMDB数据】(国际数据和详细元信息)\n`;
|
||||||
|
systemPrompt += JSON.stringify(
|
||||||
|
{
|
||||||
|
title: tmdbData.title || tmdbData.name,
|
||||||
|
overview: tmdbData.overview,
|
||||||
|
vote_average: tmdbData.vote_average,
|
||||||
|
genres: tmdbData.genres,
|
||||||
|
keywords: tmdbData.keywords,
|
||||||
|
similar: tmdbData.similar?.slice(0, 5),
|
||||||
|
},
|
||||||
|
null,
|
||||||
|
2
|
||||||
|
);
|
||||||
|
systemPrompt += '\n';
|
||||||
|
}
|
||||||
|
|
||||||
|
// 添加当前视频上下文
|
||||||
|
if (context?.title) {
|
||||||
|
systemPrompt += `\n## 【当前视频上下文】\n`;
|
||||||
|
systemPrompt += `用户正在浏览: ${context.title}`;
|
||||||
|
if (context.year) systemPrompt += ` (${context.year})`;
|
||||||
|
if (context.currentEpisode) {
|
||||||
|
systemPrompt += `,当前第 ${context.currentEpisode} 集`;
|
||||||
|
}
|
||||||
|
systemPrompt += '\n';
|
||||||
|
}
|
||||||
|
|
||||||
|
systemPrompt += `\n## 数据来源优先级
|
||||||
|
1. 如果有联网搜索结果,优先使用其最新信息
|
||||||
|
2. 豆瓣数据提供中文评价和评分(更适合中文用户)
|
||||||
|
3. TMDB数据更国际化,提供关键词和相似推荐
|
||||||
|
4. 如果多个数据源有冲突,以联网搜索为准
|
||||||
|
5. 如果数据不足以回答问题,诚实告知用户
|
||||||
|
|
||||||
|
现在请回答用户的问题。`;
|
||||||
|
|
||||||
|
console.log('📝 生成的系统提示词长度:', systemPrompt.length);
|
||||||
|
|
||||||
|
return {
|
||||||
|
systemPrompt,
|
||||||
|
webSearchResults: webSearchData,
|
||||||
|
doubanData,
|
||||||
|
tmdbData,
|
||||||
|
};
|
||||||
|
}
|
||||||
+1
-1
@@ -85,7 +85,7 @@ const config: Config = {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
plugins: [require('@tailwindcss/forms')],
|
plugins: [require('@tailwindcss/forms'), require('@tailwindcss/typography')],
|
||||||
} satisfies Config;
|
} satisfies Config;
|
||||||
|
|
||||||
export default config;
|
export default config;
|
||||||
|
|||||||
Reference in New Issue
Block a user