update 优化录音功能模块的代码和火山模块
This commit is contained in:
		
							
								
								
									
										552
									
								
								pnpm-lock.yaml
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										552
									
								
								pnpm-lock.yaml
									
									
									
										generated
									
									
									
								
							@@ -75,6 +75,15 @@ importers:
 | 
				
			|||||||
      '@kevisual/registry':
 | 
					      '@kevisual/registry':
 | 
				
			||||||
        specifier: ^0.0.1
 | 
					        specifier: ^0.0.1
 | 
				
			||||||
        version: 0.0.1(typescript@5.9.3)
 | 
					        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':
 | 
					      '@ricky0123/vad-web':
 | 
				
			||||||
        specifier: ^0.0.28
 | 
					        specifier: ^0.0.28
 | 
				
			||||||
        version: 0.0.28
 | 
					        version: 0.0.28
 | 
				
			||||||
@@ -84,6 +93,12 @@ importers:
 | 
				
			|||||||
      '@tailwindcss/vite':
 | 
					      '@tailwindcss/vite':
 | 
				
			||||||
        specifier: ^4.1.14
 | 
					        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))
 | 
					        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:
 | 
					      astro:
 | 
				
			||||||
        specifier: ^5.14.4
 | 
					        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)
 | 
					        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
 | 
					        version: 7.11.0
 | 
				
			||||||
      zustand:
 | 
					      zustand:
 | 
				
			||||||
        specifier: ^5.0.8
 | 
					        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:
 | 
					    devDependencies:
 | 
				
			||||||
      '@kevisual/types':
 | 
					      '@kevisual/types':
 | 
				
			||||||
        specifier: ^0.0.10
 | 
					        specifier: ^0.0.10
 | 
				
			||||||
@@ -760,6 +775,190 @@ packages:
 | 
				
			|||||||
  '@protobufjs/utf8@1.1.0':
 | 
					  '@protobufjs/utf8@1.1.0':
 | 
				
			||||||
    resolution: {integrity: sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==}
 | 
					    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':
 | 
					  '@ricky0123/vad-web@0.0.28':
 | 
				
			||||||
    resolution: {integrity: sha512-Hvw8jN3r1SBxmjJa89HITxRcwlT6dc7CQPVtVQLrqfY8EeQcx41QeqKUol4lw8ZCeAIHKwYndHnB1K/4SAQJgQ==}
 | 
					    resolution: {integrity: sha512-Hvw8jN3r1SBxmjJa89HITxRcwlT6dc7CQPVtVQLrqfY8EeQcx41QeqKUol4lw8ZCeAIHKwYndHnB1K/4SAQJgQ==}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -1051,6 +1250,39 @@ packages:
 | 
				
			|||||||
    peerDependencies:
 | 
					    peerDependencies:
 | 
				
			||||||
      vite: ^5.2.0 || ^6 || ^7
 | 
					      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':
 | 
					  '@tweenjs/tween.js@23.1.3':
 | 
				
			||||||
    resolution: {integrity: sha512-vJmvvwFxYuGnF2axRtPYocag6Clbb5YS7kLL+SO/TeVFzHqDIWrNKYtcsPMibjDx9O+bu+psAy9NKfWklassUA==}
 | 
					    resolution: {integrity: sha512-vJmvvwFxYuGnF2axRtPYocag6Clbb5YS7kLL+SO/TeVFzHqDIWrNKYtcsPMibjDx9O+bu+psAy9NKfWklassUA==}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -1263,6 +1495,10 @@ packages:
 | 
				
			|||||||
  argparse@2.0.1:
 | 
					  argparse@2.0.1:
 | 
				
			||||||
    resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==}
 | 
					    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:
 | 
					  aria-query@5.3.2:
 | 
				
			||||||
    resolution: {integrity: sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==}
 | 
					    resolution: {integrity: sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==}
 | 
				
			||||||
    engines: {node: '>= 0.4'}
 | 
					    engines: {node: '>= 0.4'}
 | 
				
			||||||
