import { memo, useContext } from "react"; import Markdown from "react-markdown"; import remarkGfm from "remark-gfm"; import Row from "@moduletrace/twui/layout/Row"; import Stack from "@moduletrace/twui/layout/Stack"; import type { BotMessageWithAttachments } from "@/src/types"; import linkComputerRefs from "@/src/utils/link-computer-refs"; import { BotMessageActionsContext } from "../(data)/message-actions-context"; import { MessageMarkdownComponents } from "../(data)/message-markdown-components"; import { QuotableAttribute } from "../(hooks)/use-message-selection"; import GroupTimestamp from "./group-timestamp"; import MessageActions from "./message-actions"; import MessageReactions from "./message-reactions"; import MessageReplyQuote from "./message-reply-quote"; import MessageUsageChip from "./message-usage-chip"; import NeedsYouChip from "./needs-you-chip"; import SpeakMessageButton from "./speak-message-button"; /** * Kept module-level: a new array on every render makes `react-markdown` * parse the message again. */ const RemarkPlugins = [remarkGfm]; type Props = { message: BotMessageWithAttachments; bot_name: string; /** * The group's time, on the last message only: shown beside its play * button. */ at?: number; }; /** * One bot message, rendered from markdown (GFM; raw HTML is never * rendered). Computers it names (`computerRef`) show as buttons that * switch the bot to them. A quoted reply sits above it, its reactions * below, and a row under it replies, reacts, reads it aloud and shows * why it needs you. Its text can be selected and quoted. * * Memoized: a message only renders (and parses its markdown) again when * it changes, not on every live step of the thread. */ function BotMessageLine({ message, bot_name, at }: Props) { const { onReact } = useContext(BotMessageActionsContext); const can_speak = Boolean(message.id && message.text?.trim()); return ( {message.reply_to ? ( ) : null} {linkComputerRefs({ text: message.text || "" })} {message.id ? ( ) : null} {message.needs_you ? : null} {at ? : null} {can_speak ? ( ) : null} ); } export default memo(BotMessageLine);