import { useEffect, useRef, useState, useCallback } from 'react' import ReactMarkdown from 'react-markdown' import remarkGfm from 'remark-gfm' import { Hash, Crown, Send, AtSign, Copy, Check, FileText, ExternalLink, } from 'lucide-react' import { useStore } from '../store' import type { Message } from '../types' export function ChatView() { const { activeRoomId, rooms, messages, sendMessage, user } = useStore() const [input, setInput] = useState('') const [mentionQuery, setMentionQuery] = useState(null) const [mentionIndex, setMentionIndex] = useState(0) const inputRef = useRef(null) const bottomRef = useRef(null) const room = rooms.find(r => r.id === activeRoomId) const msgs: Message[] = activeRoomId ? (messages[activeRoomId] || []) : [] const mode = room?.mode || 'plan' // Build agent list for @ mentions const mentionCandidates = room ? [room.master, ...(room.members || [])].filter(Boolean) : [] const filteredMentions = mentionQuery !== null ? mentionCandidates.filter(name => name.toLowerCase().includes(mentionQuery.toLowerCase()) ) : [] // Detect @ trigger from input const updateMention = useCallback((value: string, cursorPos: number) => { const before = value.slice(0, cursorPos) const atMatch = before.match(/@(\w*)$/) if (atMatch) { setMentionQuery(atMatch[1]) setMentionIndex(0) } else { setMentionQuery(null) } }, []) const insertMention = useCallback((name: string) => { const el = inputRef.current if (!el) return const cursorPos = el.selectionStart || input.length const before = input.slice(0, cursorPos) const after = input.slice(cursorPos) const atIdx = before.lastIndexOf('@') const newInput = before.slice(0, atIdx) + `@${name} ` + after setInput(newInput) setMentionQuery(null) setTimeout(() => { const pos = atIdx + name.length + 2 el.setSelectionRange(pos, pos) el.focus() }) }, [input]) const toggleMode = useCallback(() => { if (!activeRoomId) return const newMode = mode === 'build' ? 'plan' : 'build' // 发送到后端 const { ws } = useStore.getState() const socket = ws[activeRoomId] if (socket) { socket.send(JSON.stringify({ type: 'set_mode', mode: newMode })) } // 本地立即更新 useStore.setState(s => ({ rooms: s.rooms.map(r => r.id === activeRoomId ? { ...r, mode: newMode } : r) })) }, [activeRoomId, mode]) useEffect(() => { bottomRef.current?.scrollIntoView({ behavior: 'smooth' }) }, [msgs, room?.status]) if (!room) { return (

选择一个项目开始聊天

) } const statusLabel = room.status === 'working' && room.activeAgent ? `${room.activeAgent} ${room.action || ''}` : room.status === 'thinking' ? '思考中...' : room.status === 'working' ? '工作中...' : '空闲' return (
{/* Main chat area */}
{/* Header */}
{room.name}
{room.team || '项目群'}
{/* Status badge */} {statusLabel}
{/* Messages */}
{msgs.length === 0 && (

这里是 {room.name} 的开始

发送消息开始对话

)} {msgs.map(msg => msg.role === 'artifact' ? ( ) : ( ) )} {/* Thinking indicator */} {room.status === 'thinking' && msgs[msgs.length - 1]?.role === 'user' && ( )}
{/* Input area */}
{/* @ Mention dropdown */} {mentionQuery !== null && filteredMentions.length > 0 && (
成员
{filteredMentions.map((name, i) => { const isMaster = name === room.master return ( ) })}
)} {/* Input bar with mode toggle */}
{/* Mode toggle - OpenCode style */}