ishaq101's picture
[NOTICKET] Branding Styling and Integration (Chat & Voice)
5ca6cf1
import { useState, useRef, useEffect } from "react";
import { SendHorizontal, Square } from "lucide-react";
import type { VoiceState } from "../../../hooks/useVoiceSession";
import VoiceMicButton from "./VoiceMicButton";
interface ChatInputProps {
onSend: (text: string) => void;
onStop?: () => void;
isLoading: boolean;
disabled?: boolean;
voiceState: VoiceState;
onVoiceToggle: () => void;
}
export default function ChatInput({
onSend,
onStop,
isLoading,
disabled,
voiceState,
onVoiceToggle,
}: ChatInputProps) {
const [value, setValue] = useState("");
const textareaRef = useRef<HTMLTextAreaElement>(null);
const isVoiceActive = voiceState !== "IDLE" && voiceState !== "ERROR";
useEffect(() => {
const el = textareaRef.current;
if (!el) return;
el.style.height = "auto";
el.style.height = Math.min(el.scrollHeight, 50) + "px";
}, [value]);
const handleSend = () => {
const trimmed = value.trim();
if (!trimmed || isLoading || disabled || isVoiceActive) return;
onSend(trimmed);
setValue("");
};
const handleKeyDown = (e: React.KeyboardEvent<HTMLTextAreaElement>) => {
if (e.key === "Enter" && !e.shiftKey) {
e.preventDefault();
handleSend();
}
};
const canSend = value.trim().length > 0 && !disabled && !isVoiceActive;
return (
<div
className={`bg-white border rounded-2xl shadow-sm px-4 py-3 transition-all ${
disabled || isVoiceActive
? "opacity-80 border-neutral-200"
: "border-neutral-200 focus-within:border-brand-green focus-within:shadow-md focus-within:shadow-brand-green/10"
}`}
>
<div className="flex items-end gap-3">
<VoiceMicButton
voiceState={voiceState}
onToggle={onVoiceToggle}
disabled={isLoading}
/>
<textarea
ref={textareaRef}
value={value}
onChange={(e) => setValue(e.target.value)}
onKeyDown={handleKeyDown}
placeholder={
isVoiceActive
? "Voice session active — speak to interact"
: "Ask me anything… (Enter to send, Shift+Enter for newline)"
}
disabled={isLoading || disabled || isVoiceActive}
rows={1}
className="flex-1 resize-none bg-transparent text-sm text-neutral-900 leading-6 placeholder:text-neutral-400 outline-none overflow-y-auto"
style={{ minHeight: "24px", maxHeight: "50px" }}
/>
{isLoading ? (
<button
onClick={onStop}
className="w-9 h-9 flex-shrink-0 rounded-xl bg-gradient-to-br from-brand-green-light to-brand-green text-white shadow-md shadow-brand-green/25 hover:brightness-105 flex items-center justify-center transition-all"
aria-label="Stop"
>
<Square className="h-3.5 w-3.5" />
</button>
) : (
<button
onClick={handleSend}
disabled={!canSend}
className={`w-9 h-9 flex-shrink-0 rounded-xl flex items-center justify-center transition-all ${
canSend
? "bg-gradient-to-br from-brand-green-light to-brand-green text-white shadow-md shadow-brand-green/25 hover:brightness-105"
: "bg-neutral-100 text-neutral-300 cursor-not-allowed"
}`}
aria-label="Send"
>
<SendHorizontal className="h-4 w-4" />
</button>
)}
</div>
</div>
);
}