"use client"; import Image from "next/image"; import React from "react"; import { AnimatePresence, motion } from "framer-motion"; import { Box, Chip, Collapse, FormControl, IconButton, MenuItem, Paper, Select, Stack, TextField, Typography, alpha, useTheme, } from "@mui/material"; import SendRounded from "@mui/icons-material/SendRounded"; import StopRounded from "@mui/icons-material/StopRounded"; import MicRounded from "@mui/icons-material/MicRounded"; import KeyboardArrowDownRounded from "@mui/icons-material/KeyboardArrowDownRounded"; import KeyboardArrowUpRounded from "@mui/icons-material/KeyboardArrowUpRounded"; import AttachFileRounded from "@mui/icons-material/AttachFileRounded"; import BoltRounded from "@mui/icons-material/BoltRounded"; import AutoAwesomeRounded from "@mui/icons-material/AutoAwesomeRounded"; import type { AgentModel } from "@/lib/chatStream"; export type AgentComposerHandle = { focus: () => void; clear: () => void; append: (text: string) => void; setValue: (value: string) => void; getValue: () => string; }; type AgentComposerProps = { isHydrating?: boolean; isStreaming: boolean; isListening: boolean; isSttSupported: boolean; presets: string[]; onSend: (prompt: string) => void; onAbort: () => void; onStartListening: () => void; onStopListening: () => void; selectedModel: AgentModel; onModelChange: (model: AgentModel) => void; }; export const AgentComposer = React.forwardRef(function AgentComposer({ isHydrating = false, isStreaming, isListening, isSttSupported, presets, onSend, onAbort, onStartListening, onStopListening, selectedModel, onModelChange, }, ref) { const theme = useTheme(); const inputRef = React.useRef(null); const [input, setInput] = React.useState(""); const [isPresetOpen, setIsPresetOpen] = React.useState(false); const canSend = input.trim().length > 0 && !isStreaming && !isHydrating; React.useImperativeHandle( ref, () => ({ focus: () => inputRef.current?.focus(), clear: () => setInput(""), append: (text: string) => setInput((prev) => prev + text), setValue: (value: string) => setInput(value), getValue: () => input, }), [input], ); const handleSend = React.useCallback(() => { const prompt = input.trim(); if (!prompt || isStreaming || isHydrating) return; setInput(""); onSend(prompt); }, [input, isHydrating, isStreaming, onSend]); return ( TJWater Agent 管网分析快捷指令 setIsPresetOpen((value) => !value)} aria-label={isPresetOpen ? "收起常用管网任务" : "展开常用管网任务"} sx={{ width: 28, height: 28, color: "text.secondary", bgcolor: alpha("#fff", 0.5) }} > {isPresetOpen ? ( ) : ( )} {presets.map((prompt) => ( { setInput(prompt); setIsPresetOpen(false); window.setTimeout(() => { inputRef.current?.focus(); }, 0); }} sx={{ height: 32, borderRadius: "16px", bgcolor: alpha("#fff", 0.7), border: `1px solid ${alpha("#00acc1", 0.15)}`, color: "text.primary", fontWeight: 600, fontSize: '0.85rem', boxShadow: `0 2px 6px ${alpha("#000", 0.03)}`, backdropFilter: "blur(10px)", "&:hover": { bgcolor: alpha("#fff", 0.95), boxShadow: `0 4px 10px ${alpha("#00acc1", 0.2)}`, borderColor: alpha("#00acc1", 0.4), color: "#00acc1" } }} /> ))} setInput(event.target.value)} onKeyDown={(event) => { if (event.key === "Enter" && !event.shiftKey) { event.preventDefault(); handleSend(); } }} placeholder={isHydrating ? "正在加载对话记录..." : "描述你的分析目标,或点击上方指令库..."} fullWidth multiline maxRows={5} variant="standard" disabled={isHydrating} InputProps={{ disableUnderline: true, sx: { px: 1, py: 0.5, fontSize: "1rem", lineHeight: 1.6, fontWeight: 500, color: "text.primary" }, }} /> {isSttSupported ? ( isListening ? ( ) : ( ) ) : null} {isStreaming ? ( ) : ( )} DeepSeek Powered by DeepSeek V4 · TJWater Agent Intelligence ); });