import React from 'react'; import { cn } from '@/lib/utils'; import type { Part } from '@opencode-ai/sdk/v2'; import type { AgentMentionInfo } from '../types'; type PartWithText = Part & { text?: string; content?: string; value?: string }; type UserTextPartProps = { part: Part; messageId: string; isMobile: boolean; agentMention?: AgentMentionInfo; }; const buildMentionUrl = (name: string): string => { const encoded = encodeURIComponent(name); return `https://opencode.ai/docs/agents/#${encoded}`; }; const UserTextPart: React.FC = ({ part, messageId, agentMention }) => { const partWithText = part as PartWithText; const rawText = partWithText.text; const textContent = typeof rawText === 'string' ? rawText : partWithText.content || partWithText.value || ''; const [isExpanded, setIsExpanded] = React.useState(false); const [isTruncated, setIsTruncated] = React.useState(false); const textRef = React.useRef(null); React.useEffect(() => { const el = textRef.current; if (!el || isExpanded) return; const checkTruncation = () => { setIsTruncated(el.scrollHeight > el.clientHeight); }; checkTruncation(); const resizeObserver = new ResizeObserver(checkTruncation); resizeObserver.observe(el); return () => resizeObserver.disconnect(); }, [textContent, isExpanded]); const handleClick = React.useCallback(() => { if (isTruncated || isExpanded) { setIsExpanded((prev) => !prev); } }, [isTruncated, isExpanded]); if (!textContent || textContent.trim().length === 0) { return null; } // Render content with optional agent mention link const renderContent = () => { if (!agentMention?.token || !textContent.includes(agentMention.token)) { return textContent; } const idx = textContent.indexOf(agentMention.token); const before = textContent.slice(0, idx); const after = textContent.slice(idx + agentMention.token.length); return ( <> {before} e.stopPropagation()} > {agentMention.token} {after} ); }; return (
{renderContent()}
); }; export default React.memo(UserTextPart);