feat(chat): port chat widget components from Dart to React

This commit is contained in:
2026-06-09 20:19:52 +08:00
parent 199a14650e
commit 109a3e3855
48 changed files with 2463 additions and 382 deletions
@@ -0,0 +1,86 @@
"use client";
/**
* ChatInputBar 输入栏
*
* 原始 Dart: lib/ui/chat/widgets/chat_input_bar.dart121 行)
*
* 组成(从左到右):
* - ChatMoreButton(⊞):展开更多选项(图片/语音/功能)
* - ChatInputTextField:文本输入
* - ChatSendButton(↑):发送
*
* 状态管理(自包含):
* - input:受控文本
* - focused:是否聚焦(视觉状态)
* - hasContent:是否有内容(决定发送按钮启用)
*/
import { useState } from "react";
import { useChatDispatch } from "@/stores/chat/chat-context";
import { ChatInputTextField } from "./chat-input-text-field";
import { ChatMoreButton } from "./chat-more-button";
import { ChatSendButton } from "./chat-send-button";
import { FunctionButtonBar } from "./function-button-bar";
import { ImageUploadButton } from "./image-upload-button";
import styles from "./chat-input-bar.module.css";
export interface ChatInputBarProps {
disabled?: boolean;
}
export function ChatInputBar({ disabled = false }: ChatInputBarProps) {
const dispatch = useChatDispatch();
const [input, setInput] = useState("");
const [focused, setFocused] = useState(false);
const [showFunctions, setShowFunctions] = useState(false);
const hasContent = input.trim().length > 0;
const handleSend = () => {
if (!hasContent) return;
dispatch({ type: "ChatSendMessage", content: input });
setInput("");
};
return (
<div className={styles.bar}>
{showFunctions && (
<FunctionButtonBar
items={[
{
key: "image",
icon: "🖼",
label: "Image",
// ImageUploadButton 内部已管理 sheetFunctionButtonBar 仅触发
onClick: () => {
/* 触发:ImageUploadButton 已在 ChatInputBar 中渲染 */
},
},
{ key: "restart", icon: "↻", label: "Restart" },
{ key: "history", icon: "⏱", label: "History" },
{ key: "call", icon: "📞", label: "Call" },
]}
/>
)}
<div className={`${styles.row} ${focused ? styles.rowFocused : ""}`}>
<ImageUploadButton disabled={disabled} />
<ChatMoreButton onClick={() => setShowFunctions((v) => !v)} />
<div
className={styles.textFieldWrap}
onFocus={() => setFocused(true)}
onBlur={() => setFocused(false)}
>
<ChatInputTextField
value={input}
onChange={setInput}
onSubmit={handleSend}
disabled={disabled}
/>
</div>
<ChatSendButton disabled={!hasContent || disabled} onClick={handleSend} />
</div>
</div>
);
}