fix(style): playground image incomplete

main
jialin 1 year ago
parent af789ee886
commit bd418b23dc

@ -33,6 +33,7 @@
"axios": "^1.7.2",
"classnames": "^2.5.1",
"clipboard": "^2.0.11",
"colorthief": "^2.6.0",
"crypto-js": "^4.2.0",
"dayjs": "^1.11.11",
"echarts": "^5.5.1",

@ -62,6 +62,9 @@ dependencies:
clipboard:
specifier: ^2.0.11
version: 2.0.11
colorthief:
specifier: ^2.6.0
version: 2.6.0
crypto-js:
specifier: ^4.2.0
version: 4.2.0
@ -3289,6 +3292,14 @@ packages:
tslib: 2.6.2
dev: false
/@emnapi/runtime@1.3.1:
resolution: {integrity: sha512-kEBmG8KyqtxJZv+ygbEim+KCGtIq1fC22Ms3S4ziXmYKm8uyoLX0MHONVKwp+9opg390VaKRNt4a7A9NwmpNhw==, tarball: https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.3.1.tgz}
requiresBuild: true
dependencies:
tslib: 2.6.2
dev: false
optional: true
/@emotion/babel-plugin@11.11.0:
resolution: {integrity: sha512-m4HEDZleaaCH+XgDDsPF15Ht6wTLsgDTeR3WYj9Q/k76JtWhrJjcP4+/XlG8LGT/Rol9qUfOIztXeA84ATpqPQ==}
dependencies:
@ -4288,6 +4299,186 @@ packages:
- supports-color
dev: false
/@img/sharp-darwin-arm64@0.33.5:
resolution: {integrity: sha512-UT4p+iz/2H4twwAoLCqfA9UH5pI6DggwKEGuaPy7nCVQ8ZsiY5PIcrRvD1DzuY3qYL07NtIQcWnBSY/heikIFQ==, tarball: https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.33.5.tgz}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [arm64]
os: [darwin]
requiresBuild: true
optionalDependencies:
'@img/sharp-libvips-darwin-arm64': 1.0.4
dev: false
optional: true
/@img/sharp-darwin-x64@0.33.5:
resolution: {integrity: sha512-fyHac4jIc1ANYGRDxtiqelIbdWkIuQaI84Mv45KvGRRxSAa7o7d1ZKAOBaYbnepLC1WqxfpimdeWfvqqSGwR2Q==, tarball: https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.33.5.tgz}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [x64]
os: [darwin]
requiresBuild: true
optionalDependencies:
'@img/sharp-libvips-darwin-x64': 1.0.4
dev: false
optional: true
/@img/sharp-libvips-darwin-arm64@1.0.4:
resolution: {integrity: sha512-XblONe153h0O2zuFfTAbQYAX2JhYmDHeWikp1LM9Hul9gVPjFY427k6dFEcOL72O01QxQsWi761svJ/ev9xEDg==, tarball: https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.0.4.tgz}
cpu: [arm64]
os: [darwin]
requiresBuild: true
dev: false
optional: true
/@img/sharp-libvips-darwin-x64@1.0.4:
resolution: {integrity: sha512-xnGR8YuZYfJGmWPvmlunFaWJsb9T/AO2ykoP3Fz/0X5XV2aoYBPkX6xqCQvUTKKiLddarLaxpzNe+b1hjeWHAQ==, tarball: https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.0.4.tgz}
cpu: [x64]
os: [darwin]
requiresBuild: true
dev: false
optional: true
/@img/sharp-libvips-linux-arm64@1.0.4:
resolution: {integrity: sha512-9B+taZ8DlyyqzZQnoeIvDVR/2F4EbMepXMc/NdVbkzsJbzkUjhXv/70GQJ7tdLA4YJgNP25zukcxpX2/SueNrA==, tarball: https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.0.4.tgz}
cpu: [arm64]
os: [linux]
requiresBuild: true
dev: false
optional: true
/@img/sharp-libvips-linux-arm@1.0.5:
resolution: {integrity: sha512-gvcC4ACAOPRNATg/ov8/MnbxFDJqf/pDePbBnuBDcjsI8PssmjoKMAz4LtLaVi+OnSb5FK/yIOamqDwGmXW32g==, tarball: https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.0.5.tgz}
cpu: [arm]
os: [linux]
requiresBuild: true
dev: false
optional: true
/@img/sharp-libvips-linux-s390x@1.0.4:
resolution: {integrity: sha512-u7Wz6ntiSSgGSGcjZ55im6uvTrOxSIS8/dgoVMoiGE9I6JAfU50yH5BoDlYA1tcuGS7g/QNtetJnxA6QEsCVTA==, tarball: https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.0.4.tgz}
cpu: [s390x]
os: [linux]
requiresBuild: true
dev: false
optional: true
/@img/sharp-libvips-linux-x64@1.0.4:
resolution: {integrity: sha512-MmWmQ3iPFZr0Iev+BAgVMb3ZyC4KeFc3jFxnNbEPas60e1cIfevbtuyf9nDGIzOaW9PdnDciJm+wFFaTlj5xYw==, tarball: https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.0.4.tgz}
cpu: [x64]
os: [linux]
requiresBuild: true
dev: false
optional: true
/@img/sharp-libvips-linuxmusl-arm64@1.0.4:
resolution: {integrity: sha512-9Ti+BbTYDcsbp4wfYib8Ctm1ilkugkA/uscUn6UXK1ldpC1JjiXbLfFZtRlBhjPZ5o1NCLiDbg8fhUPKStHoTA==, tarball: https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.0.4.tgz}
cpu: [arm64]
os: [linux]
requiresBuild: true
dev: false
optional: true
/@img/sharp-libvips-linuxmusl-x64@1.0.4:
resolution: {integrity: sha512-viYN1KX9m+/hGkJtvYYp+CCLgnJXwiQB39damAO7WMdKWlIhmYTfHjwSbQeUK/20vY154mwezd9HflVFM1wVSw==, tarball: https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.0.4.tgz}
cpu: [x64]
os: [linux]
requiresBuild: true
dev: false
optional: true
/@img/sharp-linux-arm64@0.33.5:
resolution: {integrity: sha512-JMVv+AMRyGOHtO1RFBiJy/MBsgz0x4AWrT6QoEVVTyh1E39TrCUpTRI7mx9VksGX4awWASxqCYLCV4wBZHAYxA==, tarball: https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.33.5.tgz}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [arm64]
os: [linux]
requiresBuild: true
optionalDependencies:
'@img/sharp-libvips-linux-arm64': 1.0.4
dev: false
optional: true
/@img/sharp-linux-arm@0.33.5:
resolution: {integrity: sha512-JTS1eldqZbJxjvKaAkxhZmBqPRGmxgu+qFKSInv8moZ2AmT5Yib3EQ1c6gp493HvrvV8QgdOXdyaIBrhvFhBMQ==, tarball: https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.33.5.tgz}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [arm]
os: [linux]
requiresBuild: true
optionalDependencies:
'@img/sharp-libvips-linux-arm': 1.0.5
dev: false
optional: true
/@img/sharp-linux-s390x@0.33.5:
resolution: {integrity: sha512-y/5PCd+mP4CA/sPDKl2961b+C9d+vPAveS33s6Z3zfASk2j5upL6fXVPZi7ztePZ5CuH+1kW8JtvxgbuXHRa4Q==, tarball: https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.33.5.tgz}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [s390x]
os: [linux]
requiresBuild: true
optionalDependencies:
'@img/sharp-libvips-linux-s390x': 1.0.4
dev: false
optional: true
/@img/sharp-linux-x64@0.33.5:
resolution: {integrity: sha512-opC+Ok5pRNAzuvq1AG0ar+1owsu842/Ab+4qvU879ippJBHvyY5n2mxF1izXqkPYlGuP/M556uh53jRLJmzTWA==, tarball: https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.33.5.tgz}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [x64]
os: [linux]
requiresBuild: true
optionalDependencies:
'@img/sharp-libvips-linux-x64': 1.0.4
dev: false
optional: true
/@img/sharp-linuxmusl-arm64@0.33.5:
resolution: {integrity: sha512-XrHMZwGQGvJg2V/oRSUfSAfjfPxO+4DkiRh6p2AFjLQztWUuY/o8Mq0eMQVIY7HJ1CDQUJlxGGZRw1a5bqmd1g==, tarball: https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.33.5.tgz}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [arm64]
os: [linux]
requiresBuild: true
optionalDependencies:
'@img/sharp-libvips-linuxmusl-arm64': 1.0.4
dev: false
optional: true
/@img/sharp-linuxmusl-x64@0.33.5:
resolution: {integrity: sha512-WT+d/cgqKkkKySYmqoZ8y3pxx7lx9vVejxW/W4DOFMYVSkErR+w7mf2u8m/y4+xHe7yY9DAXQMWQhpnMuFfScw==, tarball: https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.33.5.tgz}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [x64]
os: [linux]
requiresBuild: true
optionalDependencies:
'@img/sharp-libvips-linuxmusl-x64': 1.0.4
dev: false
optional: true
/@img/sharp-wasm32@0.33.5:
resolution: {integrity: sha512-ykUW4LVGaMcU9lu9thv85CbRMAwfeadCJHRsg2GmeRa/cJxsVY9Rbd57JcMxBkKHag5U/x7TSBpScF4U8ElVzg==, tarball: https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.33.5.tgz}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [wasm32]
requiresBuild: true
dependencies:
'@emnapi/runtime': 1.3.1
dev: false
optional: true
/@img/sharp-win32-ia32@0.33.5:
resolution: {integrity: sha512-T36PblLaTwuVJ/zw/LaH0PdZkRz5rd3SmMHX8GSmR7vtNSP5Z6bQkExdSK7xGWyxLw4sUknBuugTelgw2faBbQ==, tarball: https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.33.5.tgz}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [ia32]
os: [win32]
requiresBuild: true
dev: false
optional: true
/@img/sharp-win32-x64@0.33.5:
resolution: {integrity: sha512-MpY/o8/8kj+EcnxwvrP4aTJSWw/aZ7JIGR4aBeZkZw5B7/Jn+tY9/VNwtcoGmdT7GfggGIU4kygOMSbYnOrAbg==, tarball: https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.33.5.tgz}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [x64]
os: [win32]
requiresBuild: true
dev: false
optional: true
/@isaacs/cliui@8.0.2:
resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==, tarball: https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz}
engines: {node: '>=12'}
@ -4445,6 +4636,10 @@ packages:
react-is: 16.13.1
dev: false
/@lokesh.dhakar/quantize@1.4.0:
resolution: {integrity: sha512-+//cqVWKis//t0YH62EDtwaFSPG/CDtYNg4CZmzNmG2d5W17Iu3fuDAdpQXCDHUDrrU9q0veze4A7tPZXlR/mg==, tarball: https://registry.npmjs.org/@lokesh.dhakar/quantize/-/quantize-1.4.0.tgz}
dev: false
/@mapbox/node-pre-gyp@1.0.11:
resolution: {integrity: sha512-Yhlar6v9WQgUp/He7BdgzOz8lqMQ8sU+jkCq7Wx8Myc5YFJLbEe7lgui/V7G1qB1DJykHSGwreceSaD60Y0PUQ==, tarball: https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.11.tgz}
hasBin: true
@ -4872,7 +5067,7 @@ packages:
dependencies:
'@babel/core': 7.24.5
postcss: 7.0.39
postcss-syntax: 0.36.2(postcss@7.0.39)
postcss-syntax: 0.36.2(postcss@8.4.38)
transitivePeerDependencies:
- supports-color
dev: false
@ -4913,7 +5108,7 @@ packages:
postcss-syntax: '>=0.36.2'
dependencies:
postcss: 7.0.39
postcss-syntax: 0.36.2(postcss@7.0.39)
postcss-syntax: 0.36.2(postcss@8.4.38)
remark: 13.0.0
unist-util-find-all-after: 3.0.2
transitivePeerDependencies:
@ -5107,6 +5302,10 @@ packages:
use-sync-external-store: 1.2.2(react@18.2.0)
dev: false
/@tokenizer/token@0.3.0:
resolution: {integrity: sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A==, tarball: https://registry.npmjs.org/@tokenizer/token/-/token-0.3.0.tgz}
dev: false
/@trysound/sax@0.2.0:
resolution: {integrity: sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==, tarball: https://registry.npmjs.org/@trysound/sax/-/sax-0.2.0.tgz}
engines: {node: '>=10.13.0'}
@ -5266,6 +5465,10 @@ packages:
resolution: {integrity: sha512-hov8bUuiLiyFPGyFPE1lwWhmzYbirOXQNNo40+y3zow8aFVTeyn3VWL0VFFfdNddA8S4Vf0Tc062rzyNr7Paag==}
dev: false
/@types/ndarray@1.0.14:
resolution: {integrity: sha512-oANmFZMnFQvb219SSBIhI1Ih/r4CvHDOzkWyJS/XRqkMrGH5/kaPSA1hQhdIBzouaE+5KpE/f5ylI9cujmckQg==, tarball: https://registry.npmjs.org/@types/ndarray/-/ndarray-1.0.14.tgz}
dev: false
/@types/node@20.12.11:
resolution: {integrity: sha512-vDg9PZ/zi+Nqp6boSOT7plNuthRugEKixDv5sFTIpkE89MmNtEArAShI4mxuX2+UrLEe9pxC1vm2cjm9YlWbJw==}
dependencies:
@ -8500,6 +8703,13 @@ packages:
/color-name@1.1.4:
resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==}
/color-string@1.9.1:
resolution: {integrity: sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==, tarball: https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz}
dependencies:
color-name: 1.1.4
simple-swizzle: 0.2.2
dev: false
/color-support@1.1.3:
resolution: {integrity: sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==, tarball: https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz}
hasBin: true
@ -8507,6 +8717,14 @@ packages:
dev: false
optional: true
/color@4.2.3:
resolution: {integrity: sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==, tarball: https://registry.npmjs.org/color/-/color-4.2.3.tgz}
engines: {node: '>=12.5.0'}
dependencies:
color-convert: 2.0.1
color-string: 1.9.1
dev: false
/colord@2.9.3:
resolution: {integrity: sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw==, tarball: https://registry.npmjs.org/colord/-/colord-2.9.3.tgz}
dev: false
@ -8515,6 +8733,15 @@ packages:
resolution: {integrity: sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==}
dev: true
/colorthief@2.6.0:
resolution: {integrity: sha512-yL3B7laeOr4kH9XasFF5rl+9Taz+Pmt/CRbaTI6XepZFyQvk4K/abaGKIAsngVpxKkgFeoJ2IwdRpS228icrig==, tarball: https://registry.npmjs.org/colorthief/-/colorthief-2.6.0.tgz}
dependencies:
'@lokesh.dhakar/quantize': 1.4.0
file-type: 16.5.4
ndarray-pixels: 4.1.0
sharp: 0.33.5
dev: false
/combined-stream@1.0.8:
resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==, tarball: https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz}
engines: {node: '>= 0.8'}
@ -8959,6 +9186,12 @@ packages:
resolution: {integrity: sha512-qv8s+G47V6Hq+g2kRE5th+ASzzrL7b6l+tap1DHKK25ZQJv3yIFhH96XaQ7NGL+zRW3t/RDbweJf/dJDe5Z5KA==, tarball: https://registry.npmjs.org/current-script-polyfill/-/current-script-polyfill-1.0.0.tgz}
dev: false
/cwise-compiler@1.1.3:
resolution: {integrity: sha512-WXlK/m+Di8DMMcCjcWr4i+XzcQra9eCdXIJrgh4TUgh0pIS/yJduLxS9JgefsHJ/YVLdgPtXm9r62W92MvanEQ==, tarball: https://registry.npmjs.org/cwise-compiler/-/cwise-compiler-1.1.3.tgz}
dependencies:
uniq: 1.0.1
dev: false
/d@1.0.2:
resolution: {integrity: sha512-MOqHvMWF9/9MX6nza0KgvFH4HpMU0EF5uUDXqX/BtxtU8NfB0QzRtJ8Oe/6SuS4kbhyzVJwjd97EA4PKrzJ8bw==}
engines: {node: '>=0.12'}
@ -9201,7 +9434,6 @@ packages:
engines: {node: '>=8'}
requiresBuild: true
dev: false
optional: true
/detect-newline@3.1.0:
resolution: {integrity: sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==, tarball: https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz}
@ -10694,6 +10926,15 @@ packages:
webpack: 5.95.0
dev: true
/file-type@16.5.4:
resolution: {integrity: sha512-/yFHK0aGjFEgDJjEKP0pWCplsPFPhwyfwevf/pVxiN0tmE4L9LmwWxWukdJSHdoCli4VgQLehjJtwQBnqmsKcw==, tarball: https://registry.npmjs.org/file-type/-/file-type-16.5.4.tgz}
engines: {node: '>=10'}
dependencies:
readable-web-to-node-stream: 3.0.2
strtok3: 6.3.0
token-types: 4.2.1
dev: false
/fill-range@7.0.1:
resolution: {integrity: sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==}
engines: {node: '>=8'}
@ -11622,6 +11863,10 @@ packages:
engines: {node: '>=8'}
dev: false
/iota-array@1.0.0:
resolution: {integrity: sha512-pZ2xT+LOHckCatGQ3DcG/a+QuEqvoxqkiL7tvE8nn3uuu+f6i1TtpB5/FtWFbxUuVr5PZCx8KskuGatbJDXOWA==, tarball: https://registry.npmjs.org/iota-array/-/iota-array-1.0.0.tgz}
dev: false
/ipaddr.js@1.9.1:
resolution: {integrity: sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==}
engines: {node: '>= 0.10'}
@ -11667,6 +11912,10 @@ packages:
resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==}
dev: false
/is-arrayish@0.3.2:
resolution: {integrity: sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==, tarball: https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz}
dev: false
/is-arrow-function@2.0.3:
resolution: {integrity: sha512-iDStzcT1FJMzx+TjCOK//uDugSe/Mif/8a+T0htydQ3qkJGvSweTZpVYz4hpJH0baloSPiAFQdA8WslAgJphvQ==, tarball: https://registry.npmjs.org/is-arrow-function/-/is-arrow-function-2.0.3.tgz}
engines: {node: '>= 0.4'}
@ -11702,6 +11951,10 @@ packages:
has-tostringtag: 1.0.2
dev: false
/is-buffer@1.1.6:
resolution: {integrity: sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==, tarball: https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz}
dev: false
/is-buffer@2.0.5:
resolution: {integrity: sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ==, tarball: https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.5.tgz}
engines: {node: '>=4'}
@ -13055,6 +13308,28 @@ packages:
/natural-compare@1.4.0:
resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==}
/ndarray-ops@1.2.2:
resolution: {integrity: sha512-BppWAFRjMYF7N/r6Ie51q6D4fs0iiGmeXIACKY66fLpnwIui3Wc3CXiD/30mgLbDjPpSLrsqcp3Z62+IcHZsDw==, tarball: https://registry.npmjs.org/ndarray-ops/-/ndarray-ops-1.2.2.tgz}
dependencies:
cwise-compiler: 1.1.3
dev: false
/ndarray-pixels@4.1.0:
resolution: {integrity: sha512-xKPI4zXJ2pkUcVX24zIN1AWqqPWvRWWhRuO6PlY4EdB2VNRauNwA6rDdsAQG/ldQp0sU7nTXgPR/io1duy3Zyg==, tarball: https://registry.npmjs.org/ndarray-pixels/-/ndarray-pixels-4.1.0.tgz}
dependencies:
'@types/ndarray': 1.0.14
ndarray: 1.0.19
ndarray-ops: 1.2.2
sharp: 0.33.5
dev: false
/ndarray@1.0.19:
resolution: {integrity: sha512-B4JHA4vdyZU30ELBw3g7/p9bZupyew5a7tX1Y/gGeF2hafrPaQZhgrGQfsvgfYbgdFZjYwuEcnaobeM/WMW+HQ==, tarball: https://registry.npmjs.org/ndarray/-/ndarray-1.0.19.tgz}
dependencies:
iota-array: 1.0.0
is-buffer: 1.1.6
dev: false
/needle@3.3.1:
resolution: {integrity: sha512-6k0YULvhpw+RoLNiQCRKOl09Rv1dPLr8hHnVjHqdolKwDrdNyk+Hmrthi4lIGPPz3r39dLx0hsF5s40sZ3Us4Q==, tarball: https://registry.npmjs.org/needle/-/needle-3.3.1.tgz}
engines: {node: '>= 4.4.x'}
@ -13733,6 +14008,11 @@ packages:
- supports-color
dev: false
/peek-readable@4.1.0:
resolution: {integrity: sha512-ZI3LnwUv5nOGbQzD9c2iDG6toheuXSZP5esSHBjopsXH4dg19soufvpUGA3uohi5anFtGb2lhAVdHzH6R/Evvg==, tarball: https://registry.npmjs.org/peek-readable/-/peek-readable-4.1.0.tgz}
engines: {node: '>=8'}
dev: false
/performance-now@2.1.0:
resolution: {integrity: sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==, tarball: https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz}
dev: false
@ -14143,7 +14423,7 @@ packages:
dependencies:
htmlparser2: 3.10.1
postcss: 7.0.39
postcss-syntax: 0.36.2(postcss@7.0.39)
postcss-syntax: 0.36.2(postcss@8.4.38)
dev: false
/postcss-image-set-function@4.0.7(postcss@8.4.38):
@ -14617,30 +14897,6 @@ packages:
lodash: 4.17.21
postcss: 8.4.38
/postcss-syntax@0.36.2(postcss@7.0.39):
resolution: {integrity: sha512-nBRg/i7E3SOHWxF3PpF5WnJM/jQ1YpY9000OaVXlAQj6Zp/kIqJxEDWIZ67tAd7NLuk7zqN4yqe9nc0oNAOs1w==}
peerDependencies:
postcss: '>=5.0.0'
postcss-html: '*'
postcss-jsx: '*'
postcss-less: '*'
postcss-markdown: '*'
postcss-scss: '*'
peerDependenciesMeta:
postcss-html:
optional: true
postcss-jsx:
optional: true
postcss-less:
optional: true
postcss-markdown:
optional: true
postcss-scss:
optional: true
dependencies:
postcss: 7.0.39
dev: false
/postcss-syntax@0.36.2(postcss@8.4.38):
resolution: {integrity: sha512-nBRg/i7E3SOHWxF3PpF5WnJM/jQ1YpY9000OaVXlAQj6Zp/kIqJxEDWIZ67tAd7NLuk7zqN4yqe9nc0oNAOs1w==}
peerDependencies:
@ -16561,6 +16817,13 @@ packages:
util-deprecate: 1.0.2
dev: false
/readable-web-to-node-stream@3.0.2:
resolution: {integrity: sha512-ePeK6cc1EcKLEhJFt/AebMCLL+GgSKhuygrZ/GLaKZYEecIgIECf4UaUuaByiGtzckwR4ain9VzUh95T1exYGw==, tarball: https://registry.npmjs.org/readable-web-to-node-stream/-/readable-web-to-node-stream-3.0.2.tgz}
engines: {node: '>=8'}
dependencies:
readable-stream: 3.6.2
dev: false
/readdirp@3.6.0:
resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==}
engines: {node: '>=8.10.0'}
@ -17276,6 +17539,36 @@ packages:
resolution: {integrity: sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ==}
dev: false
/sharp@0.33.5:
resolution: {integrity: sha512-haPVm1EkS9pgvHrQ/F3Xy+hgcuMV0Wm9vfIBSiwZ05k+xgb0PkBQpGsAA/oWdDobNaZTH5ppvHtzCFbnSEwHVw==, tarball: https://registry.npmjs.org/sharp/-/sharp-0.33.5.tgz}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
requiresBuild: true
dependencies:
color: 4.2.3
detect-libc: 2.0.3
semver: 7.6.3
optionalDependencies:
'@img/sharp-darwin-arm64': 0.33.5
'@img/sharp-darwin-x64': 0.33.5
'@img/sharp-libvips-darwin-arm64': 1.0.4
'@img/sharp-libvips-darwin-x64': 1.0.4
'@img/sharp-libvips-linux-arm': 1.0.5
'@img/sharp-libvips-linux-arm64': 1.0.4
'@img/sharp-libvips-linux-s390x': 1.0.4
'@img/sharp-libvips-linux-x64': 1.0.4
'@img/sharp-libvips-linuxmusl-arm64': 1.0.4
'@img/sharp-libvips-linuxmusl-x64': 1.0.4
'@img/sharp-linux-arm': 0.33.5
'@img/sharp-linux-arm64': 0.33.5
'@img/sharp-linux-s390x': 0.33.5
'@img/sharp-linux-x64': 0.33.5
'@img/sharp-linuxmusl-arm64': 0.33.5
'@img/sharp-linuxmusl-x64': 0.33.5
'@img/sharp-wasm32': 0.33.5
'@img/sharp-win32-ia32': 0.33.5
'@img/sharp-win32-x64': 0.33.5
dev: false
/shebang-command@2.0.0:
resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==}
engines: {node: '>=8'}
@ -17357,6 +17650,12 @@ packages:
dev: false
optional: true
/simple-swizzle@0.2.2:
resolution: {integrity: sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==, tarball: https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz}
dependencies:
is-arrayish: 0.3.2
dev: false
/simplebar-core@1.2.6:
resolution: {integrity: sha512-H5NYU+O+uvqOH5VXw3+lgoc1vTI6jL8LOZJsw4xgRpV7uIPjRpmLPdz0TrouxwKHBhpVLzVIlyKhaRLelIThMw==, tarball: https://registry.npmjs.org/simplebar-core/-/simplebar-core-1.2.6.tgz}
dependencies:
@ -17755,6 +18054,14 @@ packages:
resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==}
engines: {node: '>=8'}
/strtok3@6.3.0:
resolution: {integrity: sha512-fZtbhtvI9I48xDSywd/somNqgUHl2L2cstmXCCif0itOf96jeW18MBSyrLuNicYQVkvpOxkZtkzujiTJ9LW5Jw==, tarball: https://registry.npmjs.org/strtok3/-/strtok3-6.3.0.tgz}
engines: {node: '>=10'}
dependencies:
'@tokenizer/token': 0.3.0
peek-readable: 4.1.0
dev: false
/style-search@0.1.0:
resolution: {integrity: sha512-Dj1Okke1C3uKKwQcetra4jSuk0DqbzbYtXipzFlFMZtowbF1x7BKJwB9AayVMyFARvU8EDrZdcax4At/452cAg==}
dev: false
@ -17897,7 +18204,7 @@ packages:
postcss-sass: 0.4.4
postcss-scss: 2.1.1
postcss-selector-parser: 6.0.16
postcss-syntax: 0.36.2(postcss@7.0.39)
postcss-syntax: 0.36.2(postcss@8.4.38)
postcss-value-parser: 4.2.0
resolve-from: 5.0.0
slash: 3.0.0
@ -18281,6 +18588,14 @@ packages:
engines: {node: '>=0.6'}
dev: false
/token-types@4.2.1:
resolution: {integrity: sha512-6udB24Q737UD/SDsKAHI9FCRP7Bqc9D/MQUV02ORQg5iskjtLJlZJNdN4kKtcdtwCeWIwIHDGaUsTsCCAa8sFQ==, tarball: https://registry.npmjs.org/token-types/-/token-types-4.2.1.tgz}
engines: {node: '>=10'}
dependencies:
'@tokenizer/token': 0.3.0
ieee754: 1.2.1
dev: false
/totalist@3.0.1:
resolution: {integrity: sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==, tarball: https://registry.npmjs.org/totalist/-/totalist-3.0.1.tgz}
engines: {node: '>=6'}
@ -18657,6 +18972,10 @@ packages:
vfile: 4.2.1
dev: false
/uniq@1.0.1:
resolution: {integrity: sha512-Gw+zz50YNKPDKXs+9d+aKAjVwpjNwqzvNpLigIruT4HA9lMZNdMqs9x07kKHB/L9WRzqp4+DlTU5s4wG2esdoA==, tarball: https://registry.npmjs.org/uniq/-/uniq-1.0.1.tgz}
dev: false
/unist-util-find-all-after@3.0.2:
resolution: {integrity: sha512-xaTC/AGZ0rIM2gM28YVRAFPIZpzbpDtU3dRmp7EXlNVA8ziQc4hY3H7BHXM1J49nEmiqc3svnqMReW+PGqbZKQ==, tarball: https://registry.npmjs.org/unist-util-find-all-after/-/unist-util-find-all-after-3.0.2.tgz}
dependencies:

@ -49,5 +49,11 @@
.speaker {
margin-left: 10px;
position: relative;
.volume-slider {
position: absolute;
bottom: 30px;
}
}
}

@ -1,12 +1,14 @@
import { formatTime } from '@/utils/index';
import { PauseCircleFilled, PlayCircleFilled } from '@ant-design/icons';
import { Button, Slider } from 'antd';
import { Button, Slider, Tooltip } from 'antd';
import { round } from 'lodash';
import React, {
forwardRef,
useCallback,
useEffect,
useImperativeHandle
} from 'react';
import CheckButtons from '../check-buttons';
import IconFont from '../icon-font';
import './index.less';
@ -21,6 +23,13 @@ interface AudioPlayerProps {
duration?: number;
}
const speedOptions = [
{ label: '1x', value: 1 },
{ label: '2x', value: 2 },
{ label: '3x', value: 3 },
{ label: '4x', value: 4 }
];
const AudioPlayer: React.FC<AudioPlayerProps> = forwardRef((props, ref) => {
const { autoplay = false, speed: defaultSpeed = 1 } = props;
const audioRef = React.useRef<HTMLAudioElement>(null);
@ -45,6 +54,14 @@ const AudioPlayer: React.FC<AudioPlayerProps> = forwardRef((props, ref) => {
audioRef.current?.pause();
}
}));
const handleShowVolume = useCallback(() => {
setSpeakerOn(!speakerOn);
}, [speakerOn]);
const handleSeepdChange = useCallback((value: number | string) => {
setSpeed(value as number);
audioRef.current!.playbackRate = value as number;
}, []);
const handleAudioOnPlay = useCallback(() => {
timer.current = setInterval(() => {
@ -78,16 +95,22 @@ const AudioPlayer: React.FC<AudioPlayerProps> = forwardRef((props, ref) => {
setPlayOn(!playOn);
}, [playOn]);
const handleFormatVolume = (val: number) => {
return `${round(val * 100)}%`;
};
const handleVolumeChange = useCallback((value: number) => {
audioRef.current!.volume = round(value, 2);
setVolume(round(value, 2));
}, []);
const initPlayerConfig = useCallback(() => {
// set volume
audioRef.current!.volume = volume;
// set playback rate
audioRef.current!.playbackRate = speed;
}, []);
const handleLoadedMetadata = useCallback(
(data: any) => {
console.log('loadmetadata++++++++');
const duration = Math.ceil(audioRef.current?.duration || 0);
setAudioState({
currentTime: 0,
@ -158,7 +181,23 @@ const AudioPlayer: React.FC<AudioPlayerProps> = forwardRef((props, ref) => {
onChange={handleCurrentChange}
/>
</div>
<span>{props.speed ? `${props.speed}x` : '1x'}</span>
<Tooltip
overlayInnerStyle={{
backgroundColor: 'var(--color-white-1)'
}}
arrow={false}
title={
<CheckButtons
options={speedOptions}
onChange={handleSeepdChange}
size="small"
></CheckButtons>
}
>
<span style={{ cursor: 'pointer' }}>
{speed ? `${speed}x` : '1x'}
</span>
</Tooltip>
</div>
<span className="time">{formatTime(audioState.duration)}</span>
</div>
@ -173,6 +212,19 @@ const AudioPlayer: React.FC<AudioPlayerProps> = forwardRef((props, ref) => {
></IconFont>
}
></Button>
{speakerOn && (
<Slider
tooltip={{ formatter: handleFormatVolume }}
style={{ height: '100px' }}
className="volume-slider"
min={0}
max={1}
step={0.01}
value={volume}
vertical
onChange={handleVolumeChange}
/>
)}
</span>
</div>
<audio

@ -0,0 +1,43 @@
import { Button } from 'antd';
import React from 'react';
interface CheckButtonsProps {
options: Global.BaseOption<string | number>[];
onChange: (value: string | number) => void;
cancelable?: boolean;
size?: 'small' | 'middle' | 'large';
type?: 'text' | 'primary' | 'default' | 'dashed' | 'link' | undefined;
}
const CheckButtons: React.FC<CheckButtonsProps> = (props) => {
const [type, setType] = React.useState(props.type || 'text');
const [active, setActive] = React.useState<string | number | null>(null);
const handleChange = (value: string | number) => {
props.onChange(value);
if (props.cancelable && active === value) {
setActive(null);
} else {
setActive(value);
}
};
return (
<div className="flex-center gap-6">
{props.options?.map?.((option, index) => {
return (
<Button
size={props.size}
key={option.value}
onClick={() => handleChange(option.value)}
variant="filled"
color={active === option.value ? 'default' : undefined}
type={type}
>
{option.label}
</Button>
);
})}
</div>
);
};
export default React.memo(CheckButtons);

@ -7,7 +7,6 @@ interface SpeechContentProps {
}
const SpeechContent: React.FC<SpeechContentProps> = (props) => {
console.log('SpeechContent', props);
return (
<>
{props.dataList.map((item) => (

@ -1,11 +1,29 @@
import IconFont from '@/components/icon-font';
import { DownloadOutlined, PlayCircleOutlined } from '@ant-design/icons';
import {
DownloadOutlined,
PauseCircleOutlined,
PlayCircleOutlined
} from '@ant-design/icons';
import { useIntl } from '@umijs/max';
import { Button, Tooltip } from 'antd';
import dayjs from 'dayjs';
import React, { useRef, useState } from 'react';
import AudioPlayer from './audio-player';
import './styles/index.less';
// const audioUrl = require('./ih.mp4');
const audioFormat = {
'audio/mpeg': 'mp3',
'audio/wav': 'wav',
'audio/ogg': 'ogg',
'audio/webm': 'webm',
'audio/aac': 'aac',
'audio/x-flac': 'flac',
'audio/pcm': 'pcm',
'audio/flac': 'flac',
'audio/x-wav': 'wav',
'audio/L16': 'pcm',
'audio/opus': 'opus'
};
interface SpeechContentProps {
prompt: string;
@ -16,12 +34,19 @@ interface SpeechContentProps {
audioUrl: string;
}
const SpeechItem: React.FC<SpeechContentProps> = (props) => {
console.log('porps=======', props);
const intl = useIntl();
const [collapsed, setCollapsed] = useState(false);
const [isPlay, setIsPlay] = useState(false);
const ref = useRef<HTMLAudioElement>(null);
const handlePlay = () => {
if (isPlay) {
ref.current?.pause();
setIsPlay(false);
return;
}
ref.current?.play();
setIsPlay(true);
};
const handleCollapse = () => {
@ -30,7 +55,7 @@ const SpeechItem: React.FC<SpeechContentProps> = (props) => {
const onDownload = () => {
const url = props.audioUrl || '';
const filename = Date.now() + '';
const filename = `audio-${dayjs().format('YYYYMMDDHHmmss')}.${props.format}`;
const link = document.createElement('a');
link.href = url;
@ -45,7 +70,6 @@ const SpeechItem: React.FC<SpeechContentProps> = (props) => {
<div className="speech-item">
<div className="voice">
<IconFont type="icon-user_voice" className="font-size-16" />
{/* <span className="text">{props.voice}</span> */}
</div>
<div className="wrapper">
<AudioPlayer
@ -62,15 +86,25 @@ const SpeechItem: React.FC<SpeechContentProps> = (props) => {
<span className="item">{props.speed}x</span>
</span>
<div className="actions">
<Tooltip title="Play">
<Tooltip
title={
isPlay
? intl.formatMessage({ id: 'playground.audio.button.stop' })
: intl.formatMessage({ id: 'playground.audio.button.play' })
}
>
<Button
onClick={handlePlay}
icon={<PlayCircleOutlined />}
icon={isPlay ? <PauseCircleOutlined /> : <PlayCircleOutlined />}
type="text"
size="small"
></Button>
</Tooltip>
<Tooltip title="Download">
<Tooltip
title={intl.formatMessage({
id: 'playground.audio.button.download'
})}
>
<Button
onClick={onDownload}
icon={<DownloadOutlined />}
@ -78,21 +112,8 @@ const SpeechItem: React.FC<SpeechContentProps> = (props) => {
size="small"
></Button>
</Tooltip>
{/* <Tooltip title="Show Prompt">
<Button
icon={<FileTextOutlined />}
type="text"
size="small"
onClick={handleCollapse}
></Button>
</Tooltip> */}
</div>
</div>
{/* {collapsed && (
<div className="prompt-box">
<div className="prompt">{props.prompt}</div>
</div>
)} */}
</div>
);
};

@ -13,12 +13,11 @@ interface UploadAudioProps {
const UploadAudio: React.FC<UploadAudioProps> = (props) => {
const intl = useIntl();
const beforeUpload = (file: any) => {
return true;
return false;
};
const handleOnChange = React.useCallback(
(data: { file: any; fileList: any }) => {
console.log('handleOnChange', data);
props.onChange?.(data);
},
[]

@ -88,7 +88,10 @@ export default {
'playground.audio.generating.tips': 'Generated text will appear here.',
'playground.audio.uploadfile.tips':
'Please upload an audio file, supported formats: {formats}',
'playground.input.multiplePaste': 'Multi-line paste',
'playground.input.multiplePaste': 'Batch Input Mode',
'playground.input.multiplePaste.tips':
'When enabled, pasted multi-line text will be automatically split by newline into separate entries in the form.',
'playground.audio.button.generate': 'Generate Text Content',
'playground.multiple.on': 'Enable',
'playground.multiple.off': 'Disable',
'playground.image.params.sampler': 'Sampler',
@ -96,12 +99,15 @@ export default {
'playground.image.params.seed': 'Seed',
'playground.image.params.negativePrompt': 'Negative Prompt',
'playground.image.params.cfgScale': 'Scale Factor',
'playground.image.params.custom': 'Custom',
'playground.image.params.custom.tips': 'Parameter definition',
'playground.image.params.custom': 'Advanced',
'playground.image.params.custom.tips': 'API Style',
'playground.image.params.openai': 'OpenAI Compatible',
'playground.embedding.handler.tips': 'Resize Height',
'playground.embedding.pcatips1':
'PCA is used to reduce the dimensionality of document vectors, projecting new data into PCA space.',
'PCA (Principal Component Analysis) is used to reduce the dimensionality of embedding vectors, making them easier to visualize.',
'playground.embedding.pcatips2':
'In the chart, the distance between points represents the similarity between documents.'
'In the chart, the distance between points indicates the similarity between the corresponding documents. Closer points mean higher similarity.',
'playground.audio.button.play': 'Play',
'playground.audio.button.download': 'Download',
'playground.audio.button.stop': 'Stop'
};

@ -86,20 +86,25 @@ export default {
'playground.audio.generating.tips': '生成的文本将出现在这里',
'playground.audio.uploadfile.tips': '请上传音频文件,支持格式:{formats}',
'playground.audio.button.generate': '生成文本',
'playground.input.multiplePaste': '多行粘贴',
'playground.input.multiplePaste': '批量输入',
'playground.input.multiplePaste.tips':
'启用后,粘贴的多行文本将自动按换行符分割为表单中的单独条目。',
'playground.multiple.on': '开启',
'playground.multiple.off': '关闭',
'playground.image.params.sampler': '采样',
'playground.image.params.samplerSteps': '采样器步数',
'playground.image.params.sampler': '采样方法',
'playground.image.params.samplerSteps': '迭代步数',
'playground.image.params.seed': '随机种子',
'playground.image.params.negativePrompt': '负提示',
'playground.image.params.cfgScale': '缩放因子',
'playground.image.params.custom': '自定义',
'playground.image.params.custom.tips': '参数定义',
'playground.image.params.negativePrompt': '负提示',
'playground.image.params.cfgScale': '提示词引导系数',
'playground.image.params.custom': '高级',
'playground.image.params.custom.tips': 'API 风格',
'playground.image.params.openai': 'OpenAI 兼容',
'playground.embedding.handler.tips': '高度调节',
'playground.embedding.pcatips1':
'采用主成分分析PCA对文档向量化后的数据降维将新数据投射到PCA 空间中。',
'PCA主成分分析用于降低嵌入向量的维数使它们更容易可视化。',
'playground.embedding.pcatips2':
'图表中,点之间的距离表示对应文档之间的相似度。'
'在图表中,点之间的距离表示相应文档之间的相似度。点越近意味着相似度越高。',
'playground.audio.button.play': '播放',
'playground.audio.button.download': '下载',
'playground.audio.button.stop': '停止'
};

@ -450,7 +450,7 @@ const Models: React.FC<ModelsProps> = ({
}}
color="geekblue"
>
Embedding Only
Embedding
</Tag>
);
}
@ -464,7 +464,7 @@ const Models: React.FC<ModelsProps> = ({
}}
color="geekblue"
>
{intl.formatMessage({ id: 'playground.audio.texttospeech' })}
Text-To-Speech
</Tag>
);
}
@ -478,7 +478,7 @@ const Models: React.FC<ModelsProps> = ({
}}
color="geekblue"
>
{intl.formatMessage({ id: 'playground.audio.speechtotext' })}
Speech-To-Text
</Tag>
);
}
@ -492,7 +492,7 @@ const Models: React.FC<ModelsProps> = ({
}}
color="geekblue"
>
Image Only
Image
</Tag>
);
}

@ -99,25 +99,17 @@ export const textToSpeech = async (params: any, options?: any) => {
const audioBlob = await res.blob();
const audioUrl = URL.createObjectURL(audioBlob);
return audioUrl;
return {
url: audioUrl,
type: audioBlob.type
};
};
// export const speechToText = async (params: any, options?: any) => {
// const res = await fetch(AUDIO_SPEECH_TO_TEXT_API, {
// method: 'POST',
// body: JSON.stringify(params.data),
// signal: params.signal
// });
// if (!res.ok) {
// throw new Error('Network response was not ok');
// }
// return res.json();
// };
export const speechToText = async (params: any, options?: any) => {
return request(AUDIO_SPEECH_TO_TEXT_API, {
method: 'POST',
data: params.data,
cancelToken: options?.cancelToken,
headers: {
'Content-Type': 'multipart/form-data'
}

@ -1,7 +1,13 @@
import { AudioOutlined } from '@ant-design/icons';
import { useIntl } from '@umijs/max';
import { Button, Space, Tooltip } from 'antd';
import React, { useCallback, useEffect, useRef, useState } from 'react';
import React, {
useCallback,
useEffect,
useMemo,
useRef,
useState
} from 'react';
// import '../style/audio-input.less';
interface AudioInputProps {
@ -9,6 +15,7 @@ interface AudioInputProps {
chunks: any[];
url: string;
name: string;
type: string;
duration: number;
}) => void;
onAnalyse?: (analyseData: any, frequencyBinCount: any) => void;
@ -19,6 +26,11 @@ interface AudioInputProps {
type?: 'text' | 'primary' | 'default';
}
const recordingFormat = {
type: 'audio/wav',
suffix: '.wav'
};
const AudioInput: React.FC<AudioInputProps> = (props) => {
const intl = useIntl();
const [audioOn, setAudioOn] = useState(false);
@ -155,6 +167,8 @@ const AudioInput: React.FC<AudioInputProps> = (props) => {
try {
await EnableAudio();
console.log('audioStream:', audioStream.current);
audioRecorder.current = new MediaRecorder(audioStream.current);
const audioChunks: any[] = [];
@ -170,14 +184,14 @@ const AudioInput: React.FC<AudioInputProps> = (props) => {
// stop recording
audioRecorder.current.onstop = () => {
const audioBlob = new Blob(audioChunks, { type: 'audio/wav' });
const audioBlob = new Blob(audioChunks, { type: recordingFormat.type });
const audioUrl = URL.createObjectURL(audioBlob);
handleAudioData({
chunks: audioBlob,
size: audioBlob.size,
type: audioBlob.type,
url: audioUrl,
name: `recording-${new Date().toISOString()}.wav`,
name: `recording-${new Date().toISOString()}${recordingFormat.suffix}`,
duration: Math.ceil((Date.now() - startTime.current) / 1000)
});
@ -195,6 +209,25 @@ const AudioInput: React.FC<AudioInputProps> = (props) => {
}
};
const renderRecordButtonTips = useMemo(() => {
if (!audioPermission) {
return intl.formatMessage({ id: 'playground.audio.enablemic' });
}
return isRecording
? intl.formatMessage({ id: 'playground.audio.stoprecord' })
: intl.formatMessage({ id: 'playground.audio.startrecord' });
}, [audioPermission, isRecording, intl]);
const noAudioPermissionButtonStyle = useMemo(() => {
if (!audioPermission) {
return {
backgroundColor: 'var(--ant-color-error-bg-filled-hover)',
color: 'var(--ant-color-error-border-hover)',
border: 'none'
};
}
}, [audioPermission]);
useEffect(() => {
return () => {
handleStopRecording();
@ -210,13 +243,7 @@ const AudioInput: React.FC<AudioInputProps> = (props) => {
<div className="audio-input">
<Space size={40} className="btns">
{
<Tooltip
title={
isRecording
? intl.formatMessage({ id: 'playground.audio.stoprecord' })
: intl.formatMessage({ id: 'playground.audio.startrecord' })
}
>
<Tooltip title={renderRecordButtonTips}>
<div
style={{
display: 'flex',
@ -231,6 +258,9 @@ const AudioInput: React.FC<AudioInputProps> = (props) => {
icon={<AudioOutlined />}
size="middle"
type={props.type ?? 'text'}
style={{
...noAudioPermissionButtonStyle
}}
danger={isRecording}
onClick={StartRecording}
></Button>

@ -12,7 +12,7 @@ import {
SendOutlined
} from '@ant-design/icons';
import { useIntl, useSearchParams } from '@umijs/max';
import { Button, Checkbox, Segmented, Tabs, Tooltip } from 'antd';
import { Button, Segmented, Tabs, Tooltip } from 'antd';
import classNames from 'classnames';
import { PCA } from 'ml-pca';
import 'overlayscrollbars/overlayscrollbars.css';
@ -81,7 +81,7 @@ const GroundEmbedding: React.FC<MessageProps> = forwardRef((props, ref) => {
copyValue: ''
});
const [lessTwoInput, setLessTwoInput] = useState<boolean>(false);
const multiplePasteEnable = useRef<boolean>(true);
const [multiplePasteEnable, setMultiplePasteEnable] = useState<boolean>(true);
const [textList, setTextList] = useState<
{ text: string; uid: number | string; name: string }[]
@ -279,7 +279,7 @@ const GroundEmbedding: React.FC<MessageProps> = forwardRef((props, ref) => {
const handleOnPaste = useCallback(
(e: any, index: number) => {
if (!multiplePasteEnable.current) return;
if (!multiplePasteEnable) return;
const text = e.clipboardData.getData('text');
if (text) {
const dataLlist = text.split('\n').map((item: string) => {
@ -402,18 +402,26 @@ const GroundEmbedding: React.FC<MessageProps> = forwardRef((props, ref) => {
</div>
</h3>
<div className="flex-center gap-10">
<Button className="flex-center" size="middle">
<Checkbox
defaultChecked={multiplePasteEnable.current}
onChange={(e: any) => {
multiplePasteEnable.current = e.target.checked;
<Tooltip
title={intl.formatMessage({
id: 'playground.input.multiplePaste.tips'
})}
>
<Button
className="flex-center"
variant="filled"
size="middle"
color={multiplePasteEnable ? 'primary' : 'default'}
onClick={() => {
setMultiplePasteEnable(!multiplePasteEnable);
}}
>
{intl.formatMessage({
id: 'playground.input.multiplePaste'
})}
</Checkbox>
</Button>
</Button>
</Tooltip>
<Button size="middle" onClick={handleAddText}>
<PlusOutlined />
{intl.formatMessage({ id: 'playground.embedding.addtext' })}

@ -47,14 +47,17 @@ interface MessageProps {
const initialValues = {
n: 1,
size: '512x512',
quality: 'standard',
style: null
seed: null,
sampler: 'euler_a',
cfg_scale: 4.5,
sample_steps: 10,
negative_prompt: null
};
const GroundImages: React.FC<MessageProps> = forwardRef((props, ref) => {
const { modelList } = props;
const messageId = useRef<number>(0);
const [isOpenaiCompatible, setIsOpenaiCompatible] = useState<boolean>(true);
const [isOpenaiCompatible, setIsOpenaiCompatible] = useState<boolean>(false);
const [imageList, setImageList] = useState<
{
dataUrl: string;
@ -223,7 +226,7 @@ const GroundImages: React.FC<MessageProps> = forwardRef((props, ref) => {
const params = {
stream: true,
stream_options: {
chunk_result: true
// chunk_result: false
},
prompt: current?.content || currentPrompt || '',
..._.omitBy(finalParameters, (value: string) => !value)
@ -251,8 +254,6 @@ const GroundImages: React.FC<MessageProps> = forwardRef((props, ref) => {
const imgItem = newImageList[item.index];
if (item.b64_json) {
imgItem.dataUrl += item.b64_json;
// imgItem.cache.push(item.b64_json);
console.log('imgItem.dataUrl:', imgItem.dataUrl);
}
const progress = _.round(item.progress, 0);
newImageList[item.index] = {

@ -303,7 +303,6 @@ const GroundLeft: React.FC<MessageProps> = forwardRef((props, ref) => {
clearAll={handleClear}
setModelSelections={handleSelectModel}
presetPrompt={handlePresetPrompt}
modelList={modelList}
/>
</div>
</div>

@ -2,7 +2,7 @@ import useOverlayScroller from '@/hooks/use-overlay-scroller';
import useRequestToken from '@/hooks/use-request-token';
import { ClearOutlined, PlusOutlined, SendOutlined } from '@ant-design/icons';
import { useIntl, useSearchParams } from '@umijs/max';
import { Button, Checkbox, Input, Spin, Tag } from 'antd';
import { Button, Input, Spin, Tag, Tooltip } from 'antd';
import classNames from 'classnames';
import _ from 'lodash';
import 'overlayscrollbars/overlayscrollbars.css';
@ -77,7 +77,7 @@ const GroundReranker: React.FC<MessageProps> = forwardRef((props, ref) => {
const messageListLengthCache = useRef<number>(0);
const requestToken = useRef<any>(null);
const formRef = useRef<any>(null);
const multiplePasteEnable = useRef<boolean>(true);
const [multiplePasteEnable, setMultiplePasteEnable] = useState<boolean>(true);
const [fileList, setFileList] = useState<
{
text: string;
@ -346,7 +346,7 @@ const GroundReranker: React.FC<MessageProps> = forwardRef((props, ref) => {
const handleOnPaste = useCallback(
(e: any, index: number) => {
if (!multiplePasteEnable.current) return;
if (!multiplePasteEnable) return;
const text = e.clipboardData.getData('text');
if (text) {
console.log('text:', text);
@ -465,18 +465,25 @@ const GroundReranker: React.FC<MessageProps> = forwardRef((props, ref) => {
)}
</span>
<div className="flex-center gap-10">
<Button className="flex-center" size="middle">
<Checkbox
defaultChecked={multiplePasteEnable.current}
onChange={(e: any) => {
multiplePasteEnable.current = e.target.checked;
<Tooltip
title={intl.formatMessage({
id: 'playground.input.multiplePaste.tips'
})}
>
<Button
className="flex-center"
variant="filled"
size="middle"
color={multiplePasteEnable ? 'primary' : 'default'}
onClick={() => {
setMultiplePasteEnable(!multiplePasteEnable);
}}
>
{intl.formatMessage({
id: 'playground.input.multiplePaste'
})}
</Checkbox>
</Button>
</Button>
</Tooltip>
<Button size="middle" onClick={handleAddText}>
<PlusOutlined />
{intl.formatMessage({ id: 'playground.embedding.addtext' })}

@ -4,9 +4,9 @@ import IconFont from '@/components/icon-font';
import UploadAudio from '@/components/upload-audio';
import useOverlayScroller from '@/hooks/use-overlay-scroller';
import { readAudioFile } from '@/utils/load-audio-file';
import { AudioOutlined, SendOutlined } from '@ant-design/icons';
import { SendOutlined } from '@ant-design/icons';
import { useIntl, useSearchParams } from '@umijs/max';
import { Button, Spin, Tag, Tooltip } from 'antd';
import { Button, Spin, Tooltip } from 'antd';
import classNames from 'classnames';
import 'overlayscrollbars/overlayscrollbars.css';
import {
@ -20,7 +20,6 @@ import {
} from 'react';
import { speechToText } from '../apis';
import { RealtimeParamsConfig as paramsConfig } from '../config/params-config';
import { MessageItem } from '../config/types';
import '../style/ground-left.less';
import '../style/speech-to-text.less';
import '../style/system-message-wrap.less';
@ -42,14 +41,9 @@ const GroundLeft: React.FC<MessageProps> = forwardRef((props, ref) => {
const intl = useIntl();
const { modelList } = props;
const messageId = useRef<number>(0);
const [messageList, setMessageList] = useState<MessageItem[]>([
{
content: '',
title: '',
role: '',
uid: messageId.current
}
]);
const [messageList, setMessageList] = useState<
{ uid: number; content: string }[]
>([]);
const [searchParams] = useSearchParams();
const selectModel = searchParams.get('model') || '';
const [parameters, setParams] = useState<any>({});
@ -68,7 +62,6 @@ const GroundLeft: React.FC<MessageProps> = forwardRef((props, ref) => {
analyser: null
});
const [isRecording, setIsRecording] = useState(false);
const [recordEnd, setRecordEnd] = useState(false);
const formRef = useRef<any>(null);
const { initialize, updateScrollerPosition } = useOverlayScroller();
@ -127,8 +120,6 @@ const GroundLeft: React.FC<MessageProps> = forwardRef((props, ref) => {
setMessageList([
{
content: result.text,
title: '',
role: '',
uid: messageId.current
}
]);
@ -136,7 +127,6 @@ const GroundLeft: React.FC<MessageProps> = forwardRef((props, ref) => {
console.log('error:', error);
} finally {
setLoading(false);
setRecordEnd(false);
setIsRecording(false);
}
};
@ -170,9 +160,6 @@ const GroundLeft: React.FC<MessageProps> = forwardRef((props, ref) => {
duration: data.duration
};
});
setTimeout(() => {
setRecordEnd(true);
}, 200);
},
[]
);
@ -183,9 +170,8 @@ const GroundLeft: React.FC<MessageProps> = forwardRef((props, ref) => {
const handleUploadChange = useCallback(
async (data: { file: any; fileList: any }) => {
const res = await readAudioFile(data.file.originFileObj);
const res = await readAudioFile(data.file);
setAudioData(res);
setRecordEnd(true);
},
[]
);
@ -208,15 +194,16 @@ const GroundLeft: React.FC<MessageProps> = forwardRef((props, ref) => {
submitMessage();
};
const handleOnDiscard = useCallback(() => {
setRecordEnd(false);
setAudioData(null);
setIsRecording(false);
}, []);
const renderAniamtion = () => {
if (!audioPermissionOn) {
return null;
return (
<div className="tips-text">
<IconFont type={'icon-audio'} style={{ fontSize: 20 }}></IconFont>
<span>
{intl.formatMessage({ id: 'playground.audio.enablemic' })}
</span>
</div>
);
}
if (isRecording) {
return (
@ -240,7 +227,7 @@ const GroundLeft: React.FC<MessageProps> = forwardRef((props, ref) => {
useEffect(() => {
console.log('parameters:', parameters);
}, [parameters]);
useEffect(() => {}, [messageList]);
useEffect(() => {
if (scroller.current) {
initialize(scroller.current);
@ -259,13 +246,6 @@ const GroundLeft: React.FC<MessageProps> = forwardRef((props, ref) => {
}
}, [messageList, loading]);
useEffect(() => {
if (messageList.length > messageListLengthCache.current) {
updateScrollerPosition();
}
messageListLengthCache.current = messageList.length;
}, [messageList.length]);
return (
<div className="ground-left-wrapper">
<div className="ground-left">
@ -303,42 +283,6 @@ const GroundLeft: React.FC<MessageProps> = forwardRef((props, ref) => {
renderAniamtion()
)}
</div>
{!audioPermissionOn && (
<div
style={{
display: 'flex',
flexDirection: 'column',
justifyContent: 'center',
alignItems: 'center',
height: '100%'
}}
>
<span>
<Tag
style={{
width: 36,
height: 36,
lineHeight: '36px',
textAlign: 'center'
}}
bordered={false}
color="error"
icon={
<AudioOutlined className="font-size-16"></AudioOutlined>
}
></Tag>
</span>
<span
style={{
marginTop: 10,
fontSize: 14,
fontWeight: 500
}}
>
{intl.formatMessage({ id: 'playground.audio.enablemic' })}
</span>
</div>
)}
</div>
<div
style={{
@ -365,7 +309,7 @@ const GroundLeft: React.FC<MessageProps> = forwardRef((props, ref) => {
justifyContent: 'center'
}}
>
{audioData ? (
{messageList.length ? (
messageList[0]?.content
) : (
<span className="text-tertiary">

@ -36,8 +36,7 @@ interface MessageProps {
const initialValues = {
voice: '',
response_format: 'mp3',
speed: 1
response_format: 'mp3'
};
const GroundLeft: React.FC<MessageProps> = forwardRef((props, ref) => {
@ -113,13 +112,13 @@ const GroundLeft: React.FC<MessageProps> = forwardRef((props, ref) => {
...parameters,
input: current?.content || currentPrompt
};
const audioUrl: any = await textToSpeech({
const res: any = await textToSpeech({
data: params,
url: CHAT_API,
signal
});
console.log('result:', parameters, audioUrl);
console.log('result:', res);
setMessageList([
{
@ -129,7 +128,7 @@ const GroundLeft: React.FC<MessageProps> = forwardRef((props, ref) => {
speed: parameters.speed,
uid: messageId.current,
autoplay: checkvalueRef.current,
audioUrl: audioUrl
audioUrl: res.url
}
]);
} catch (error) {

@ -229,8 +229,8 @@ const ParamsSettings: React.FC<ParamsSettingsProps> = ({
variant="borderless"
>
<Slider
defaultValue={1024}
max={2048}
defaultValue={2048}
max={16 * 1024}
step={1}
style={{ marginBottom: 0, marginTop: 16, marginInline: 0 }}
tooltip={{ open: false }}

@ -28,11 +28,11 @@ export const TTSParamsConfig: ParamsSchema[] = [
name: 'response_format',
options: [
{ label: 'mp3', value: 'mp3' },
{ label: 'opus', value: 'opus' },
{ label: 'aac', value: 'aac' },
{ label: 'flac', value: 'flac' },
{ label: 'wav', value: 'wav' },
{ label: 'pcm', value: 'pcm' }
// { label: 'opus', value: 'opus' },
// { label: 'aac', value: 'aac' },
// { label: 'flac', value: 'flac' },
{ label: 'wav', value: 'wav' }
// { label: 'pcm', value: 'pcm' }
],
label: {
text: 'playground.params.format',
@ -43,27 +43,27 @@ export const TTSParamsConfig: ParamsSchema[] = [
required: false
}
]
},
{
type: 'Select',
name: 'speed',
options: [
{ label: '0.25x', value: 0.25 },
{ label: '0.5x', value: 0.5 },
{ label: '1x', value: 1 },
{ label: '2x', value: 2 },
{ label: '4x', value: 4 }
],
label: {
text: 'playground.params.speed',
isLocalized: true
},
rules: [
{
required: false
}
]
}
// {
// type: 'Select',
// name: 'speed',
// options: [
// { label: '0.25x', value: 0.25 },
// { label: '0.5x', value: 0.5 },
// { label: '1x', value: 1 },
// { label: '2x', value: 2 },
// { label: '4x', value: 4 }
// ],
// label: {
// text: 'playground.params.speed',
// isLocalized: true
// },
// rules: [
// {
// required: false
// }
// ]
// }
];
export const RealtimeParamsConfig: ParamsSchema[] = [

@ -86,45 +86,62 @@ export const readStreamData = async (
await readStreamData(reader, decoder, callback);
};
// Process the remainder of the buffer
const processBuffer = (buffer: string, callback: (data: any) => void) => {
const lines = buffer.split('\n');
for (const line of lines) {
if (line.startsWith('data: ')) {
const jsonStr = line.slice(6).trim();
try {
const jsonData = JSON.parse(jsonStr);
callback(jsonData);
} catch (e) {
console.error(
'Failed to parse JSON from remaining buffer:',
jsonStr,
e
);
}
}
}
};
export const readLargeStreamData = async (
reader: any,
decoder: TextDecoder,
callback: (data: any) => void
) => {
let buffer = '';
let buffer = ''; // cache incomplete line
const processStream = async () => {
while (true) {
const { done, value } = await reader.read();
if (done) {
if (buffer) {
try {
extractJSON(buffer).forEach((data) => {
callback?.(data);
});
} catch (e) {
console.error('parse buffer failed:', buffer);
}
// Process remaining buffered data
if (buffer.trim()) {
processBuffer(buffer, callback);
}
return;
break;
}
// cache each chunk
// Decode new chunk of data and append to buffer
buffer += decoder.decode(value, { stream: true });
const extractedData = extractJSON(buffer);
extractedData.forEach((data) => {
callback?.(data);
});
// Try to process the complete line in the buffer
const lines = buffer.split('\n');
buffer = lines.pop() || ''; // Keep last line (may be incomplete)
const lastIndex = buffer.lastIndexOf('}');
buffer = lastIndex !== -1 ? buffer.slice(lastIndex + 1) : buffer;
// next chunk
await processStream();
};
await processStream();
for (const line of lines) {
if (line.startsWith('data: ')) {
const jsonStr = line.slice(6).trim();
try {
const jsonData = JSON.parse(jsonStr);
callback(jsonData);
} catch (e) {
console.error('Failed to parse JSON:', jsonStr, e);
}
}
}
}
};
export const readTextEventStreamData = async (

@ -32,6 +32,7 @@ export const loadAudioData = async (data: any, type: string) => {
};
export const readAudioFile = async (file: File) => {
console.log('file====', file);
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onload = async function (e: any) {

Loading…
Cancel
Save