"use client"; /** * ChatInputBar 输入栏 * * 原始 Dart: lib/ui/chat/widgets/chat_input_bar.dart(121 行) * * 状态: * - input:受控文本 * - isFocused:是否聚焦(控制外层 pink-bg / box-shadow / padding-top) * - hasContent:是否有 trim 内容(决定发送按钮是否启用) * * 容器层次(与 Dart 一致): * - 外层 `.bar`:focused 时 `padding-top: md` + 粉色背景 + 阴影 * - 内层 `.row`:白底 + `border-radius: 32` + focused 时 accent 边框 * - Row:[ChatInputTextField (Expanded), ChatSendButton] * * 行为: * - send 后清空 input + 保持 textarea 焦点(与 Dart `_focusNode.requestFocus()` 一致) * * 注意:Dart 源里的 `ImageUploadButton` 已被注释(`// const ImageUploadButton()`), * `ChatMoreButton` / `FunctionButtonBar` 不存在于 Dart 源;本组件仅保留与 Dart 一致的部分。 */ import { useRef, useState } from "react"; import { useChatDispatch } from "@/stores/chat/chat-context"; import { useUserState } from "@/stores/user/user-context"; import { Logger } from "@/utils"; import { ChatInputActionPanel } from "./chat-input-action-panel"; import { ChatInputTextField } from "./chat-input-text-field"; import { ChatSendButton } from "./chat-send-button"; import styles from "./chat-input-bar.module.css"; const log = new Logger("AppChatComponentsChatInputBar"); export interface ChatInputBarProps { disabled?: boolean; } export function ChatInputBar({ disabled = false }: ChatInputBarProps) { const dispatch = useChatDispatch(); const { currentUser } = useUserState(); const [input, setInput] = useState(""); const [isFocused, setIsFocused] = useState(false); const [isActionPanelOpen, setIsActionPanelOpen] = useState(false); const textareaRef = useRef(null); const hasContent = input.trim().length > 0; const isActive = isFocused || isActionPanelOpen; const voiceMinutesRemaining = currentUser?.voiceMinutesRemaining ?? 0; const handleInputChange = (value: string) => { setInput(value); if (value.trim().length > 0) { setIsActionPanelOpen(false); } }; const handleSend = () => { if (!hasContent) return; log.debug("[chat-input-bar] handleSend", { contentLength: input.length, contentPreview: input.slice(0, 50), hasContent, disabled, isFocused, }); dispatch({ type: "ChatSendMessage", content: input }); setInput(""); textareaRef.current?.focus(); }; const handleActionButtonClick = () => { if (hasContent) { handleSend(); return; } textareaRef.current?.blur(); setIsFocused(false); setIsActionPanelOpen((open) => !open); }; const handleVoiceMessageClick = () => { log.debug("[chat-input-bar] voice message action clicked", { voiceMinutesRemaining, }); }; return (
{isActionPanelOpen ? ( ) : null}
); }