@@ -1439,6 +1675,9 @@ packages:
 | 
				
			|||||||
      supports-color:
 | 
					      supports-color:
 | 
				
			||||||
        optional: true
 | 
					        optional: true
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  decode-formdata@0.9.0:
 | 
				
			||||||
 | 
					    resolution: {integrity: sha512-q5uwOjR3Um5YD+ZWPOF/1sGHVW9A5rCrRwITQChRXlmPkxDFBqCm4jNTIVdGHNH9OnR+V9MoZVgRhsFb+ARbUw==}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  decode-named-character-reference@1.2.0:
 | 
					  decode-named-character-reference@1.2.0:
 | 
				
			||||||
    resolution: {integrity: sha512-c6fcElNV6ShtZXmsgNgFFV5tVX2PaV4g+MOAkb8eXHvn6sryJBrZa9r0zV6+dtTyoCKxtDy5tyQ5ZwQuidtd+Q==}
 | 
					    resolution: {integrity: sha512-c6fcElNV6ShtZXmsgNgFFV5tVX2PaV4g+MOAkb8eXHvn6sryJBrZa9r0zV6+dtTyoCKxtDy5tyQ5ZwQuidtd+Q==}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -1469,6 +1708,9 @@ packages:
 | 
				
			|||||||
    resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==}
 | 
					    resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==}
 | 
				
			||||||
    engines: {node: '>=8'}
 | 
					    engines: {node: '>=8'}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  detect-node-es@1.1.0:
 | 
				
			||||||
 | 
					    resolution: {integrity: sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  deterministic-object-hash@2.0.2:
 | 
					  deterministic-object-hash@2.0.2:
 | 
				
			||||||
    resolution: {integrity: sha512-KxektNH63SrbfUyDiwXqRb1rLwKt33AmMv+5Nhsw1kqZ13SJBRTgZHtGbE+hH3a1mVW1cz+4pqSWVPAtLVXTzQ==}
 | 
					    resolution: {integrity: sha512-KxektNH63SrbfUyDiwXqRb1rLwKt33AmMv+5Nhsw1kqZ13SJBRTgZHtGbE+hH3a1mVW1cz+4pqSWVPAtLVXTzQ==}
 | 
				
			||||||
    engines: {node: '>=18'}
 | 
					    engines: {node: '>=18'}
 | 
				
			||||||
@@ -1677,6 +1919,10 @@ packages:
 | 
				
			|||||||
    resolution: {integrity: sha512-QZjmEOC+IT1uk6Rx0sX22V6uHWVwbdbxf1faPqJ1QhLdGgsRGCZoyaQBm/piRdJy/D2um6hM1UP7ZEeQ4EkP+Q==}
 | 
					    resolution: {integrity: sha512-QZjmEOC+IT1uk6Rx0sX22V6uHWVwbdbxf1faPqJ1QhLdGgsRGCZoyaQBm/piRdJy/D2um6hM1UP7ZEeQ4EkP+Q==}
 | 
				
			||||||
    engines: {node: '>=18'}
 | 
					    engines: {node: '>=18'}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  get-nonce@1.0.1:
 | 
				
			||||||
 | 
					    resolution: {integrity: sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q==}
 | 
				
			||||||
 | 
					    engines: {node: '>=6'}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  github-slugger@2.0.0:
 | 
					  github-slugger@2.0.0:
 | 
				
			||||||
    resolution: {integrity: sha512-IaOQ9puYtjrkq7Y0Ygl9KDZnrf/aiUJYUpVf89y8kyaxbRG7Y1SrX/jaumrv81vc61+kiMempujsM3Yw7w5qcw==}
 | 
					    resolution: {integrity: sha512-IaOQ9puYtjrkq7Y0Ygl9KDZnrf/aiUJYUpVf89y8kyaxbRG7Y1SrX/jaumrv81vc61+kiMempujsM3Yw7w5qcw==}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -2555,12 +2801,42 @@ packages:
 | 
				
			|||||||
    resolution: {integrity: sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ==}
 | 
					    resolution: {integrity: sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ==}
 | 
				
			||||||
    engines: {node: '>=0.10.0'}
 | 
					    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:
 | 
					  react-resizable-panels@3.0.6:
 | 
				
			||||||
    resolution: {integrity: sha512-b3qKHQ3MLqOgSS+FRYKapNkJZf5EQzuf6+RLiq1/IlTHw99YrZ2NJZLk4hQIzTnnIkRg2LUqyVinu6YWWpUYew==}
 | 
					    resolution: {integrity: sha512-b3qKHQ3MLqOgSS+FRYKapNkJZf5EQzuf6+RLiq1/IlTHw99YrZ2NJZLk4hQIzTnnIkRg2LUqyVinu6YWWpUYew==}
 | 
				
			||||||
    peerDependencies:
 | 
					    peerDependencies:
 | 
				
			||||||
      react: ^16.14.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc
 | 
					      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-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:
 | 
					  react-toastify@11.0.5:
 | 
				
			||||||
    resolution: {integrity: sha512-EpqHBGvnSTtHYhCPLxML05NLY2ZX0JURbAdNYa6BUkk+amz4wbKBQvoKQAB0ardvSarUBuY4Q4s1sluAzZwkmA==}
 | 
					    resolution: {integrity: sha512-EpqHBGvnSTtHYhCPLxML05NLY2ZX0JURbAdNYa6BUkk+amz4wbKBQvoKQAB0ardvSarUBuY4Q4s1sluAzZwkmA==}
 | 
				
			||||||
    peerDependencies:
 | 
					    peerDependencies:
 | 
				
			||||||
@@ -3058,6 +3334,31 @@ packages:
 | 
				
			|||||||
  url-parse@1.5.10:
 | 
					  url-parse@1.5.10:
 | 
				
			||||||
    resolution: {integrity: sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==}
 | 
					    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:
 | 
					  util-deprecate@1.0.2:
 | 
				
			||||||
    resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==}
 | 
					    resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -3799,6 +4100,163 @@ snapshots:
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  '@protobufjs/utf8@1.1.0': {}
 | 
					  '@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':
 | 
					  '@ricky0123/vad-web@0.0.28':
 | 
				
			||||||
    dependencies:
 | 
					    dependencies:
 | 
				
			||||||
      onnxruntime-web: 1.23.0
 | 
					      onnxruntime-web: 1.23.0
 | 
				
			||||||
