bolt.diy/app/components/chat/Messages.client.tsx
KevIsDev 0017d29154 fix: add model and provider info to quick action messages
Pass model and provider information through chat components to include in quick action messages. This fixes the issue of defaulting to anthropic model.
2025-05-28 12:32:00 +01:00

122 lines
4.6 KiB
TypeScript

import type { Message } from 'ai';
import { Fragment } from 'react';
import { classNames } from '~/utils/classNames';
import { AssistantMessage } from './AssistantMessage';
import { UserMessage } from './UserMessage';
import { useLocation } from '@remix-run/react';
import { db, chatId } from '~/lib/persistence/useChatHistory';
import { forkChat } from '~/lib/persistence/db';
import { toast } from 'react-toastify';
import { useStore } from '@nanostores/react';
import { profileStore } from '~/lib/stores/profile';
import { forwardRef } from 'react';
import type { ForwardedRef } from 'react';
import type { ProviderInfo } from '~/types/model';
interface MessagesProps {
id?: string;
className?: string;
isStreaming?: boolean;
messages?: Message[];
append?: (message: Message) => void;
chatMode?: 'discuss' | 'build';
setChatMode?: (mode: 'discuss' | 'build') => void;
model?: string;
provider?: ProviderInfo;
}
export const Messages = forwardRef<HTMLDivElement, MessagesProps>(
(props: MessagesProps, ref: ForwardedRef<HTMLDivElement> | undefined) => {
const { id, isStreaming = false, messages = [] } = props;
const location = useLocation();
const profile = useStore(profileStore);
const handleRewind = (messageId: string) => {
const searchParams = new URLSearchParams(location.search);
searchParams.set('rewindTo', messageId);
window.location.search = searchParams.toString();
};
const handleFork = async (messageId: string) => {
try {
if (!db || !chatId.get()) {
toast.error('Chat persistence is not available');
return;
}
const urlId = await forkChat(db, chatId.get()!, messageId);
window.location.href = `/chat/${urlId}`;
} catch (error) {
toast.error('Failed to fork chat: ' + (error as Error).message);
}
};
return (
<div id={id} className={props.className} ref={ref}>
{messages.length > 0
? messages.map((message, index) => {
const { role, content, id: messageId, annotations } = message;
const isUserMessage = role === 'user';
const isFirst = index === 0;
const isLast = index === messages.length - 1;
const isHidden = annotations?.includes('hidden');
if (isHidden) {
return <Fragment key={index} />;
}
return (
<div
key={index}
className={classNames('flex gap-4 p-6 py-5 w-full rounded-[calc(0.75rem-1px)]', {
'bg-bolt-elements-messages-background': isUserMessage || !isStreaming || (isStreaming && !isLast),
'bg-gradient-to-b from-bolt-elements-messages-background from-30% to-transparent':
isStreaming && isLast,
'mt-4': !isFirst,
})}
>
{isUserMessage && (
<div className="flex items-center justify-center w-[40px] h-[40px] overflow-hidden bg-white dark:bg-gray-800 text-gray-600 dark:text-gray-500 rounded-full shrink-0 self-start">
{profile?.avatar ? (
<img
src={profile.avatar}
alt={profile?.username || 'User'}
className="w-full h-full object-cover"
loading="eager"
decoding="sync"
/>
) : (
<div className="i-ph:user-fill text-2xl" />
)}
</div>
)}
<div className="grid grid-col-1 w-full">
{isUserMessage ? (
<UserMessage content={content} />
) : (
<AssistantMessage
content={content}
annotations={message.annotations}
messageId={messageId}
onRewind={handleRewind}
onFork={handleFork}
append={props.append}
chatMode={props.chatMode}
setChatMode={props.setChatMode}
model={props.model}
provider={props.provider}
/>
)}
</div>
</div>
);
})
: null}
{isStreaming && (
<div className="text-center w-full text-bolt-elements-item-contentAccent i-svg-spinners:3-dots-fade text-4xl mt-4"></div>
)}
</div>
);
},
);