Files
Cyrene_v2/frontend/web/src/components/ChatInput.tsx
T

71 lines
2.2 KiB
TypeScript

import { useEffect, useRef, useState } from "react";
import { Button } from "./primitives";
export interface ChatInputProps {
onSend: (text: string) => void;
sending: boolean;
onVoice: () => void;
placeholder?: string;
}
export function ChatInput({
onSend,
sending,
onVoice,
placeholder = "给昔涟发消息…",
}: ChatInputProps): React.ReactNode {
const [value, setValue] = useState("");
const taRef = useRef<HTMLTextAreaElement>(null);
// auto-grow the textarea up to a max height
useEffect(() => {
const el = taRef.current;
if (!el) return;
el.style.height = "auto";
el.style.height = Math.min(el.scrollHeight, 140) + "px";
}, [value]);
const submit = () => {
const t = value.trim();
if (!t || sending) return;
onSend(t);
setValue("");
};
return (
<div className="glass sticky bottom-0 border-t border-[var(--mu-glass-border)] p-3 md:p-4">
<div className="mx-auto flex max-w-2xl items-end gap-2 rounded-[var(--mu-radius-lg)] bg-[var(--mu-surface-2)] p-2 shadow-[var(--mu-shadow-1)]">
<textarea
ref={taRef}
value={value}
onChange={(e) => setValue(e.target.value)}
onKeyDown={(e) => {
if (e.key === "Enter" && !e.shiftKey) {
e.preventDefault();
submit();
}
}}
rows={1}
placeholder={placeholder}
className="max-h-[140px] flex-1 resize-none bg-transparent px-2 py-2 text-sm text-[var(--mu-text-hi)] outline-none placeholder:text-[var(--mu-text-lo)]"
/>
<button
onClick={onVoice}
aria-label="语音"
className="inline-flex h-9 w-9 shrink-0 items-center justify-center rounded-[var(--mu-radius)] bg-[var(--mu-surface-4)] text-base transition-all duration-200 ease-[var(--mu-spring)] hover:bg-[var(--mu-surface-5)] active:scale-95"
>
🎙
</button>
<Button
variant="primary"
onClick={submit}
disabled={sending || value.trim().length === 0}
className="shrink-0"
>
{sending ? "发送中…" : "发送"}
</Button>
</div>
</div>
);
}