import type { Message } from 'ai'; import React, { Suspense, useState } from 'react'; import { classNames } from '~/utils/classNames'; import { AssistantMessage, getAnnotationsTokensUsage } from './AssistantMessage'; import { UserMessage } from './UserMessage'; import { useLocation } from '@remix-run/react'; import { forkChat } from '~/lib/persistence/db'; import { toast } from 'react-toastify'; import WithTooltip from '~/components/ui/Tooltip'; import { assert, sendCommandDedicatedClient } from '~/lib/replay/ReplayProtocolClient'; interface MessagesProps { id?: string; className?: string; isStreaming?: boolean; messages?: Message[]; onRewind?: (messageId: string, contents: string) => void; } interface ProjectContents { content: string; // base64 encoded } const gProjectContentsByMessageId = new Map(); export function saveProjectContents(messageId: string, contents: ProjectContents) { gProjectContentsByMessageId.set(messageId, contents); } function hasFileModifications(content: string) { return content.includes('__boltArtifact__'); } export const Messages = React.forwardRef((props: MessagesProps, ref) => { const { id, isStreaming = false, messages = [], onRewind } = props; const getLastMessageProjectContents = (index: number) => { // The message index is for the model response, and the project // contents will be associated with the last message present when // the user prompt was sent to the model. This could be either two // or three messages back, depending on whether the "fix bug" // button was clicked. const beforeUserMessage = messages[index - 2]; const contents = gProjectContentsByMessageId.get(beforeUserMessage?.id); if (!contents) { const priorMessage = messages[index - 3]; const priorContents = gProjectContentsByMessageId.get(priorMessage?.id); if (!priorContents) { return undefined; } // We still rewind to just before the user message to retain any // explanation from the Nut API. return { messageId: beforeUserMessage.id, contents: priorContents }; } return { messageId: beforeUserMessage.id, contents }; }; return (
{messages.length > 0 ? messages.map((message, index) => { const { role, content, id: messageId } = message; const isUserMessage = role === 'user'; const isFirst = index === 0; const isLast = index === messages.length - 1; return (
} > {isUserMessage && (
)}
{isUserMessage ? ( ) : ( )}
{!isUserMessage && messageId && onRewind && getLastMessageProjectContents(index) && hasFileModifications(content) && (
)}
); }) : null} {isStreaming && (
)} ); });