update 优化录音功能模块的代码和火山模块
This commit is contained in:
552
pnpm-lock.yaml
generated
552
pnpm-lock.yaml
generated
@@ -75,6 +75,15 @@ importers:
|
||||
'@kevisual/registry':
|
||||
specifier: ^0.0.1
|
||||
version: 0.0.1(typescript@5.9.3)
|
||||
'@radix-ui/react-alert-dialog':
|
||||
specifier: ^1.1.15
|
||||
version: 1.1.15(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
|
||||
'@radix-ui/react-dialog':
|
||||
specifier: ^1.1.15
|
||||
version: 1.1.15(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
|
||||
'@radix-ui/react-slot':
|
||||
specifier: ^1.2.3
|
||||
version: 1.2.3(@types/react@19.2.2)(react@19.2.0)
|
||||
'@ricky0123/vad-web':
|
||||
specifier: ^0.0.28
|
||||
version: 0.0.28
|
||||
@@ -84,6 +93,12 @@ importers:
|
||||
'@tailwindcss/vite':
|
||||
specifier: ^4.1.14
|
||||
version: 4.1.14(vite@6.3.7(@types/node@24.7.2)(jiti@2.6.1)(lightningcss@1.30.1))
|
||||
'@tanstack/react-form':
|
||||
specifier: ^1.23.7
|
||||
version: 1.23.7(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
|
||||
'@tanstack/react-query':
|
||||
specifier: ^5.90.5
|
||||
version: 5.90.5(react@19.2.0)
|
||||
astro:
|
||||
specifier: ^5.14.4
|
||||
version: 5.14.5(@types/node@24.7.2)(idb-keyval@6.2.2)(jiti@2.6.1)(lightningcss@1.30.1)(rollup@4.52.4)(typescript@5.9.3)
|
||||
@@ -161,7 +176,7 @@ importers:
|
||||
version: 7.11.0
|
||||
zustand:
|
||||
specifier: ^5.0.8
|
||||
version: 5.0.8(@types/react@19.2.2)(react@19.2.0)
|
||||
version: 5.0.8(@types/react@19.2.2)(react@19.2.0)(use-sync-external-store@1.6.0(react@19.2.0))
|
||||
devDependencies:
|
||||
'@kevisual/types':
|
||||
specifier: ^0.0.10
|
||||
@@ -760,6 +775,190 @@ packages:
|
||||
'@protobufjs/utf8@1.1.0':
|
||||
resolution: {integrity: sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==}
|
||||
|
||||
'@radix-ui/primitive@1.1.3':
|
||||
resolution: {integrity: sha512-JTF99U/6XIjCBo0wqkU5sK10glYe27MRRsfwoiq5zzOEZLHU3A3KCMa5X/azekYRCJ0HlwI0crAXS/5dEHTzDg==}
|
||||
|
||||
'@radix-ui/react-alert-dialog@1.1.15':
|
||||
resolution: {integrity: sha512-oTVLkEw5GpdRe29BqJ0LSDFWI3qu0vR1M0mUkOQWDIUnY/QIkLpgDMWuKxP94c2NAC2LGcgVhG1ImF3jkZ5wXw==}
|
||||
peerDependencies:
|
||||
'@types/react': '*'
|
||||
'@types/react-dom': '*'
|
||||
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
||||
react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
||||
peerDependenciesMeta:
|
||||
'@types/react':
|
||||
optional: true
|
||||
'@types/react-dom':
|
||||
optional: true
|
||||
|
||||
'@radix-ui/react-compose-refs@1.1.2':
|
||||
resolution: {integrity: sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg==}
|
||||
peerDependencies:
|
||||
'@types/react': '*'
|
||||
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
||||
peerDependenciesMeta:
|
||||
'@types/react':
|
||||
optional: true
|
||||
|
||||
'@radix-ui/react-context@1.1.2':
|
||||
resolution: {integrity: sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA==}
|
||||
peerDependencies:
|
||||
'@types/react': '*'
|
||||
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
||||
peerDependenciesMeta:
|
||||
'@types/react':
|
||||
optional: true
|
||||
|
||||
'@radix-ui/react-dialog@1.1.15':
|
||||
resolution: {integrity: sha512-TCglVRtzlffRNxRMEyR36DGBLJpeusFcgMVD9PZEzAKnUs1lKCgX5u9BmC2Yg+LL9MgZDugFFs1Vl+Jp4t/PGw==}
|
||||
peerDependencies:
|
||||
'@types/react': '*'
|
||||
'@types/react-dom': '*'
|
||||
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
||||
react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
||||
peerDependenciesMeta:
|
||||
'@types/react':
|
||||
optional: true
|
||||
'@types/react-dom':
|
||||
optional: true
|
||||
|
||||
'@radix-ui/react-dismissable-layer@1.1.11':
|
||||
resolution: {integrity: sha512-Nqcp+t5cTB8BinFkZgXiMJniQH0PsUt2k51FUhbdfeKvc4ACcG2uQniY/8+h1Yv6Kza4Q7lD7PQV0z0oicE0Mg==}
|
||||
peerDependencies:
|
||||
'@types/react': '*'
|
||||
'@types/react-dom': '*'
|
||||
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
||||
react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
||||
peerDependenciesMeta:
|
||||
'@types/react':
|
||||
optional: true
|
||||
'@types/react-dom':
|
||||
optional: true
|
||||
|
||||
'@radix-ui/react-focus-guards@1.1.3':
|
||||
resolution: {integrity: sha512-0rFg/Rj2Q62NCm62jZw0QX7a3sz6QCQU0LpZdNrJX8byRGaGVTqbrW9jAoIAHyMQqsNpeZ81YgSizOt5WXq0Pw==}
|
||||
peerDependencies:
|
||||
'@types/react': '*'
|
||||
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
||||
peerDependenciesMeta:
|
||||
'@types/react':
|
||||
optional: true
|
||||
|
||||
'@radix-ui/react-focus-scope@1.1.7':
|
||||
resolution: {integrity: sha512-t2ODlkXBQyn7jkl6TNaw/MtVEVvIGelJDCG41Okq/KwUsJBwQ4XVZsHAVUkK4mBv3ewiAS3PGuUWuY2BoK4ZUw==}
|
||||
peerDependencies:
|
||||
'@types/react': '*'
|
||||
'@types/react-dom': '*'
|
||||
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
||||
react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
||||
peerDependenciesMeta:
|
||||
'@types/react':
|
||||
optional: true
|
||||
'@types/react-dom':
|
||||
optional: true
|
||||
|
||||
'@radix-ui/react-id@1.1.1':
|
||||
resolution: {integrity: sha512-kGkGegYIdQsOb4XjsfM97rXsiHaBwco+hFI66oO4s9LU+PLAC5oJ7khdOVFxkhsmlbpUqDAvXw11CluXP+jkHg==}
|
||||
peerDependencies:
|
||||
'@types/react': '*'
|
||||
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
||||
peerDependenciesMeta:
|
||||
'@types/react':
|
||||
optional: true
|
||||
|
||||
'@radix-ui/react-portal@1.1.9':
|
||||
resolution: {integrity: sha512-bpIxvq03if6UNwXZ+HTK71JLh4APvnXntDc6XOX8UVq4XQOVl7lwok0AvIl+b8zgCw3fSaVTZMpAPPagXbKmHQ==}
|
||||
peerDependencies:
|
||||
'@types/react': '*'
|
||||
'@types/react-dom': '*'
|
||||
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
||||
react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
||||
peerDependenciesMeta:
|
||||
'@types/react':
|
||||
optional: true
|
||||
'@types/react-dom':
|
||||
optional: true
|
||||
|
||||
'@radix-ui/react-presence@1.1.5':
|
||||
resolution: {integrity: sha512-/jfEwNDdQVBCNvjkGit4h6pMOzq8bHkopq458dPt2lMjx+eBQUohZNG9A7DtO/O5ukSbxuaNGXMjHicgwy6rQQ==}
|
||||
peerDependencies:
|
||||
'@types/react': '*'
|
||||
'@types/react-dom': '*'
|
||||
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
||||
react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
||||
peerDependenciesMeta:
|
||||
'@types/react':
|
||||
optional: true
|
||||
'@types/react-dom':
|
||||
optional: true
|
||||
|
||||
'@radix-ui/react-primitive@2.1.3':
|
||||
resolution: {integrity: sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==}
|
||||
peerDependencies:
|
||||
'@types/react': '*'
|
||||
'@types/react-dom': '*'
|
||||
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
||||
react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
||||
peerDependenciesMeta:
|
||||
'@types/react':
|
||||
optional: true
|
||||
'@types/react-dom':
|
||||
optional: true
|
||||
|
||||
'@radix-ui/react-slot@1.2.3':
|
||||
resolution: {integrity: sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==}
|
||||
peerDependencies:
|
||||
'@types/react': '*'
|
||||
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
||||
peerDependenciesMeta:
|
||||
'@types/react':
|
||||
optional: true
|
||||
|
||||
'@radix-ui/react-use-callback-ref@1.1.1':
|
||||
resolution: {integrity: sha512-FkBMwD+qbGQeMu1cOHnuGB6x4yzPjho8ap5WtbEJ26umhgqVXbhekKUQO+hZEL1vU92a3wHwdp0HAcqAUF5iDg==}
|
||||
peerDependencies:
|
||||
'@types/react': '*'
|
||||
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
||||
peerDependenciesMeta:
|
||||
'@types/react':
|
||||
optional: true
|
||||
|
||||
'@radix-ui/react-use-controllable-state@1.2.2':
|
||||
resolution: {integrity: sha512-BjasUjixPFdS+NKkypcyyN5Pmg83Olst0+c6vGov0diwTEo6mgdqVR6hxcEgFuh4QrAs7Rc+9KuGJ9TVCj0Zzg==}
|
||||
peerDependencies:
|
||||
'@types/react': '*'
|
||||
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
||||
peerDependenciesMeta:
|
||||
'@types/react':
|
||||
optional: true
|
||||
|
||||
'@radix-ui/react-use-effect-event@0.0.2':
|
||||
resolution: {integrity: sha512-Qp8WbZOBe+blgpuUT+lw2xheLP8q0oatc9UpmiemEICxGvFLYmHm9QowVZGHtJlGbS6A6yJ3iViad/2cVjnOiA==}
|
||||
peerDependencies:
|
||||
'@types/react': '*'
|
||||
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
||||
peerDependenciesMeta:
|
||||
'@types/react':
|
||||
optional: true
|
||||
|
||||
'@radix-ui/react-use-escape-keydown@1.1.1':
|
||||
resolution: {integrity: sha512-Il0+boE7w/XebUHyBjroE+DbByORGR9KKmITzbR7MyQ4akpORYP/ZmbhAr0DG7RmmBqoOnZdy2QlvajJ2QA59g==}
|
||||
peerDependencies:
|
||||
'@types/react': '*'
|
||||
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
||||
peerDependenciesMeta:
|
||||
'@types/react':
|
||||
optional: true
|
||||
|
||||
'@radix-ui/react-use-layout-effect@1.1.1':
|
||||
resolution: {integrity: sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ==}
|
||||
peerDependencies:
|
||||
'@types/react': '*'
|
||||
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
||||
peerDependenciesMeta:
|
||||
'@types/react':
|
||||
optional: true
|
||||
|
||||
'@ricky0123/vad-web@0.0.28':
|
||||
resolution: {integrity: sha512-Hvw8jN3r1SBxmjJa89HITxRcwlT6dc7CQPVtVQLrqfY8EeQcx41QeqKUol4lw8ZCeAIHKwYndHnB1K/4SAQJgQ==}
|
||||
|
||||
@@ -1051,6 +1250,39 @@ packages:
|
||||
peerDependencies:
|
||||
vite: ^5.2.0 || ^6 || ^7
|
||||
|
||||
'@tanstack/devtools-event-client@0.3.3':
|
||||
resolution: {integrity: sha512-RfV+OPV/M3CGryYqTue684u10jUt55PEqeBOnOtCe6tAmHI9Iqyc8nHeDhWPEV9715gShuauFVaMc9RiUVNdwg==}
|
||||
engines: {node: '>=18'}
|
||||
|
||||
'@tanstack/form-core@1.24.3':
|
||||
resolution: {integrity: sha512-e+HzSD49NWr4aIqJWtPPzmi+/phBJAP3nSPN8dvxwmJWqAxuB/cH138EcmCFf3+oA7j3BXvwvTY0I+8UweGPjQ==}
|
||||
|
||||
'@tanstack/query-core@5.90.5':
|
||||
resolution: {integrity: sha512-wLamYp7FaDq6ZnNehypKI5fNvxHPfTYylE0m/ZpuuzJfJqhR5Pxg9gvGBHZx4n7J+V5Rg5mZxHHTlv25Zt5u+w==}
|
||||
|
||||
'@tanstack/react-form@1.23.7':
|
||||
resolution: {integrity: sha512-p/j9Gi2+s135sOjj48RjM+6xZQr1FVpliQlETLYBEGmmmxWHgYYs2b62mTDSnuv7AqtuZhpQ+t0CRFVfbQLsFA==}
|
||||
peerDependencies:
|
||||
'@tanstack/react-start': ^1.130.10
|
||||
react: ^17.0.0 || ^18.0.0 || ^19.0.0
|
||||
peerDependenciesMeta:
|
||||
'@tanstack/react-start':
|
||||
optional: true
|
||||
|
||||
'@tanstack/react-query@5.90.5':
|
||||
resolution: {integrity: sha512-pN+8UWpxZkEJ/Rnnj2v2Sxpx1WFlaa9L6a4UO89p6tTQbeo+m0MS8oYDjbggrR8QcTyjKoYWKS3xJQGr3ExT8Q==}
|
||||
peerDependencies:
|
||||
react: ^18 || ^19
|
||||
|
||||
'@tanstack/react-store@0.7.7':
|
||||
resolution: {integrity: sha512-qqT0ufegFRDGSof9D/VqaZgjNgp4tRPHZIJq2+QIHkMUtHjaJ0lYrrXjeIUJvjnTbgPfSD1XgOMEt0lmANn6Zg==}
|
||||
peerDependencies:
|
||||
react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
|
||||
react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
|
||||
|
||||
'@tanstack/store@0.7.7':
|
||||
resolution: {integrity: sha512-xa6pTan1bcaqYDS9BDpSiS63qa6EoDkPN9RsRaxHuDdVDNntzq3xNwR5YKTU/V3SkSyC9T4YVOPh2zRQN0nhIQ==}
|
||||
|
||||
'@tweenjs/tween.js@23.1.3':
|
||||
resolution: {integrity: sha512-vJmvvwFxYuGnF2axRtPYocag6Clbb5YS7kLL+SO/TeVFzHqDIWrNKYtcsPMibjDx9O+bu+psAy9NKfWklassUA==}
|
||||
|
||||
@@ -1263,6 +1495,10 @@ packages:
|
||||
argparse@2.0.1:
|
||||
resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==}
|
||||
|
||||
aria-hidden@1.2.6:
|
||||
resolution: {integrity: sha512-ik3ZgC9dY/lYVVM++OISsaYDeg1tb0VtP5uL3ouh1koGOaUMDPpbFIei4JkFimWUFPn90sbMNMXQAIVOlnYKJA==}
|
||||
engines: {node: '>=10'}
|
||||
|
||||
aria-query@5.3.2:
|
||||
resolution: {integrity: sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==}
|
||||
engines: {node: '>= 0.4'}
|
||||
@@ -1439,6 +1675,9 @@ packages:
|
||||
supports-color:
|
||||
optional: true
|
||||
|
||||
decode-formdata@0.9.0:
|
||||
resolution: {integrity: sha512-q5uwOjR3Um5YD+ZWPOF/1sGHVW9A5rCrRwITQChRXlmPkxDFBqCm4jNTIVdGHNH9OnR+V9MoZVgRhsFb+ARbUw==}
|
||||
|
||||
decode-named-character-reference@1.2.0:
|
||||
resolution: {integrity: sha512-c6fcElNV6ShtZXmsgNgFFV5tVX2PaV4g+MOAkb8eXHvn6sryJBrZa9r0zV6+dtTyoCKxtDy5tyQ5ZwQuidtd+Q==}
|
||||
|
||||
@@ -1469,6 +1708,9 @@ packages:
|
||||
resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==}
|
||||
engines: {node: '>=8'}
|
||||
|
||||
detect-node-es@1.1.0:
|
||||
resolution: {integrity: sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==}
|
||||
|
||||
deterministic-object-hash@2.0.2:
|
||||
resolution: {integrity: sha512-KxektNH63SrbfUyDiwXqRb1rLwKt33AmMv+5Nhsw1kqZ13SJBRTgZHtGbE+hH3a1mVW1cz+4pqSWVPAtLVXTzQ==}
|
||||
engines: {node: '>=18'}
|
||||
@@ -1677,6 +1919,10 @@ packages:
|
||||
resolution: {integrity: sha512-QZjmEOC+IT1uk6Rx0sX22V6uHWVwbdbxf1faPqJ1QhLdGgsRGCZoyaQBm/piRdJy/D2um6hM1UP7ZEeQ4EkP+Q==}
|
||||
engines: {node: '>=18'}
|
||||
|
||||
get-nonce@1.0.1:
|
||||
resolution: {integrity: sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q==}
|
||||
engines: {node: '>=6'}
|
||||
|
||||
github-slugger@2.0.0:
|
||||
resolution: {integrity: sha512-IaOQ9puYtjrkq7Y0Ygl9KDZnrf/aiUJYUpVf89y8kyaxbRG7Y1SrX/jaumrv81vc61+kiMempujsM3Yw7w5qcw==}
|
||||
|
||||
@@ -2555,12 +2801,42 @@ packages:
|
||||
resolution: {integrity: sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
||||
react-remove-scroll-bar@2.3.8:
|
||||
resolution: {integrity: sha512-9r+yi9+mgU33AKcj6IbT9oRCO78WriSj6t/cF8DWBZJ9aOGPOTEDvdUDz1FwKim7QXWwmHqtdHnRJfhAxEG46Q==}
|
||||
engines: {node: '>=10'}
|
||||
peerDependencies:
|
||||
'@types/react': '*'
|
||||
react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
|
||||
peerDependenciesMeta:
|
||||
'@types/react':
|
||||
optional: true
|
||||
|
||||
react-remove-scroll@2.7.1:
|
||||
resolution: {integrity: sha512-HpMh8+oahmIdOuS5aFKKY6Pyog+FNaZV/XyJOq7b4YFwsFHe5yYfdbIalI4k3vU2nSDql7YskmUseHsRrJqIPA==}
|
||||
engines: {node: '>=10'}
|
||||
peerDependencies:
|
||||
'@types/react': '*'
|
||||
react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc
|
||||
peerDependenciesMeta:
|
||||
'@types/react':
|
||||
optional: true
|
||||
|
||||
react-resizable-panels@3.0.6:
|
||||
resolution: {integrity: sha512-b3qKHQ3MLqOgSS+FRYKapNkJZf5EQzuf6+RLiq1/IlTHw99YrZ2NJZLk4hQIzTnnIkRg2LUqyVinu6YWWpUYew==}
|
||||
peerDependencies:
|
||||
react: ^16.14.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc
|
||||
react-dom: ^16.14.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc
|
||||
|
||||
react-style-singleton@2.2.3:
|
||||
resolution: {integrity: sha512-b6jSvxvVnyptAiLjbkWLE/lOnR4lfTtDAl+eUC7RZy+QQWc6wRzIV2CE6xBuMmDxc2qIihtDCZD5NPOFl7fRBQ==}
|
||||
engines: {node: '>=10'}
|
||||
peerDependencies:
|
||||
'@types/react': '*'
|
||||
react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc
|
||||
peerDependenciesMeta:
|
||||
'@types/react':
|
||||
optional: true
|
||||
|
||||
react-toastify@11.0.5:
|
||||
resolution: {integrity: sha512-EpqHBGvnSTtHYhCPLxML05NLY2ZX0JURbAdNYa6BUkk+amz4wbKBQvoKQAB0ardvSarUBuY4Q4s1sluAzZwkmA==}
|
||||
peerDependencies:
|
||||
@@ -3058,6 +3334,31 @@ packages:
|
||||
url-parse@1.5.10:
|
||||
resolution: {integrity: sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==}
|
||||
|
||||
use-callback-ref@1.3.3:
|
||||
resolution: {integrity: sha512-jQL3lRnocaFtu3V00JToYz/4QkNWswxijDaCVNZRiRTO3HQDLsdu1ZtmIUvV4yPp+rvWm5j0y0TG/S61cuijTg==}
|
||||
engines: {node: '>=10'}
|
||||
peerDependencies:
|
||||
'@types/react': '*'
|
||||
react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc
|
||||
peerDependenciesMeta:
|
||||
'@types/react':
|
||||
optional: true
|
||||
|
||||
use-sidecar@1.1.3:
|
||||
resolution: {integrity: sha512-Fedw0aZvkhynoPYlA5WXrMCAMm+nSWdZt6lzJQ7Ok8S6Q+VsHmHpRWndVRJ8Be0ZbkfPc5LRYH+5XrzXcEeLRQ==}
|
||||
engines: {node: '>=10'}
|
||||
peerDependencies:
|
||||
'@types/react': '*'
|
||||
react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc
|
||||
peerDependenciesMeta:
|
||||
'@types/react':
|
||||
optional: true
|
||||
|
||||
use-sync-external-store@1.6.0:
|
||||
resolution: {integrity: sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w==}
|
||||
peerDependencies:
|
||||
react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
|
||||
|
||||
util-deprecate@1.0.2:
|
||||
resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==}
|
||||
|
||||
@@ -3799,6 +4100,163 @@ snapshots:
|
||||
|
||||
'@protobufjs/utf8@1.1.0': {}
|
||||
|
||||
'@radix-ui/primitive@1.1.3': {}
|
||||
|
||||
'@radix-ui/react-alert-dialog@1.1.15(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)':
|
||||
dependencies:
|
||||
'@radix-ui/primitive': 1.1.3
|
||||
'@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.2)(react@19.2.0)
|
||||
'@radix-ui/react-context': 1.1.2(@types/react@19.2.2)(react@19.2.0)
|
||||
'@radix-ui/react-dialog': 1.1.15(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
|
||||
'@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
|
||||
'@radix-ui/react-slot': 1.2.3(@types/react@19.2.2)(react@19.2.0)
|
||||
react: 19.2.0
|
||||
react-dom: 19.2.0(react@19.2.0)
|
||||
optionalDependencies:
|
||||
'@types/react': 19.2.2
|
||||
'@types/react-dom': 19.2.2(@types/react@19.2.2)
|
||||
|
||||
'@radix-ui/react-compose-refs@1.1.2(@types/react@19.2.2)(react@19.2.0)':
|
||||
dependencies:
|
||||
react: 19.2.0
|
||||
optionalDependencies:
|
||||
'@types/react': 19.2.2
|
||||
|
||||
'@radix-ui/react-context@1.1.2(@types/react@19.2.2)(react@19.2.0)':
|
||||
dependencies:
|
||||
react: 19.2.0
|
||||
optionalDependencies:
|
||||
'@types/react': 19.2.2
|
||||
|
||||
'@radix-ui/react-dialog@1.1.15(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)':
|
||||
dependencies:
|
||||
'@radix-ui/primitive': 1.1.3
|
||||
'@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.2)(react@19.2.0)
|
||||
'@radix-ui/react-context': 1.1.2(@types/react@19.2.2)(react@19.2.0)
|
||||
'@radix-ui/react-dismissable-layer': 1.1.11(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
|
||||
'@radix-ui/react-focus-guards': 1.1.3(@types/react@19.2.2)(react@19.2.0)
|
||||
'@radix-ui/react-focus-scope': 1.1.7(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
|
||||
'@radix-ui/react-id': 1.1.1(@types/react@19.2.2)(react@19.2.0)
|
||||
'@radix-ui/react-portal': 1.1.9(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
|
||||
'@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
|
||||
'@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
|
||||
'@radix-ui/react-slot': 1.2.3(@types/react@19.2.2)(react@19.2.0)
|
||||
'@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.2)(react@19.2.0)
|
||||
aria-hidden: 1.2.6
|
||||
react: 19.2.0
|
||||
react-dom: 19.2.0(react@19.2.0)
|
||||
react-remove-scroll: 2.7.1(@types/react@19.2.2)(react@19.2.0)
|
||||
optionalDependencies:
|
||||
'@types/react': 19.2.2
|
||||
'@types/react-dom': 19.2.2(@types/react@19.2.2)
|
||||
|
||||
'@radix-ui/react-dismissable-layer@1.1.11(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)':
|
||||
dependencies:
|
||||
'@radix-ui/primitive': 1.1.3
|
||||
'@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.2)(react@19.2.0)
|
||||
'@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
|
||||
'@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.2)(react@19.2.0)
|
||||
'@radix-ui/react-use-escape-keydown': 1.1.1(@types/react@19.2.2)(react@19.2.0)
|
||||
react: 19.2.0
|
||||
react-dom: 19.2.0(react@19.2.0)
|
||||
optionalDependencies:
|
||||
'@types/react': 19.2.2
|
||||
'@types/react-dom': 19.2.2(@types/react@19.2.2)
|
||||
|
||||
'@radix-ui/react-focus-guards@1.1.3(@types/react@19.2.2)(react@19.2.0)':
|
||||
dependencies:
|
||||
react: 19.2.0
|
||||
optionalDependencies:
|
||||
'@types/react': 19.2.2
|
||||
|
||||
'@radix-ui/react-focus-scope@1.1.7(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)':
|
||||
dependencies:
|
||||
'@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.2)(react@19.2.0)
|
||||
'@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
|
||||
'@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.2)(react@19.2.0)
|
||||
react: 19.2.0
|
||||
react-dom: 19.2.0(react@19.2.0)
|
||||
optionalDependencies:
|
||||
'@types/react': 19.2.2
|
||||
'@types/react-dom': 19.2.2(@types/react@19.2.2)
|
||||
|
||||
'@radix-ui/react-id@1.1.1(@types/react@19.2.2)(react@19.2.0)':
|
||||
dependencies:
|
||||
'@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.2)(react@19.2.0)
|
||||
react: 19.2.0
|
||||
optionalDependencies:
|
||||
'@types/react': 19.2.2
|
||||
|
||||
'@radix-ui/react-portal@1.1.9(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)':
|
||||
dependencies:
|
||||
'@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
|
||||
'@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.2)(react@19.2.0)
|
||||
react: 19.2.0
|
||||
react-dom: 19.2.0(react@19.2.0)
|
||||
optionalDependencies:
|
||||
'@types/react': 19.2.2
|
||||
'@types/react-dom': 19.2.2(@types/react@19.2.2)
|
||||
|
||||
'@radix-ui/react-presence@1.1.5(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)':
|
||||
dependencies:
|
||||
'@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.2)(react@19.2.0)
|
||||
'@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.2)(react@19.2.0)
|
||||
react: 19.2.0
|
||||
react-dom: 19.2.0(react@19.2.0)
|
||||
optionalDependencies:
|
||||
'@types/react': 19.2.2
|
||||
'@types/react-dom': 19.2.2(@types/react@19.2.2)
|
||||
|
||||
'@radix-ui/react-primitive@2.1.3(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)':
|
||||
dependencies:
|
||||
'@radix-ui/react-slot': 1.2.3(@types/react@19.2.2)(react@19.2.0)
|
||||
react: 19.2.0
|
||||
react-dom: 19.2.0(react@19.2.0)
|
||||
optionalDependencies:
|
||||
'@types/react': 19.2.2
|
||||
'@types/react-dom': 19.2.2(@types/react@19.2.2)
|
||||
|
||||
'@radix-ui/react-slot@1.2.3(@types/react@19.2.2)(react@19.2.0)':
|
||||
dependencies:
|
||||
'@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.2)(react@19.2.0)
|
||||
react: 19.2.0
|
||||
optionalDependencies:
|
||||
'@types/react': 19.2.2
|
||||
|
||||
'@radix-ui/react-use-callback-ref@1.1.1(@types/react@19.2.2)(react@19.2.0)':
|
||||
dependencies:
|
||||
react: 19.2.0
|
||||
optionalDependencies:
|
||||
'@types/react': 19.2.2
|
||||
|
||||
'@radix-ui/react-use-controllable-state@1.2.2(@types/react@19.2.2)(react@19.2.0)':
|
||||
dependencies:
|
||||
'@radix-ui/react-use-effect-event': 0.0.2(@types/react@19.2.2)(react@19.2.0)
|
||||
'@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.2)(react@19.2.0)
|
||||
react: 19.2.0
|
||||
optionalDependencies:
|
||||
'@types/react': 19.2.2
|
||||
|
||||
'@radix-ui/react-use-effect-event@0.0.2(@types/react@19.2.2)(react@19.2.0)':
|
||||
dependencies:
|
||||
'@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.2)(react@19.2.0)
|
||||
react: 19.2.0
|
||||
optionalDependencies:
|
||||
'@types/react': 19.2.2
|
||||
|
||||
'@radix-ui/react-use-escape-keydown@1.1.1(@types/react@19.2.2)(react@19.2.0)':
|
||||
dependencies:
|
||||
'@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.2)(react@19.2.0)
|
||||
react: 19.2.0
|
||||
optionalDependencies:
|
||||
'@types/react': 19.2.2
|
||||
|
||||
'@radix-ui/react-use-layout-effect@1.1.1(@types/react@19.2.2)(react@19.2.0)':
|
||||
dependencies:
|
||||
react: 19.2.0
|
||||
optionalDependencies:
|
||||
'@types/react': 19.2.2
|
||||
|
||||
'@ricky0123/vad-web@0.0.28':
|
||||
dependencies:
|
||||
onnxruntime-web: 1.23.0
|
||||
@@ -4024,6 +4482,39 @@ snapshots:
|
||||
tailwindcss: 4.1.14
|
||||
vite: 6.3.7(@types/node@24.7.2)(jiti@2.6.1)(lightningcss@1.30.1)
|
||||
|
||||
'@tanstack/devtools-event-client@0.3.3': {}
|
||||
|
||||
'@tanstack/form-core@1.24.3':
|
||||
dependencies:
|
||||
'@tanstack/devtools-event-client': 0.3.3
|
||||
'@tanstack/store': 0.7.7
|
||||
|
||||
'@tanstack/query-core@5.90.5': {}
|
||||
|
||||
'@tanstack/react-form@1.23.7(react-dom@19.2.0(react@19.2.0))(react@19.2.0)':
|
||||
dependencies:
|
||||
'@tanstack/form-core': 1.24.3
|
||||
'@tanstack/react-store': 0.7.7(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
|
||||
decode-formdata: 0.9.0
|
||||
devalue: 5.3.2
|
||||
react: 19.2.0
|
||||
transitivePeerDependencies:
|
||||
- react-dom
|
||||
|
||||
'@tanstack/react-query@5.90.5(react@19.2.0)':
|
||||
dependencies:
|
||||
'@tanstack/query-core': 5.90.5
|
||||
react: 19.2.0
|
||||
|
||||
'@tanstack/react-store@0.7.7(react-dom@19.2.0(react@19.2.0))(react@19.2.0)':
|
||||
dependencies:
|
||||
'@tanstack/store': 0.7.7
|
||||
react: 19.2.0
|
||||
react-dom: 19.2.0(react@19.2.0)
|
||||
use-sync-external-store: 1.6.0(react@19.2.0)
|
||||
|
||||
'@tanstack/store@0.7.7': {}
|
||||
|
||||
'@tweenjs/tween.js@23.1.3': {}
|
||||
|
||||
'@types/babel__core@7.20.5':
|
||||
@@ -4301,6 +4792,10 @@ snapshots:
|
||||
|
||||
argparse@2.0.1: {}
|
||||
|
||||
aria-hidden@1.2.6:
|
||||
dependencies:
|
||||
tslib: 2.8.1
|
||||
|
||||
aria-query@5.3.2: {}
|
||||
|
||||
array-iterate@2.0.1: {}
|
||||
@@ -4539,6 +5034,8 @@ snapshots:
|
||||
dependencies:
|
||||
ms: 2.1.3
|
||||
|
||||
decode-formdata@0.9.0: {}
|
||||
|
||||
decode-named-character-reference@1.2.0:
|
||||
dependencies:
|
||||
character-entities: 2.0.2
|
||||
@@ -4560,6 +5057,8 @@ snapshots:
|
||||
|
||||
detect-libc@2.1.2: {}
|
||||
|
||||
detect-node-es@1.1.0: {}
|
||||
|
||||
deterministic-object-hash@2.0.2:
|
||||
dependencies:
|
||||
base-64: 1.0.0
|
||||
@@ -4782,6 +5281,8 @@ snapshots:
|
||||
|
||||
get-east-asian-width@1.4.0: {}
|
||||
|
||||
get-nonce@1.0.1: {}
|
||||
|
||||
github-slugger@2.0.0: {}
|
||||
|
||||
glob-parent@5.1.2:
|
||||
@@ -6002,11 +6503,38 @@ snapshots:
|
||||
|
||||
react-refresh@0.17.0: {}
|
||||
|
||||
react-remove-scroll-bar@2.3.8(@types/react@19.2.2)(react@19.2.0):
|
||||
dependencies:
|
||||
react: 19.2.0
|
||||
react-style-singleton: 2.2.3(@types/react@19.2.2)(react@19.2.0)
|
||||
tslib: 2.8.1
|
||||
optionalDependencies:
|
||||
'@types/react': 19.2.2
|
||||
|
||||
react-remove-scroll@2.7.1(@types/react@19.2.2)(react@19.2.0):
|
||||
dependencies:
|
||||
react: 19.2.0
|
||||
react-remove-scroll-bar: 2.3.8(@types/react@19.2.2)(react@19.2.0)
|
||||
react-style-singleton: 2.2.3(@types/react@19.2.2)(react@19.2.0)
|
||||
tslib: 2.8.1
|
||||
use-callback-ref: 1.3.3(@types/react@19.2.2)(react@19.2.0)
|
||||
use-sidecar: 1.1.3(@types/react@19.2.2)(react@19.2.0)
|
||||
optionalDependencies:
|
||||
'@types/react': 19.2.2
|
||||
|
||||
react-resizable-panels@3.0.6(react-dom@19.2.0(react@19.2.0))(react@19.2.0):
|
||||
dependencies:
|
||||
react: 19.2.0
|
||||
react-dom: 19.2.0(react@19.2.0)
|
||||
|
||||
react-style-singleton@2.2.3(@types/react@19.2.2)(react@19.2.0):
|
||||
dependencies:
|
||||
get-nonce: 1.0.1
|
||||
react: 19.2.0
|
||||
tslib: 2.8.1
|
||||
optionalDependencies:
|
||||
'@types/react': 19.2.2
|
||||
|
||||
react-toastify@11.0.5(react-dom@19.2.0(react@19.2.0))(react@19.2.0):
|
||||
dependencies:
|
||||
clsx: 2.1.1
|
||||
@@ -6597,6 +7125,25 @@ snapshots:
|
||||
querystringify: 2.2.0
|
||||
requires-port: 1.0.0
|
||||
|
||||
use-callback-ref@1.3.3(@types/react@19.2.2)(react@19.2.0):
|
||||
dependencies:
|
||||
react: 19.2.0
|
||||
tslib: 2.8.1
|
||||
optionalDependencies:
|
||||
'@types/react': 19.2.2
|
||||
|
||||
use-sidecar@1.1.3(@types/react@19.2.2)(react@19.2.0):
|
||||
dependencies:
|
||||
detect-node-es: 1.1.0
|
||||
react: 19.2.0
|
||||
tslib: 2.8.1
|
||||
optionalDependencies:
|
||||
'@types/react': 19.2.2
|
||||
|
||||
use-sync-external-store@1.6.0(react@19.2.0):
|
||||
dependencies:
|
||||
react: 19.2.0
|
||||
|
||||
util-deprecate@1.0.2: {}
|
||||
|
||||
uuid@8.3.2: {}
|
||||
@@ -6698,9 +7245,10 @@ snapshots:
|
||||
|
||||
zod@3.25.76: {}
|
||||
|
||||
zustand@5.0.8(@types/react@19.2.2)(react@19.2.0):
|
||||
zustand@5.0.8(@types/react@19.2.2)(react@19.2.0)(use-sync-external-store@1.6.0(react@19.2.0)):
|
||||
optionalDependencies:
|
||||
'@types/react': 19.2.2
|
||||
react: 19.2.0
|
||||
use-sync-external-store: 1.6.0(react@19.2.0)
|
||||
|
||||
zwitch@2.0.4: {}
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
"build": "astro build",
|
||||
"preview": "astro preview",
|
||||
"pub": "envision deploy ./dist -k light-code-center -v 0.0.1 -u",
|
||||
"ui": "pnpm dlx shadcn@latest add ",
|
||||
"sn": "pnpm dlx shadcn@latest add "
|
||||
},
|
||||
"keywords": [],
|
||||
@@ -25,9 +26,14 @@
|
||||
"@kevisual/query": "^0.0.29",
|
||||
"@kevisual/query-login": "^0.0.6",
|
||||
"@kevisual/registry": "^0.0.1",
|
||||
"@radix-ui/react-alert-dialog": "^1.1.15",
|
||||
"@radix-ui/react-dialog": "^1.1.15",
|
||||
"@radix-ui/react-slot": "^1.2.3",
|
||||
"@ricky0123/vad-web": "^0.0.28",
|
||||
"@szhsin/react-menu": "^4.5.0",
|
||||
"@tailwindcss/vite": "^4.1.14",
|
||||
"@tanstack/react-form": "^1.23.7",
|
||||
"@tanstack/react-query": "^5.90.5",
|
||||
"astro": "^5.14.4",
|
||||
"class-variance-authority": "^0.7.1",
|
||||
"clsx": "^2.1.1",
|
||||
|
||||
@@ -333,8 +333,8 @@ export const Table: React.FC<TableProps> = ({
|
||||
|
||||
// Ctrl/Cmd + A 全选
|
||||
if ((event.ctrlKey || event.metaKey) && event.key === 'a') {
|
||||
event.preventDefault();
|
||||
handleSelectAll(true);
|
||||
// event.preventDefault();
|
||||
// handleSelectAll(true);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
46
web/src/apps/muse/components/PasswordInput.tsx
Normal file
46
web/src/apps/muse/components/PasswordInput.tsx
Normal file
@@ -0,0 +1,46 @@
|
||||
import React, { useState } from 'react';
|
||||
import { Eye, EyeOff } from 'lucide-react';
|
||||
|
||||
interface PasswordInputProps {
|
||||
value: string;
|
||||
onChange: (value: string) => void;
|
||||
placeholder?: string;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
export const PasswordInput: React.FC<PasswordInputProps> = ({
|
||||
value,
|
||||
onChange,
|
||||
placeholder,
|
||||
className = '',
|
||||
}) => {
|
||||
const [showPassword, setShowPassword] = useState(false);
|
||||
|
||||
const togglePasswordVisibility = () => {
|
||||
setShowPassword(!showPassword);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="relative">
|
||||
<input
|
||||
type={showPassword ? 'text' : 'password'}
|
||||
value={value}
|
||||
onChange={(e) => onChange(e.target.value)}
|
||||
placeholder={placeholder}
|
||||
className={`pr-10 ${className}`}
|
||||
/>
|
||||
<button
|
||||
type="button"
|
||||
onClick={togglePasswordVisibility}
|
||||
className="absolute right-3 top-1/2 transform -translate-y-1/2 p-1 hover:bg-gray-100 rounded transition-colors"
|
||||
title={showPassword ? '隐藏密码' : '显示密码'}
|
||||
>
|
||||
{showPassword ? (
|
||||
<EyeOff className="w-4 h-4 text-gray-500" />
|
||||
) : (
|
||||
<Eye className="w-4 h-4 text-gray-500" />
|
||||
)}
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -3,7 +3,7 @@ import { AuthProvider } from '../login/AuthProvider';
|
||||
import { Panel, PanelGroup, PanelResizeHandle } from 'react-resizable-panels';
|
||||
import { useState, useRef } from 'react';
|
||||
|
||||
import { App as Voice } from './videos/index.tsx';
|
||||
import { App as Voice } from './voice/index.tsx';
|
||||
import { ChatInterface } from './prompts/index.tsx';
|
||||
import { BaseApp } from './base/index.tsx';
|
||||
import { exampleUsage, markService } from './modules/mark-service.ts';
|
||||
@@ -201,7 +201,7 @@ export const App: React.FC = () => {
|
||||
<MuseApp />
|
||||
<ToastContainer
|
||||
position="top-right"
|
||||
autoClose={5000}
|
||||
autoClose={1000}
|
||||
hideProgressBar={false}
|
||||
newestOnTop={false}
|
||||
closeOnClick
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
@import 'tailwindcss';
|
||||
|
||||
.low-energy-spin {
|
||||
animation: 2.5s linear 0s infinite normal forwards running spin;
|
||||
}
|
||||
148
web/src/apps/muse/voice/modules/SettingModal.tsx
Normal file
148
web/src/apps/muse/voice/modules/SettingModal.tsx
Normal file
@@ -0,0 +1,148 @@
|
||||
import React from 'react';
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogDescription,
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
} from "../../../../components/ui/dialog";
|
||||
import { useSettingStore } from '../store/settingStore';
|
||||
import { PasswordInput } from '../../components/PasswordInput';
|
||||
import { X, RotateCcw } from 'lucide-react';
|
||||
|
||||
export const SettingModal: React.FC = () => {
|
||||
const {
|
||||
isOpen,
|
||||
autoRecognize,
|
||||
listen,
|
||||
volcengineAucAppId,
|
||||
volcengineAucToken,
|
||||
closeModal,
|
||||
setAutoRecognize,
|
||||
setListen,
|
||||
setVolcengineAucAppId,
|
||||
setVolcengineAucToken,
|
||||
resetToDefault,
|
||||
} = useSettingStore();
|
||||
|
||||
const handleClose = () => {
|
||||
closeModal();
|
||||
};
|
||||
|
||||
const handleReset = () => {
|
||||
if (window.confirm('确定要重置所有设置为默认值吗?')) {
|
||||
resetToDefault();
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Dialog open={isOpen} onOpenChange={handleClose} >
|
||||
<DialogContent className="max-w-md max-h-[80vh] overflow-y-auto" showCloseButton={false}>
|
||||
<DialogHeader>
|
||||
<div className="flex items-center justify-between">
|
||||
<DialogTitle>语音设置</DialogTitle>
|
||||
<div className="flex items-center space-x-2">
|
||||
<button
|
||||
onClick={handleClose}
|
||||
className="p-1 hover:bg-gray-100 rounded transition-colors cursor-pointer"
|
||||
>
|
||||
<X className="w-4 h-4 text-gray-500" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<DialogDescription>
|
||||
配置语音录制和识别相关设置
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
|
||||
<div className="space-y-6">
|
||||
{/* 语音识别设置 */}
|
||||
<div className="space-y-4">
|
||||
|
||||
|
||||
<div className="flex items-center justify-between">
|
||||
<label className="text-sm text-gray-700">自动识别语音</label>
|
||||
<div className="relative">
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={autoRecognize}
|
||||
onChange={(e) => setAutoRecognize(e.target.checked)}
|
||||
className="sr-only"
|
||||
/>
|
||||
<div
|
||||
onClick={() => setAutoRecognize(!autoRecognize)}
|
||||
className={`w-11 h-6 rounded-full cursor-pointer transition-colors ${autoRecognize ? 'bg-blue-600' : 'bg-gray-200'
|
||||
}`}
|
||||
>
|
||||
<div
|
||||
className={`w-5 h-5 bg-white rounded-full shadow-md transform transition-transform ${autoRecognize ? 'translate-x-5' : 'translate-x-0.5'
|
||||
} mt-0.5`}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center justify-between">
|
||||
<label className="text-sm text-gray-700">自动启动监听</label>
|
||||
<div className="relative">
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={listen}
|
||||
onChange={(e) => setListen(e.target.checked)}
|
||||
className="sr-only"
|
||||
/>
|
||||
<div
|
||||
onClick={() => setListen(!listen)}
|
||||
className={`w-11 h-6 rounded-full cursor-pointer transition-colors ${listen ? 'bg-blue-600' : 'bg-gray-200'
|
||||
}`}
|
||||
>
|
||||
<div
|
||||
className={`w-5 h-5 bg-white rounded-full shadow-md transform transition-transform ${listen ? 'translate-x-5' : 'translate-x-0.5'
|
||||
} mt-0.5`}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 火山引擎配置 */}
|
||||
<div className="space-y-4">
|
||||
<h3 className="text-sm font-medium text-gray-900">火山引擎配置</h3>
|
||||
|
||||
<div className="space-y-3">
|
||||
<div>
|
||||
<label className="block text-sm text-gray-700 mb-1">App ID</label>
|
||||
<input
|
||||
type="text"
|
||||
value={volcengineAucAppId}
|
||||
onChange={(e) => setVolcengineAucAppId(e.target.value)}
|
||||
placeholder="请输入火山引擎 App ID"
|
||||
className="w-full px-3 py-2 border border-gray-300 rounded-md text-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="block text-sm text-gray-700 mb-1">Token</label>
|
||||
<PasswordInput
|
||||
value={volcengineAucToken}
|
||||
onChange={setVolcengineAucToken}
|
||||
placeholder="请输入火山引擎 Token"
|
||||
className="w-full px-3 py-2 border border-gray-300 rounded-md text-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex justify-end space-x-3 pt-4 border-t">
|
||||
<button
|
||||
onClick={handleClose}
|
||||
className="px-4 py-2 text-sm font-medium text-gray-700 bg-gray-100 hover:bg-gray-200 rounded-md transition-colors"
|
||||
>
|
||||
关闭
|
||||
</button>
|
||||
</div>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
);
|
||||
};
|
||||
@@ -2,13 +2,26 @@ import { MicVAD, utils } from "@ricky0123/vad-web"
|
||||
import clsx from "clsx";
|
||||
import { useState, useEffect, useRef } from "react";
|
||||
import './style.css'
|
||||
import { MoreHorizontal, Play, Pause } from "lucide-react";
|
||||
import { MoreHorizontal, Play, Pause, Settings, FileAudio, StopCircle, Loader } from "lucide-react";
|
||||
import { Menu, MenuItem, MenuButton, } from '@szhsin/react-menu';
|
||||
import '@szhsin/react-menu/dist/index.css';
|
||||
import { toast } from 'react-toastify';
|
||||
import { Speak } from "./speak-db/speak";
|
||||
import { useVoiceStore } from "../store/voiceStore";
|
||||
import { useSettingStore } from "../store/settingStore";
|
||||
import { SettingModal } from "./SettingModal";
|
||||
|
||||
import {
|
||||
AlertDialog,
|
||||
AlertDialogAction,
|
||||
AlertDialogCancel,
|
||||
AlertDialogContent,
|
||||
AlertDialogDescription,
|
||||
AlertDialogFooter,
|
||||
AlertDialogHeader,
|
||||
AlertDialogTitle,
|
||||
AlertDialogTrigger,
|
||||
} from "../../../../components/ui/alert-dialog";
|
||||
|
||||
type VadVoiceProps = {
|
||||
data: Speak;
|
||||
@@ -136,19 +149,51 @@ const VoicePlayer = ({ data }: VadVoiceProps) => {
|
||||
<span>识别语音</span>
|
||||
</div>
|
||||
</MenuItem>
|
||||
<MenuItem onClick={handleDelete}>
|
||||
<div className="flex items-center space-x-2">
|
||||
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth={2}
|
||||
d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1-1H8a1 1 0 00-1 1v3M4 7h16"
|
||||
/>
|
||||
</svg>
|
||||
<span>删除</span>
|
||||
</div>
|
||||
</MenuItem>
|
||||
{data.text && data.text.trim() ? (
|
||||
<AlertDialog>
|
||||
<AlertDialogTrigger asChild>
|
||||
<MenuItem>
|
||||
<div className="flex items-center space-x-2">
|
||||
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth={2}
|
||||
d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1-1H8a1 1 0 00-1 1v3M4 7h16"
|
||||
/>
|
||||
</svg>
|
||||
<span>删除</span>
|
||||
</div>
|
||||
</MenuItem>
|
||||
</AlertDialogTrigger>
|
||||
<AlertDialogContent>
|
||||
<AlertDialogHeader>
|
||||
<AlertDialogTitle>确认删除</AlertDialogTitle>
|
||||
<AlertDialogDescription>
|
||||
此语音包含文字内容:"{data.text}"。删除后将无法恢复,确定要删除吗?
|
||||
</AlertDialogDescription>
|
||||
</AlertDialogHeader>
|
||||
<AlertDialogFooter>
|
||||
<AlertDialogCancel>取消</AlertDialogCancel>
|
||||
<AlertDialogAction onClick={handleDelete}>确认删除</AlertDialogAction>
|
||||
</AlertDialogFooter>
|
||||
</AlertDialogContent>
|
||||
</AlertDialog>
|
||||
) : (
|
||||
<MenuItem onClick={handleDelete}>
|
||||
<div className="flex items-center space-x-2">
|
||||
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth={2}
|
||||
d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1-1H8a1 1 0 00-1 1v3M4 7h16"
|
||||
/>
|
||||
</svg>
|
||||
<span>删除</span>
|
||||
</div>
|
||||
</MenuItem>
|
||||
)}
|
||||
</Menu>
|
||||
{/* 播放/暂停按钮 */}
|
||||
{!isPlaying ? (
|
||||
@@ -187,11 +232,92 @@ export const ShowVoicePlayer = ({ data }: { data: Speak[] }) => {
|
||||
<VoicePlayer data={item} />
|
||||
</div>
|
||||
<div className="flex-1 min-w-0">
|
||||
<div className="text-xs text-gray-400 truncate">
|
||||
{new Date(item.timestamp).toLocaleTimeString()}
|
||||
</div>
|
||||
<div className="text-xs text-gray-300">
|
||||
#{item.no}
|
||||
<div className="flex items-center justify-between space-x-2">
|
||||
<div>
|
||||
<div className="text-xs text-gray-400 truncate">
|
||||
{new Date(item.timestamp).toLocaleTimeString()}
|
||||
</div>
|
||||
<div className="text-xs text-gray-300">
|
||||
#{item.no}
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-center space-x-1">
|
||||
{item.text && item.text.trim() ? (
|
||||
<AlertDialog>
|
||||
<AlertDialogTrigger asChild>
|
||||
<button
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
className="w-5 h-5 hover:bg-red-100 rounded flex items-center justify-center text-red-500 transition-colors cursor-pointer"
|
||||
title="删除"
|
||||
>
|
||||
<svg className="w-3 h-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth={2}
|
||||
d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1-1H8a1 1 0 00-1 1v3M4 7h16"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
</AlertDialogTrigger>
|
||||
<AlertDialogContent>
|
||||
<AlertDialogHeader>
|
||||
<AlertDialogTitle>确认删除</AlertDialogTitle>
|
||||
<AlertDialogDescription>
|
||||
此语音包含文字内容:"{item.text}"。删除后将无法恢复,确定要删除吗?
|
||||
</AlertDialogDescription>
|
||||
</AlertDialogHeader>
|
||||
<AlertDialogFooter>
|
||||
<AlertDialogCancel>取消</AlertDialogCancel>
|
||||
<AlertDialogAction
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
const { deleteVoice } = useVoiceStore.getState();
|
||||
deleteVoice(item.id)
|
||||
.then(() => {
|
||||
console.log('语音记录删除成功');
|
||||
toast.success('删除成功', { autoClose: 200 });
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error('删除语音记录失败:', error);
|
||||
toast.error('删除失败: ' + (error instanceof Error ? error.message : '未知错误'));
|
||||
});
|
||||
}}
|
||||
>
|
||||
确认删除
|
||||
</AlertDialogAction>
|
||||
</AlertDialogFooter>
|
||||
</AlertDialogContent>
|
||||
</AlertDialog>
|
||||
) : (
|
||||
<button
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
const { deleteVoice } = useVoiceStore.getState();
|
||||
deleteVoice(item.id)
|
||||
.then(() => {
|
||||
console.log('语音记录删除成功');
|
||||
toast.success('删除成功', { autoClose: 200 });
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error('删除语音记录失败:', error);
|
||||
toast.error('删除失败: ' + (error instanceof Error ? error.message : '未知错误'));
|
||||
});
|
||||
}}
|
||||
className="w-5 h-5 hover:bg-red-100 rounded flex items-center justify-center text-red-500 transition-colors cursor-pointer"
|
||||
title="删除"
|
||||
>
|
||||
<svg className="w-3 h-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth={2}
|
||||
d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1-1H8a1 1 0 00-1 1v3M4 7h16"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
{item.text && (
|
||||
<div
|
||||
@@ -230,7 +356,15 @@ export const VadVoice = () => {
|
||||
setError: setStoreError
|
||||
} = useVoiceStore();
|
||||
|
||||
const [listen, setListen] = useState<boolean>(false);
|
||||
// 使用设置 store
|
||||
const {
|
||||
openModal: openSettingModal,
|
||||
listen,
|
||||
setListen,
|
||||
autoRecognize,
|
||||
setAutoRecognize
|
||||
} = useSettingStore();
|
||||
|
||||
const [vadStatus, setVadStatus] = useState<'idle' | 'initializing' | 'ready' | 'error'>('idle');
|
||||
const [realListen, setRealListen] = useState<boolean>(false);
|
||||
const [errorMessage, setErrorMessage] = useState<string>('');
|
||||
@@ -238,7 +372,8 @@ export const VadVoice = () => {
|
||||
const ref = useRef<MicVAD | null>(null);
|
||||
const initializingRef = useRef<boolean>(false);
|
||||
|
||||
async function initializeVAD() {
|
||||
async function initializeVAD(ls: boolean = true) {
|
||||
if (!ls) { return }
|
||||
if (ref.current || initializingRef.current) return;
|
||||
|
||||
initializingRef.current = true;
|
||||
@@ -247,7 +382,6 @@ export const VadVoice = () => {
|
||||
|
||||
try {
|
||||
console.log('Starting VAD initialization...');
|
||||
|
||||
// 添加延迟确保资源加载完成
|
||||
await new Promise((resolve) => setTimeout(resolve, 500));
|
||||
|
||||
@@ -310,7 +444,7 @@ export const VadVoice = () => {
|
||||
const handleUserInteraction = async () => {
|
||||
if (!userInteracted) {
|
||||
setUserInteracted(true);
|
||||
await initializeVAD();
|
||||
// await initializeVAD();
|
||||
}
|
||||
};
|
||||
|
||||
@@ -318,7 +452,6 @@ export const VadVoice = () => {
|
||||
useEffect(() => {
|
||||
initializeStore();
|
||||
}, [initializeStore]);
|
||||
|
||||
useEffect(() => {
|
||||
// 页面加载时不自动初始化,等待用户交互
|
||||
const handleFirstClick = () => {
|
||||
@@ -329,16 +462,23 @@ export const VadVoice = () => {
|
||||
};
|
||||
|
||||
document.addEventListener('click', handleFirstClick);
|
||||
// handleUserInteraction()
|
||||
return () => {
|
||||
document.removeEventListener('click', handleFirstClick);
|
||||
// 清理 VAD 资源
|
||||
if (ref.current) {
|
||||
ref.current.destroy();
|
||||
ref.current = null;
|
||||
}
|
||||
};
|
||||
}, [])
|
||||
useEffect(() => {
|
||||
if (!userInteracted) {
|
||||
return
|
||||
}
|
||||
console.log('VadVoice listen changed:', listen, userInteracted);
|
||||
initializeVAD(listen);
|
||||
return () => {
|
||||
ref.current?.destroy?.();
|
||||
ref.current = null;
|
||||
setVadStatus('idle');
|
||||
}
|
||||
|
||||
}, [listen, userInteracted]);
|
||||
const close = () => {
|
||||
if (ref.current) {
|
||||
ref.current.destroy();
|
||||
@@ -369,20 +509,26 @@ export const VadVoice = () => {
|
||||
return <div className="h-full flex flex-col">
|
||||
{/* Audio Recordings List */}
|
||||
<div className="flex-1 overflow-y-auto px-2 py-3 min-h-0 max-h-200">
|
||||
{!userInteracted && vadStatus === 'idle' ? (
|
||||
<div className="text-center text-gray-400 text-sm py-8">
|
||||
{
|
||||
voiceList.length === 0 ? (
|
||||
<div className="text-center text-gray-400 text-sm py-8">
|
||||
<div className="mb-2">🎤</div>
|
||||
<div>No recordings yet</div>
|
||||
<div className="text-xs mt-1">Start talking to record</div>
|
||||
</div>
|
||||
) : (
|
||||
<ShowVoicePlayer data={voiceList} />
|
||||
)
|
||||
}
|
||||
{!userInteracted && vadStatus === 'idle' && listen && (
|
||||
<div className="text-center text-gray-400 text-sm py-8" onClick={() => {
|
||||
setUserInteracted(true);
|
||||
initializeVAD(listen)
|
||||
}}>
|
||||
<div className="mb-2">🎤</div>
|
||||
<div>Click anywhere to initialize microphone</div>
|
||||
<div className="text-xs mt-1">Browser requires user interaction for microphone access</div>
|
||||
</div>
|
||||
) : voiceList.length === 0 ? (
|
||||
<div className="text-center text-gray-400 text-sm py-8">
|
||||
<div className="mb-2">🎤</div>
|
||||
<div>No recordings yet</div>
|
||||
<div className="text-xs mt-1">Start talking to record</div>
|
||||
</div>
|
||||
) : (
|
||||
<ShowVoicePlayer data={voiceList} />
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -401,7 +547,7 @@ export const VadVoice = () => {
|
||||
<div className="absolute -top-1 -right-1 w-3 h-3 bg-blue-500 rounded-full animate-pulse"></div>
|
||||
)}
|
||||
|
||||
{realListen && (
|
||||
{listen && realListen && (
|
||||
<div className="absolute -top-1 -right-1 w-3 h-3 bg-red-500 rounded-full animate-pulse"></div>
|
||||
)}
|
||||
</div>
|
||||
@@ -433,17 +579,41 @@ export const VadVoice = () => {
|
||||
onClick={handleStartStop}
|
||||
disabled={vadStatus === 'initializing'}
|
||||
className={clsx(
|
||||
"px-3 py-1.5 text-xs font-medium rounded-md transition-colors",
|
||||
"w-8 h-8 text-xs font-medium rounded-full flex items-center justify-center transition-colors cursor-pointer",
|
||||
vadStatus === 'initializing' && "opacity-50 cursor-not-allowed",
|
||||
listen
|
||||
? "bg-red-100 text-red-700 hover:bg-red-200"
|
||||
: "bg-green-100 text-green-700 hover:bg-green-200"
|
||||
)}
|
||||
>
|
||||
{vadStatus === 'initializing' ? 'Initializing...' : (listen ? 'Stop' : 'Start')}
|
||||
{vadStatus === 'initializing' ? <Loader className="w-4 h-4 inline-block animate-spin" /> : (listen ? <StopCircle className="w-4 h-4 inline-block" /> :
|
||||
<Play className="w-4 h-4 inline-block" />
|
||||
)}
|
||||
</button>
|
||||
<button onClick={() => {
|
||||
const newStatus = !autoRecognize;
|
||||
setAutoRecognize(newStatus);
|
||||
}}
|
||||
className={clsx(
|
||||
"w-8 h-8 hover:bg-gray-200 rounded-full flex items-center justify-center text-gray-700 transition-colors cursor-pointer",
|
||||
{ "bg-blue-200": autoRecognize }
|
||||
)}
|
||||
|
||||
title={autoRecognize ? '自动转文字中' : '转文字禁用中'}>
|
||||
<FileAudio className="w-4 h-4" />
|
||||
</button>
|
||||
<button
|
||||
onClick={openSettingModal}
|
||||
className="w-8 h-8 hover:bg-gray-200 rounded-full flex items-center justify-center text-gray-700 transition-colors cursor-pointer"
|
||||
title="设置"
|
||||
>
|
||||
<Settings className="w-4 h-4" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 设置弹窗 */}
|
||||
<SettingModal />
|
||||
</div >
|
||||
}
|
||||
@@ -10,7 +10,9 @@ export const getConfig = () => {
|
||||
};
|
||||
|
||||
return {
|
||||
// 火山引擎APPID
|
||||
VOLCENGINE_AUC_APPID: getFromLocalStorage('VOLCENGINE_AUC_APPID', ''),
|
||||
// 火山引擎Access Token
|
||||
VOLCENGINE_AUC_TOKEN: getFromLocalStorage('VOLCENGINE_AUC_TOKEN', ''),
|
||||
};
|
||||
};
|
||||
58
web/src/apps/muse/voice/modules/style.css
Normal file
58
web/src/apps/muse/voice/modules/style.css
Normal file
@@ -0,0 +1,58 @@
|
||||
@import 'tailwindcss';
|
||||
|
||||
.low-energy-spin {
|
||||
animation: 2.5s linear 0s infinite normal forwards running spin;
|
||||
}
|
||||
|
||||
/* 自定义滑块样式 */
|
||||
.slider {
|
||||
-webkit-appearance: none;
|
||||
appearance: none;
|
||||
background: transparent;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.slider::-webkit-slider-track {
|
||||
background: #e5e7eb;
|
||||
height: 8px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.slider::-webkit-slider-thumb {
|
||||
-webkit-appearance: none;
|
||||
appearance: none;
|
||||
background: #3b82f6;
|
||||
height: 20px;
|
||||
width: 20px;
|
||||
border-radius: 50%;
|
||||
cursor: pointer;
|
||||
margin-top: -6px;
|
||||
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
|
||||
transition: background-color 0.2s;
|
||||
}
|
||||
|
||||
.slider::-webkit-slider-thumb:hover {
|
||||
background: #2563eb;
|
||||
}
|
||||
|
||||
.slider::-moz-range-track {
|
||||
background: #e5e7eb;
|
||||
height: 8px;
|
||||
border-radius: 4px;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.slider::-moz-range-thumb {
|
||||
background: #3b82f6;
|
||||
height: 20px;
|
||||
width: 20px;
|
||||
border-radius: 50%;
|
||||
cursor: pointer;
|
||||
border: none;
|
||||
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
|
||||
transition: background-color 0.2s;
|
||||
}
|
||||
|
||||
.slider::-moz-range-thumb:hover {
|
||||
background: #2563eb;
|
||||
}
|
||||
133
web/src/apps/muse/voice/store/settingStore.ts
Normal file
133
web/src/apps/muse/voice/store/settingStore.ts
Normal file
@@ -0,0 +1,133 @@
|
||||
import { create } from 'zustand';
|
||||
import { persist } from 'zustand/middleware';
|
||||
|
||||
interface SettingState {
|
||||
// 弹窗状态
|
||||
isOpen: boolean;
|
||||
mount: boolean;
|
||||
// 语音设置
|
||||
autoRecognize: boolean;
|
||||
listen: boolean;
|
||||
recognitionLanguage: string;
|
||||
|
||||
// 火山引擎配置
|
||||
volcengineAucAppId: string;
|
||||
volcengineAucToken: string;
|
||||
|
||||
// 操作方法
|
||||
openModal: () => void;
|
||||
closeModal: () => void;
|
||||
setAutoRecognize: (value: boolean) => void;
|
||||
setListen: (value: boolean) => void;
|
||||
setRecognitionLanguage: (language: string) => void;
|
||||
setVolcengineAucAppId: (appId: string) => void;
|
||||
setVolcengineAucToken: (token: string) => void;
|
||||
resetToDefault: () => void;
|
||||
}
|
||||
|
||||
const defaultSettings = {
|
||||
autoRecognize: false,
|
||||
listen: false,
|
||||
recognitionLanguage: 'zh-CN',
|
||||
volcengineAucAppId: '',
|
||||
volcengineAucToken: '',
|
||||
};
|
||||
|
||||
// 从原有的 localStorage key 读取初始值
|
||||
const getInitialVolcengineConfig = () => {
|
||||
try {
|
||||
return {
|
||||
volcengineAucAppId: localStorage.getItem('VOLCENGINE_AUC_APPID') || '',
|
||||
volcengineAucToken: localStorage.getItem('VOLCENGINE_AUC_TOKEN') || '',
|
||||
};
|
||||
} catch (error) {
|
||||
console.warn('Failed to read volcengine config from localStorage:', error);
|
||||
return {
|
||||
volcengineAucAppId: '',
|
||||
volcengineAucToken: '',
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
export const useSettingStore = create<SettingState>()(
|
||||
persist(
|
||||
(set, get) => ({
|
||||
// 初始状态 - 合并默认设置和从 localStorage 读取的火山引擎配置
|
||||
isOpen: false,
|
||||
...defaultSettings,
|
||||
...getInitialVolcengineConfig(),
|
||||
mount: false,
|
||||
// 弹窗控制方法
|
||||
openModal: () => set({ isOpen: true }),
|
||||
closeModal: () => set({ isOpen: false }),
|
||||
|
||||
// 设置更新方法
|
||||
setAutoRecognize: (value: boolean) => set({ autoRecognize: value }),
|
||||
setListen: (value: boolean) => set({ listen: value }),
|
||||
setRecognitionLanguage: (language: string) => set({ recognitionLanguage: language }),
|
||||
setVolcengineAucAppId: (appId: string) => {
|
||||
// 同时更新 zustand 状态和原有的 localStorage key
|
||||
try {
|
||||
localStorage.setItem('VOLCENGINE_AUC_APPID', appId);
|
||||
} catch (error) {
|
||||
console.warn('Failed to save VOLCENGINE_AUC_APPID to localStorage:', error);
|
||||
}
|
||||
set({ volcengineAucAppId: appId });
|
||||
},
|
||||
setVolcengineAucToken: (token: string) => {
|
||||
// 同时更新 zustand 状态和原有的 localStorage key
|
||||
try {
|
||||
localStorage.setItem('VOLCENGINE_AUC_TOKEN', token);
|
||||
} catch (error) {
|
||||
console.warn('Failed to save VOLCENGINE_AUC_TOKEN to localStorage:', error);
|
||||
}
|
||||
set({ volcengineAucToken: token });
|
||||
},
|
||||
|
||||
// 重置为默认设置
|
||||
resetToDefault: () => {
|
||||
try {
|
||||
localStorage.removeItem('VOLCENGINE_AUC_APPID');
|
||||
localStorage.removeItem('VOLCENGINE_AUC_TOKEN');
|
||||
} catch (error) {
|
||||
console.warn('Failed to remove volcengine config from localStorage:', error);
|
||||
}
|
||||
set({
|
||||
...defaultSettings,
|
||||
volcengineAucAppId: '',
|
||||
volcengineAucToken: '',
|
||||
});
|
||||
},
|
||||
}),
|
||||
{
|
||||
name: 'voice-settings',
|
||||
partialize: (state) => ({
|
||||
autoRecognize: state.autoRecognize,
|
||||
listen: state.listen,
|
||||
recognitionLanguage: state.recognitionLanguage,
|
||||
mount: true,
|
||||
// 火山引擎配置不通过 zustand persist 保存,而是直接使用原有的 localStorage key
|
||||
}),
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
// 兼容原有 config.ts 的 API
|
||||
export const getConfig = () => {
|
||||
const state = useSettingStore.getState();
|
||||
return {
|
||||
VOLCENGINE_AUC_APPID: state.volcengineAucAppId,
|
||||
VOLCENGINE_AUC_TOKEN: state.volcengineAucToken,
|
||||
};
|
||||
};
|
||||
|
||||
export const setConfig = (config: { VOLCENGINE_AUC_APPID?: string; VOLCENGINE_AUC_TOKEN?: string }) => {
|
||||
const { setVolcengineAucAppId, setVolcengineAucToken } = useSettingStore.getState();
|
||||
|
||||
if (config.VOLCENGINE_AUC_APPID !== undefined) {
|
||||
setVolcengineAucAppId(config.VOLCENGINE_AUC_APPID);
|
||||
}
|
||||
if (config.VOLCENGINE_AUC_TOKEN !== undefined) {
|
||||
setVolcengineAucToken(config.VOLCENGINE_AUC_TOKEN);
|
||||
}
|
||||
};
|
||||
@@ -3,6 +3,7 @@ import { devtools, persist } from 'zustand/middleware';
|
||||
import { Speak, getDayOfYear, CreateSpeakData } from '../modules/speak-db/speak';
|
||||
import { speakService } from '../modules/speak-db/speak-service';
|
||||
import { getText } from '../modules/text';
|
||||
import { useSettingStore } from './settingStore';
|
||||
|
||||
interface VoiceState {
|
||||
// 状态数据
|
||||
@@ -105,7 +106,7 @@ export const useVoiceStore = create<VoiceState>()(
|
||||
// 添加新的语音记录
|
||||
addVoice: async (url: string, duration: number, audioBlob?: Blob) => {
|
||||
const { setError } = get();
|
||||
|
||||
const autoRecognize = useSettingStore.getState().autoRecognize;
|
||||
try {
|
||||
setError(null);
|
||||
|
||||
@@ -123,8 +124,12 @@ export const useVoiceStore = create<VoiceState>()(
|
||||
day: getDayOfYear(),
|
||||
no: 0, // 将由 service 自动生成
|
||||
timestamp: Date.now(),
|
||||
type: 'normal' as const
|
||||
type: 'normal' as const,
|
||||
text: '', // 初始为空
|
||||
};
|
||||
if (autoRecognize) {
|
||||
speakData.text = await getText(fileData || '').then(res => res.text);
|
||||
}
|
||||
|
||||
// 保存到 IndexedDB(不包含 url)
|
||||
const newSpeak = await speakService.createSpeakAuto(speakData);
|
||||
155
web/src/components/ui/alert-dialog.tsx
Normal file
155
web/src/components/ui/alert-dialog.tsx
Normal file
@@ -0,0 +1,155 @@
|
||||
import * as React from "react"
|
||||
import * as AlertDialogPrimitive from "@radix-ui/react-alert-dialog"
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
import { buttonVariants } from "@/components/ui/button"
|
||||
|
||||
function AlertDialog({
|
||||
...props
|
||||
}: React.ComponentProps<typeof AlertDialogPrimitive.Root>) {
|
||||
return <AlertDialogPrimitive.Root data-slot="alert-dialog" {...props} />
|
||||
}
|
||||
|
||||
function AlertDialogTrigger({
|
||||
...props
|
||||
}: React.ComponentProps<typeof AlertDialogPrimitive.Trigger>) {
|
||||
return (
|
||||
<AlertDialogPrimitive.Trigger data-slot="alert-dialog-trigger" {...props} />
|
||||
)
|
||||
}
|
||||
|
||||
function AlertDialogPortal({
|
||||
...props
|
||||
}: React.ComponentProps<typeof AlertDialogPrimitive.Portal>) {
|
||||
return (
|
||||
<AlertDialogPrimitive.Portal data-slot="alert-dialog-portal" {...props} />
|
||||
)
|
||||
}
|
||||
|
||||
function AlertDialogOverlay({
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<typeof AlertDialogPrimitive.Overlay>) {
|
||||
return (
|
||||
<AlertDialogPrimitive.Overlay
|
||||
data-slot="alert-dialog-overlay"
|
||||
className={cn(
|
||||
"data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/50",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function AlertDialogContent({
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<typeof AlertDialogPrimitive.Content>) {
|
||||
return (
|
||||
<AlertDialogPortal>
|
||||
<AlertDialogOverlay />
|
||||
<AlertDialogPrimitive.Content
|
||||
data-slot="alert-dialog-content"
|
||||
className={cn(
|
||||
"bg-background data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 fixed top-[50%] left-[50%] z-50 grid w-full max-w-[calc(100%-2rem)] translate-x-[-50%] translate-y-[-50%] gap-4 rounded-lg border p-6 shadow-lg duration-200 sm:max-w-lg",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
</AlertDialogPortal>
|
||||
)
|
||||
}
|
||||
|
||||
function AlertDialogHeader({
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<"div">) {
|
||||
return (
|
||||
<div
|
||||
data-slot="alert-dialog-header"
|
||||
className={cn("flex flex-col gap-2 text-center sm:text-left", className)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function AlertDialogFooter({
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<"div">) {
|
||||
return (
|
||||
<div
|
||||
data-slot="alert-dialog-footer"
|
||||
className={cn(
|
||||
"flex flex-col-reverse gap-2 sm:flex-row sm:justify-end",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function AlertDialogTitle({
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<typeof AlertDialogPrimitive.Title>) {
|
||||
return (
|
||||
<AlertDialogPrimitive.Title
|
||||
data-slot="alert-dialog-title"
|
||||
className={cn("text-lg font-semibold", className)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function AlertDialogDescription({
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<typeof AlertDialogPrimitive.Description>) {
|
||||
return (
|
||||
<AlertDialogPrimitive.Description
|
||||
data-slot="alert-dialog-description"
|
||||
className={cn("text-muted-foreground text-sm", className)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function AlertDialogAction({
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<typeof AlertDialogPrimitive.Action>) {
|
||||
return (
|
||||
<AlertDialogPrimitive.Action
|
||||
className={cn(buttonVariants(), className)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function AlertDialogCancel({
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<typeof AlertDialogPrimitive.Cancel>) {
|
||||
return (
|
||||
<AlertDialogPrimitive.Cancel
|
||||
className={cn(buttonVariants({ variant: "outline" }), className)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
export {
|
||||
AlertDialog,
|
||||
AlertDialogPortal,
|
||||
AlertDialogOverlay,
|
||||
AlertDialogTrigger,
|
||||
AlertDialogContent,
|
||||
AlertDialogHeader,
|
||||
AlertDialogFooter,
|
||||
AlertDialogTitle,
|
||||
AlertDialogDescription,
|
||||
AlertDialogAction,
|
||||
AlertDialogCancel,
|
||||
}
|
||||
66
web/src/components/ui/alert.tsx
Normal file
66
web/src/components/ui/alert.tsx
Normal file
@@ -0,0 +1,66 @@
|
||||
import * as React from "react"
|
||||
import { cva, type VariantProps } from "class-variance-authority"
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
|
||||
const alertVariants = cva(
|
||||
"relative w-full rounded-lg border px-4 py-3 text-sm grid has-[>svg]:grid-cols-[calc(var(--spacing)*4)_1fr] grid-cols-[0_1fr] has-[>svg]:gap-x-3 gap-y-0.5 items-start [&>svg]:size-4 [&>svg]:translate-y-0.5 [&>svg]:text-current",
|
||||
{
|
||||
variants: {
|
||||
variant: {
|
||||
default: "bg-card text-card-foreground",
|
||||
destructive:
|
||||
"text-destructive bg-card [&>svg]:text-current *:data-[slot=alert-description]:text-destructive/90",
|
||||
},
|
||||
},
|
||||
defaultVariants: {
|
||||
variant: "default",
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
function Alert({
|
||||
className,
|
||||
variant,
|
||||
...props
|
||||
}: React.ComponentProps<"div"> & VariantProps<typeof alertVariants>) {
|
||||
return (
|
||||
<div
|
||||
data-slot="alert"
|
||||
role="alert"
|
||||
className={cn(alertVariants({ variant }), className)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function AlertTitle({ className, ...props }: React.ComponentProps<"div">) {
|
||||
return (
|
||||
<div
|
||||
data-slot="alert-title"
|
||||
className={cn(
|
||||
"col-start-2 line-clamp-1 min-h-4 font-medium tracking-tight",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function AlertDescription({
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<"div">) {
|
||||
return (
|
||||
<div
|
||||
data-slot="alert-description"
|
||||
className={cn(
|
||||
"text-muted-foreground col-start-2 grid justify-items-start gap-1 text-sm [&_p]:leading-relaxed",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
export { Alert, AlertTitle, AlertDescription }
|
||||
60
web/src/components/ui/button.tsx
Normal file
60
web/src/components/ui/button.tsx
Normal file
@@ -0,0 +1,60 @@
|
||||
import * as React from "react"
|
||||
import { Slot } from "@radix-ui/react-slot"
|
||||
import { cva, type VariantProps } from "class-variance-authority"
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
|
||||
const buttonVariants = cva(
|
||||
"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
|
||||
{
|
||||
variants: {
|
||||
variant: {
|
||||
default: "bg-primary text-primary-foreground hover:bg-primary/90",
|
||||
destructive:
|
||||
"bg-destructive text-white hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60",
|
||||
outline:
|
||||
"border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50",
|
||||
secondary:
|
||||
"bg-secondary text-secondary-foreground hover:bg-secondary/80",
|
||||
ghost:
|
||||
"hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50",
|
||||
link: "text-primary underline-offset-4 hover:underline",
|
||||
},
|
||||
size: {
|
||||
default: "h-9 px-4 py-2 has-[>svg]:px-3",
|
||||
sm: "h-8 rounded-md gap-1.5 px-3 has-[>svg]:px-2.5",
|
||||
lg: "h-10 rounded-md px-6 has-[>svg]:px-4",
|
||||
icon: "size-9",
|
||||
"icon-sm": "size-8",
|
||||
"icon-lg": "size-10",
|
||||
},
|
||||
},
|
||||
defaultVariants: {
|
||||
variant: "default",
|
||||
size: "default",
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
function Button({
|
||||
className,
|
||||
variant,
|
||||
size,
|
||||
asChild = false,
|
||||
...props
|
||||
}: React.ComponentProps<"button"> &
|
||||
VariantProps<typeof buttonVariants> & {
|
||||
asChild?: boolean
|
||||
}) {
|
||||
const Comp = asChild ? Slot : "button"
|
||||
|
||||
return (
|
||||
<Comp
|
||||
data-slot="button"
|
||||
className={cn(buttonVariants({ variant, size, className }))}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
export { Button, buttonVariants }
|
||||
141
web/src/components/ui/dialog.tsx
Normal file
141
web/src/components/ui/dialog.tsx
Normal file
@@ -0,0 +1,141 @@
|
||||
import * as React from "react"
|
||||
import * as DialogPrimitive from "@radix-ui/react-dialog"
|
||||
import { XIcon } from "lucide-react"
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
|
||||
function Dialog({
|
||||
...props
|
||||
}: React.ComponentProps<typeof DialogPrimitive.Root>) {
|
||||
return <DialogPrimitive.Root data-slot="dialog" {...props} />
|
||||
}
|
||||
|
||||
function DialogTrigger({
|
||||
...props
|
||||
}: React.ComponentProps<typeof DialogPrimitive.Trigger>) {
|
||||
return <DialogPrimitive.Trigger data-slot="dialog-trigger" {...props} />
|
||||
}
|
||||
|
||||
function DialogPortal({
|
||||
...props
|
||||
}: React.ComponentProps<typeof DialogPrimitive.Portal>) {
|
||||
return <DialogPrimitive.Portal data-slot="dialog-portal" {...props} />
|
||||
}
|
||||
|
||||
function DialogClose({
|
||||
...props
|
||||
}: React.ComponentProps<typeof DialogPrimitive.Close>) {
|
||||
return <DialogPrimitive.Close data-slot="dialog-close" {...props} />
|
||||
}
|
||||
|
||||
function DialogOverlay({
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<typeof DialogPrimitive.Overlay>) {
|
||||
return (
|
||||
<DialogPrimitive.Overlay
|
||||
data-slot="dialog-overlay"
|
||||
className={cn(
|
||||
"data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/50",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function DialogContent({
|
||||
className,
|
||||
children,
|
||||
showCloseButton = true,
|
||||
...props
|
||||
}: React.ComponentProps<typeof DialogPrimitive.Content> & {
|
||||
showCloseButton?: boolean
|
||||
}) {
|
||||
return (
|
||||
<DialogPortal data-slot="dialog-portal">
|
||||
<DialogOverlay />
|
||||
<DialogPrimitive.Content
|
||||
data-slot="dialog-content"
|
||||
className={cn(
|
||||
"bg-background data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 fixed top-[50%] left-[50%] z-50 grid w-full max-w-[calc(100%-2rem)] translate-x-[-50%] translate-y-[-50%] gap-4 rounded-lg border p-6 shadow-lg duration-200 sm:max-w-lg",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
{showCloseButton && (
|
||||
<DialogPrimitive.Close
|
||||
data-slot="dialog-close"
|
||||
className="ring-offset-background focus:ring-ring data-[state=open]:bg-accent data-[state=open]:text-muted-foreground absolute top-4 right-4 rounded-xs opacity-70 transition-opacity hover:opacity-100 focus:ring-2 focus:ring-offset-2 focus:outline-hidden disabled:pointer-events-none [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4"
|
||||
>
|
||||
<XIcon />
|
||||
<span className="sr-only">Close</span>
|
||||
</DialogPrimitive.Close>
|
||||
)}
|
||||
</DialogPrimitive.Content>
|
||||
</DialogPortal>
|
||||
)
|
||||
}
|
||||
|
||||
function DialogHeader({ className, ...props }: React.ComponentProps<"div">) {
|
||||
return (
|
||||
<div
|
||||
data-slot="dialog-header"
|
||||
className={cn("flex flex-col gap-2 text-center sm:text-left", className)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function DialogFooter({ className, ...props }: React.ComponentProps<"div">) {
|
||||
return (
|
||||
<div
|
||||
data-slot="dialog-footer"
|
||||
className={cn(
|
||||
"flex flex-col-reverse gap-2 sm:flex-row sm:justify-end",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function DialogTitle({
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<typeof DialogPrimitive.Title>) {
|
||||
return (
|
||||
<DialogPrimitive.Title
|
||||
data-slot="dialog-title"
|
||||
className={cn("text-lg leading-none font-semibold", className)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function DialogDescription({
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<typeof DialogPrimitive.Description>) {
|
||||
return (
|
||||
<DialogPrimitive.Description
|
||||
data-slot="dialog-description"
|
||||
className={cn("text-muted-foreground text-sm", className)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
export {
|
||||
Dialog,
|
||||
DialogClose,
|
||||
DialogContent,
|
||||
DialogDescription,
|
||||
DialogFooter,
|
||||
DialogHeader,
|
||||
DialogOverlay,
|
||||
DialogPortal,
|
||||
DialogTitle,
|
||||
DialogTrigger,
|
||||
}
|
||||
Reference in New Issue
Block a user