@@ -4024,6 +4482,39 @@ snapshots:
 | 
				
			|||||||
      tailwindcss: 4.1.14
 | 
					      tailwindcss: 4.1.14
 | 
				
			||||||
      vite: 6.3.7(@types/node@24.7.2)(jiti@2.6.1)(lightningcss@1.30.1)
 | 
					      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': {}
 | 
					  '@tweenjs/tween.js@23.1.3': {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  '@types/babel__core@7.20.5':
 | 
					  '@types/babel__core@7.20.5':
 | 
				
			||||||
@@ -4301,6 +4792,10 @@ snapshots:
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  argparse@2.0.1: {}
 | 
					  argparse@2.0.1: {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  aria-hidden@1.2.6:
 | 
				
			||||||
 | 
					    dependencies:
 | 
				
			||||||
 | 
					      tslib: 2.8.1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  aria-query@5.3.2: {}
 | 
					  aria-query@5.3.2: {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  array-iterate@2.0.1: {}
 | 
					  array-iterate@2.0.1: {}
 | 
				
			||||||
@@ -4539,6 +5034,8 @@ snapshots:
 | 
				
			|||||||
    dependencies:
 | 
					    dependencies:
 | 
				
			||||||
      ms: 2.1.3
 | 
					      ms: 2.1.3
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  decode-formdata@0.9.0: {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  decode-named-character-reference@1.2.0:
 | 
					  decode-named-character-reference@1.2.0:
 | 
				
			||||||
    dependencies:
 | 
					    dependencies:
 | 
				
			||||||
      character-entities: 2.0.2
 | 
					      character-entities: 2.0.2
 | 
				
			||||||
@@ -4560,6 +5057,8 @@ snapshots:
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  detect-libc@2.1.2: {}
 | 
					  detect-libc@2.1.2: {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  detect-node-es@1.1.0: {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  deterministic-object-hash@2.0.2:
 | 
					  deterministic-object-hash@2.0.2:
 | 
				
			||||||
    dependencies:
 | 
					    dependencies:
 | 
				
			||||||
      base-64: 1.0.0
 | 
					      base-64: 1.0.0
 | 
				
			||||||
@@ -4782,6 +5281,8 @@ snapshots:
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  get-east-asian-width@1.4.0: {}
 | 
					  get-east-asian-width@1.4.0: {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  get-nonce@1.0.1: {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  github-slugger@2.0.0: {}
 | 
					  github-slugger@2.0.0: {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  glob-parent@5.1.2:
 | 
					  glob-parent@5.1.2:
 | 
				
			||||||
@@ -6002,11 +6503,38 @@ snapshots:
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  react-refresh@0.17.0: {}
 | 
					  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):
 | 
					  react-resizable-panels@3.0.6(react-dom@19.2.0(react@19.2.0))(react@19.2.0):
 | 
				
			||||||
    dependencies:
 | 
					    dependencies:
 | 
				
			||||||
      react: 19.2.0
 | 
					      react: 19.2.0
 | 
				
			||||||
      react-dom: 19.2.0(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):
 | 
					  react-toastify@11.0.5(react-dom@19.2.0(react@19.2.0))(react@19.2.0):
 | 
				
			||||||
    dependencies:
 | 
					    dependencies:
 | 
				
			||||||
      clsx: 2.1.1
 | 
					      clsx: 2.1.1
 | 
				
			||||||
@@ -6597,6 +7125,25 @@ snapshots:
 | 
				
			|||||||
      querystringify: 2.2.0
 | 
					      querystringify: 2.2.0
 | 
				
			||||||
      requires-port: 1.0.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: {}
 | 
					  util-deprecate@1.0.2: {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  uuid@8.3.2: {}
 | 
					  uuid@8.3.2: {}
 | 
				
			||||||
@@ -6698,9 +7245,10 @@ snapshots:
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  zod@3.25.76: {}
 | 
					  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:
 | 
					    optionalDependencies:
 | 
				
			||||||
      '@types/react': 19.2.2
 | 
					      '@types/react': 19.2.2
 | 
				
			||||||
      react: 19.2.0
 | 
					      react: 19.2.0
 | 
				
			||||||
 | 
					      use-sync-external-store: 1.6.0(react@19.2.0)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  zwitch@2.0.4: {}
 | 
					  zwitch@2.0.4: {}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -9,6 +9,7 @@
 | 
				
			|||||||
    "build": "astro build",
 | 
					    "build": "astro build",
 | 
				
			||||||
    "preview": "astro preview",
 | 
					    "preview": "astro preview",
 | 
				
			||||||
    "pub": "envision deploy ./dist -k light-code-center -v 0.0.1 -u",
 | 
					    "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 "
 | 
					    "sn": "pnpm dlx shadcn@latest add "
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  "keywords": [],
 | 
					  "keywords": [],
 | 
				
			||||||
@@ -25,9 +26,14 @@
 | 
				
			|||||||
    "@kevisual/query": "^0.0.29",
 | 
					    "@kevisual/query": "^0.0.29",
 | 
				
			||||||
    "@kevisual/query-login": "^0.0.6",
 | 
					    "@kevisual/query-login": "^0.0.6",
 | 
				
			||||||
    "@kevisual/registry": "^0.0.1",
 | 
					    "@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",
 | 
					    "@ricky0123/vad-web": "^0.0.28",
 | 
				
			||||||
    "@szhsin/react-menu": "^4.5.0",
 | 
					    "@szhsin/react-menu": "^4.5.0",
 | 
				
			||||||
    "@tailwindcss/vite": "^4.1.14",
 | 
					    "@tailwindcss/vite": "^4.1.14",
 | 
				
			||||||
 | 
					    "@tanstack/react-form": "^1.23.7",
 | 
				
			||||||
 | 
					    "@tanstack/react-query": "^5.90.5",
 | 
				
			||||||
    "astro": "^5.14.4",
 | 
					    "astro": "^5.14.4",
 | 
				
			||||||
    "class-variance-authority": "^0.7.1",
 | 
					    "class-variance-authority": "^0.7.1",
 | 
				
			||||||
    "clsx": "^2.1.1",
 | 
					    "clsx": "^2.1.1",
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -333,8 +333,8 @@ export const Table: React.FC<TableProps> = ({
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    // Ctrl/Cmd + A 全选
 | 
					    // Ctrl/Cmd + A 全选
 | 
				
			||||||
    if ((event.ctrlKey || event.metaKey) && event.key === 'a') {
 | 
					    if ((event.ctrlKey || event.metaKey) && event.key === 'a') {
 | 
				
			||||||
      event.preventDefault();
 | 
					      // event.preventDefault();
 | 
				
			||||||
      handleSelectAll(true);
 | 
					      // handleSelectAll(true);
 | 
				
			||||||
      return;
 | 
					      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 { Panel, PanelGroup, PanelResizeHandle } from 'react-resizable-panels';
 | 
				
			||||||
import { useState, useRef } from 'react';
 | 
					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 { ChatInterface } from './prompts/index.tsx';
 | 
				
			||||||
import { BaseApp } from './base/index.tsx';
 | 
					import { BaseApp } from './base/index.tsx';
 | 
				
			||||||
import { exampleUsage, markService } from './modules/mark-service.ts';
 | 
					import { exampleUsage, markService } from './modules/mark-service.ts';
 | 
				
			||||||
@@ -201,7 +201,7 @@ export const App: React.FC = () => {
 | 
				
			|||||||
      <MuseApp />
 | 
					      <MuseApp />
 | 
				
			||||||
      <ToastContainer
 | 
					      <ToastContainer
 | 
				
			||||||
        position="top-right"
 | 
					        position="top-right"
 | 
				
			||||||
        autoClose={5000}
 | 
					        autoClose={1000}
 | 
				
			||||||
        hideProgressBar={false}
 | 
					        hideProgressBar={false}
 | 
				
			||||||
        newestOnTop={false}
 | 
					        newestOnTop={false}
 | 
				
			||||||
        closeOnClick
 | 
					        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 clsx from "clsx";
 | 
				
			||||||
import { useState, useEffect, useRef } from "react";
 | 
					import { useState, useEffect, useRef } from "react";
 | 
				
			||||||
import './style.css'
 | 
					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 { Menu, MenuItem, MenuButton, } from '@szhsin/react-menu';
 | 
				
			||||||
import '@szhsin/react-menu/dist/index.css';
 | 
					import '@szhsin/react-menu/dist/index.css';
 | 
				
			||||||
import { toast } from 'react-toastify';
 | 
					import { toast } from 'react-toastify';
 | 
				
			||||||
import { Speak } from "./speak-db/speak";
 | 
					import { Speak } from "./speak-db/speak";
 | 
				
			||||||
import { useVoiceStore } from "../store/voiceStore";
 | 
					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 = {
 | 
					type VadVoiceProps = {
 | 
				
			||||||
  data: Speak;
 | 
					  data: Speak;
 | 
				
			||||||
@@ -136,19 +149,51 @@ const VoicePlayer = ({ data }: VadVoiceProps) => {
 | 
				
			|||||||
            <span>识别语音</span>
 | 
					            <span>识别语音</span>
 | 
				
			||||||
          </div>
 | 
					          </div>
 | 
				
			||||||
        </MenuItem>
 | 
					        </MenuItem>
 | 
				
			||||||
        <MenuItem onClick={handleDelete}>
 | 
					        {data.text && data.text.trim() ? (
 | 
				
			||||||
          <div className="flex items-center space-x-2">
 | 
					          <AlertDialog>
 | 
				
			||||||
            <svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
 | 
					            <AlertDialogTrigger asChild>
 | 
				
			||||||
              <path
 | 
					              <MenuItem>
 | 
				
			||||||
                strokeLinecap="round"
 | 
					                <div className="flex items-center space-x-2">
 | 
				
			||||||
                strokeLinejoin="round"
 | 
					                  <svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
 | 
				
			||||||
                strokeWidth={2}
 | 
					                    <path
 | 
				
			||||||
                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"
 | 
					                      strokeLinecap="round"
 | 
				
			||||||
              />
 | 
					                      strokeLinejoin="round"
 | 
				
			||||||
            </svg>
 | 
					                      strokeWidth={2}
 | 
				
			||||||
            <span>删除</span>
 | 
					                      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"
 | 
				
			||||||
          </div>
 | 
					                    />
 | 
				
			||||||
        </MenuItem>
 | 
					                  </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>
 | 
					      </Menu>
 | 
				
			||||||
      {/* 播放/暂停按钮 */}
 | 
					      {/* 播放/暂停按钮 */}
 | 
				
			||||||
      {!isPlaying ? (
 | 
					      {!isPlaying ? (
 | 
				
			||||||
@@ -187,11 +232,92 @@ export const ShowVoicePlayer = ({ data }: { data: Speak[] }) => {
 | 
				
			|||||||
              <VoicePlayer data={item} />
 | 
					              <VoicePlayer data={item} />
 | 
				
			||||||
            </div>
 | 
					            </div>
 | 
				
			||||||
            <div className="flex-1 min-w-0">
 | 
					            <div className="flex-1 min-w-0">
 | 
				
			||||||
              <div className="text-xs text-gray-400 truncate">
 | 
					              <div className="flex items-center justify-between space-x-2">
 | 
				
			||||||
                {new Date(item.timestamp).toLocaleTimeString()}
 | 
					                <div>
 | 
				
			||||||
              </div>
 | 
					                  <div className="text-xs text-gray-400 truncate">
 | 
				
			||||||
              <div className="text-xs text-gray-300">
 | 
					                    {new Date(item.timestamp).toLocaleTimeString()}
 | 
				
			||||||
                #{item.no}
 | 
					                  </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>
 | 
					              </div>
 | 
				
			||||||
              {item.text && (
 | 
					              {item.text && (
 | 
				
			||||||
                <div
 | 
					                <div
 | 
				
			||||||
@@ -230,7 +356,15 @@ export const VadVoice = () => {
 | 
				
			|||||||
    setError: setStoreError
 | 
					    setError: setStoreError
 | 
				
			||||||
  } = useVoiceStore();
 | 
					  } = 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 [vadStatus, setVadStatus] = useState<'idle' | 'initializing' | 'ready' | 'error'>('idle');
 | 
				
			||||||
  const [realListen, setRealListen] = useState<boolean>(false);
 | 
					  const [realListen, setRealListen] = useState<boolean>(false);
 | 
				
			||||||
  const [errorMessage, setErrorMessage] = useState<string>('');
 | 
					  const [errorMessage, setErrorMessage] = useState<string>('');
 | 
				
			||||||
@@ -238,7 +372,8 @@ export const VadVoice = () => {
 | 
				
			|||||||
  const ref = useRef<MicVAD | null>(null);
 | 
					  const ref = useRef<MicVAD | null>(null);
 | 
				
			||||||
  const initializingRef = useRef<boolean>(false);
 | 
					  const initializingRef = useRef<boolean>(false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  async function initializeVAD() {
 | 
					  async function initializeVAD(ls: boolean = true) {
 | 
				
			||||||
 | 
					    if (!ls) { return }
 | 
				
			||||||
    if (ref.current || initializingRef.current) return;
 | 
					    if (ref.current || initializingRef.current) return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    initializingRef.current = true;
 | 
					    initializingRef.current = true;
 | 
				
			||||||
@@ -247,7 +382,6 @@ export const VadVoice = () => {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    try {
 | 
					    try {
 | 
				
			||||||
      console.log('Starting VAD initialization...');
 | 
					      console.log('Starting VAD initialization...');
 | 
				
			||||||
 | 
					 | 
				
			||||||
      // 添加延迟确保资源加载完成
 | 
					      // 添加延迟确保资源加载完成
 | 
				
			||||||
      await new Promise((resolve) => setTimeout(resolve, 500));
 | 
					      await new Promise((resolve) => setTimeout(resolve, 500));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -310,7 +444,7 @@ export const VadVoice = () => {
 | 
				
			|||||||
  const handleUserInteraction = async () => {
 | 
					  const handleUserInteraction = async () => {
 | 
				
			||||||
    if (!userInteracted) {
 | 
					    if (!userInteracted) {
 | 
				
			||||||
      setUserInteracted(true);
 | 
					      setUserInteracted(true);
 | 
				
			||||||
      await initializeVAD();
 | 
					      // await initializeVAD();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  };
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -318,7 +452,6 @@ export const VadVoice = () => {
 | 
				
			|||||||
  useEffect(() => {
 | 
					  useEffect(() => {
 | 
				
			||||||
    initializeStore();
 | 
					    initializeStore();
 | 
				
			||||||
  }, [initializeStore]);
 | 
					  }, [initializeStore]);
 | 
				
			||||||
 | 
					 | 
				
			||||||
  useEffect(() => {
 | 
					  useEffect(() => {
 | 
				
			||||||
    // 页面加载时不自动初始化,等待用户交互
 | 
					    // 页面加载时不自动初始化,等待用户交互
 | 
				
			||||||
    const handleFirstClick = () => {
 | 
					    const handleFirstClick = () => {
 | 
				
			||||||
@@ -329,16 +462,23 @@ export const VadVoice = () => {
 | 
				
			|||||||
    };
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    document.addEventListener('click', handleFirstClick);
 | 
					    document.addEventListener('click', handleFirstClick);
 | 
				
			||||||
    // handleUserInteraction()
 | 
					 | 
				
			||||||
    return () => {
 | 
					    return () => {
 | 
				
			||||||
      document.removeEventListener('click', handleFirstClick);
 | 
					      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 = () => {
 | 
					  const close = () => {
 | 
				
			||||||
    if (ref.current) {
 | 
					    if (ref.current) {
 | 
				
			||||||
      ref.current.destroy();
 | 
					      ref.current.destroy();
 | 
				
			||||||
@@ -369,20 +509,26 @@ export const VadVoice = () => {
 | 
				
			|||||||
  return <div className="h-full flex flex-col">
 | 
					  return <div className="h-full flex flex-col">
 | 
				
			||||||
    {/* Audio Recordings List */}
 | 
					    {/* Audio Recordings List */}
 | 
				
			||||||
    <div className="flex-1 overflow-y-auto px-2 py-3 min-h-0 max-h-200">
 | 
					    <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 className="mb-2">🎤</div>
 | 
				
			||||||
          <div>Click anywhere to initialize microphone</div>
 | 
					          <div>Click anywhere to initialize microphone</div>
 | 
				
			||||||
          <div className="text-xs mt-1">Browser requires user interaction for microphone access</div>
 | 
					          <div className="text-xs mt-1">Browser requires user interaction for microphone access</div>
 | 
				
			||||||
        </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>
 | 
					    </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>
 | 
					              <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 className="absolute -top-1 -right-1 w-3 h-3 bg-red-500 rounded-full animate-pulse"></div>
 | 
				
			||||||
            )}
 | 
					            )}
 | 
				
			||||||
          </div>
 | 
					          </div>
 | 
				
			||||||
@@ -433,17 +579,41 @@ export const VadVoice = () => {
 | 
				
			|||||||
            onClick={handleStartStop}
 | 
					            onClick={handleStartStop}
 | 
				
			||||||
            disabled={vadStatus === 'initializing'}
 | 
					            disabled={vadStatus === 'initializing'}
 | 
				
			||||||
            className={clsx(
 | 
					            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",
 | 
					              vadStatus === 'initializing' && "opacity-50 cursor-not-allowed",
 | 
				
			||||||
              listen
 | 
					              listen
 | 
				
			||||||
                ? "bg-red-100 text-red-700 hover:bg-red-200"
 | 
					                ? "bg-red-100 text-red-700 hover:bg-red-200"
 | 
				
			||||||
                : "bg-green-100 text-green-700 hover:bg-green-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>
 | 
					          </button>
 | 
				
			||||||
        </div>
 | 
					        </div>
 | 
				
			||||||
      </div>
 | 
					      </div>
 | 
				
			||||||
    </div>
 | 
					    </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    {/* 设置弹窗 */}
 | 
				
			||||||
 | 
					    <SettingModal />
 | 
				
			||||||
  </div >
 | 
					  </div >
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -10,7 +10,9 @@ export const getConfig = () => {
 | 
				
			|||||||
  };
 | 
					  };
 | 
				
			||||||
  
 | 
					  
 | 
				
			||||||
  return {
 | 
					  return {
 | 
				
			||||||
 | 
					    // 火山引擎APPID
 | 
				
			||||||
    VOLCENGINE_AUC_APPID: getFromLocalStorage('VOLCENGINE_AUC_APPID', ''),
 | 
					    VOLCENGINE_AUC_APPID: getFromLocalStorage('VOLCENGINE_AUC_APPID', ''),
 | 
				
			||||||
 | 
					    // 火山引擎Access Token
 | 
				
			||||||
    VOLCENGINE_AUC_TOKEN: getFromLocalStorage('VOLCENGINE_AUC_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 { Speak, getDayOfYear, CreateSpeakData } from '../modules/speak-db/speak';
 | 
				
			||||||
import { speakService } from '../modules/speak-db/speak-service';
 | 
					import { speakService } from '../modules/speak-db/speak-service';
 | 
				
			||||||
import { getText } from '../modules/text';
 | 
					import { getText } from '../modules/text';
 | 
				
			||||||
 | 
					import { useSettingStore } from './settingStore';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
interface VoiceState {
 | 
					interface VoiceState {
 | 
				
			||||||
  // 状态数据
 | 
					  // 状态数据
 | 
				
			||||||
@@ -105,7 +106,7 @@ export const useVoiceStore = create<VoiceState>()(
 | 
				
			|||||||
      // 添加新的语音记录
 | 
					      // 添加新的语音记录
 | 
				
			||||||
      addVoice: async (url: string, duration: number, audioBlob?: Blob) => {
 | 
					      addVoice: async (url: string, duration: number, audioBlob?: Blob) => {
 | 
				
			||||||
        const { setError } = get();
 | 
					        const { setError } = get();
 | 
				
			||||||
        
 | 
					        const autoRecognize = useSettingStore.getState().autoRecognize;
 | 
				
			||||||
        try {
 | 
					        try {
 | 
				
			||||||
          setError(null);
 | 
					          setError(null);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -123,8 +124,12 @@ export const useVoiceStore = create<VoiceState>()(
 | 
				
			|||||||
            day: getDayOfYear(),
 | 
					            day: getDayOfYear(),
 | 
				
			||||||
            no: 0, // 将由 service 自动生成
 | 
					            no: 0, // 将由 service 自动生成
 | 
				
			||||||
            timestamp: Date.now(),
 | 
					            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)
 | 
					          // 保存到 IndexedDB(不包含 url)
 | 
				
			||||||
          const newSpeak = await speakService.createSpeakAuto(speakData);
 | 
					          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