this tsx file for astro shadcui:

// src/components/VoiceAssistant.tsx
import React, { useState, useRef, useEffect } from "react";
import { CardFooter , CardDescription, CardContent, CardHeader, Card } from "./ui/card";
import { Button } from "./ui/button";
import { Input } from "./ui/input";
import { PlayIcon, CircleStop, MessageCircle, XIcon } from "lucide-react";
import * as THREE from "three";

const api = "http://localhost:5000"; // Update this if deploying
const bgcolor = new THREE.Color().setHex(0x11161f);
const visualcolor = new THREE.Color().setHex(0x1d92c4);

const VoiceAssistant: React.FC = () => {
  const [chatVisible, setChatVisible] = useState(false);
  const [isListening, setIsListening] = useState(false);
  const [messages, setMessages] = useState<{ user: string; bot: string }[]>([]);
  const [input, setInput] = useState("");
  const audioRef = useRef<HTMLAudioElement>(null);
  const [analyser, setAnalyser] = useState<THREE.AudioAnalyser | null>(null);
  const visualizerRef = useRef<HTMLDivElement>(null);

  const sceneRef = useRef<THREE.Scene | null>(null);
  const cameraRef = useRef<THREE.PerspectiveCamera | null>(null);
  const rendererRef = useRef<THREE.WebGLRenderer | null>(null);
  const sphereRef = useRef<THREE.Points | null>(null);

  const toggleChat = () => setChatVisible(!chatVisible);

  // Handle sending a message to the chat API
  const handleSendMessage = async () => {
    if (!input.trim()) return;

    try {
      const response = await fetch(`${api}/chat`, {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ message: input }),
      });

      const data = await response.json();
      const botResponse = data.response;

      setMessages((prev) => [...prev, { user: input, bot: botResponse }]);
      setInput("");
      playBotResponse(botResponse);
    } catch (error) {
      console.error("Error fetching chat response:", error);
    }
  };

  // Play bot response using text-to-speech API
  const playBotResponse = async (text: string) => {
    try {
      const response = await fetch(`${api}/text-to-speech`, {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ text }),
      });

      const audioBlob = await response.blob();
      const audioUrl = URL.createObjectURL(audioBlob);

      if (audioRef.current) {
        audioRef.current.src = audioUrl;
        audioRef.current.play();
      }
    } catch (error) {
      console.error("Error fetching TTS response:", error);
    }
  };

  // Setup Three.js Visualizer
  const initVisualizer = () => {
    if (!visualizerRef.current) return;

    const scene = new THREE.Scene();
    scene.background = bgcolor;
    const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
    camera.position.z = 5;

    const renderer = new THREE.WebGLRenderer({ antialias: true });
    renderer.setSize(window.innerWidth, window.innerHeight);
    visualizerRef.current.appendChild(renderer.domElement);

    const geometry = new THREE.SphereGeometry(1.5, 32, 32);
    const material = new THREE.PointsMaterial({ color: visualcolor, size: 0.05 });
    const sphere = new THREE.Points(geometry, material);

    scene.add(sphere);

    sceneRef.current = scene;
    cameraRef.current = camera;
    rendererRef.current = renderer;
    sphereRef.current = sphere;

    const animate = () => {
      requestAnimationFrame(animate);

      if (analyser) {
        const data = analyser.getFrequencyData();
        const scale = 1 + data[0] / 256;
        sphere.scale.set(scale, scale, scale);
      }

      sphere.rotation.y += 0.003;
      sphere.rotation.x += 0.005;

      renderer.render(scene, camera);
    };

    animate();
  };

  useEffect(() => {
    initVisualizer();
  }, []);

  return (
    <div className="flex flex-col items-center h-screen">
      {/* Audio Visualizer */}
      <div ref={visualizerRef} className="w-full h-3/4 bg-background"></div>

      {/* Chat Interface Fixed at the Bottom */}
      {chatVisible && (
        <Card className="fixed bottom-0 w-full max-w-lg mx-auto bg-background shadow-lg border-t border-t-border">
          <CardHeader className="text-xl font-bold p-4">AI Chat Assistant</CardHeader>
          <CardContent className="p-4">
            <div className="h-40 overflow-y-auto p-2 bg-background rounded-md">
              {messages.map((msg, index) => (
                <div key={index} className="mb-4">
                  <p className="font-semibold text-blue-500">User:</p>
                  <p>{msg.user}</p>
                  <p className="font-semibold text-green-500 mt-2">Bot:</p>
                  <p>{msg.bot}</p>
                </div>
              ))}
            </div>
            <div className="flex mt-4">
              <Input
                placeholder="Type a message..."
                value={input}
                onChange={(e) => setInput(e.target.value)}
                onKeyDown={(e) => e.key === "Enter" && handleSendMessage()}
              />
              <Button onClick={handleSendMessage} className="ml-2 text-foreground">
                Send
              </Button>
            </div>
          </CardContent>
        </Card>
      )}

      {/* Control Buttons at the Bottom */}
      {!chatVisible && (
        <div className="fixed bottom-4 w-full flex justify-center space-x-4">
          <Button
            onClick={() => setIsListening(!isListening)}
            variant={isListening ? "destructive" : "default"}
            className={isListening ? undefined : "text-foreground"}
          >
            {isListening ? (
              <>
                <CircleStop className="mr-2" /> Stop
              </>
            ) : (
              <>
                <PlayIcon className="mr-2" /> Start
              </>
            )}
          </Button>
        </div>
      )}

      {/* Chat Toggle Button */}
      <div className="fixed bottom-16 w-full flex justify-center">
        <Button onClick={toggleChat} variant="secondary">
          {chatVisible ? (
            <>
              <XIcon className="mr-2" /> Hide Chat
            </>
          ) : (
            <>
              <MessageCircle className="mr-2" /> Show Chat
            </>
          )}
        </Button>
      </div>

      {/* Audio Element for TTS */}
      <audio ref={audioRef} />
    </div>
  );
};

export default VoiceAssistant;



make 3d animation move when think and do visuals when talk and add animations modern