feat: update Makelore modules and conversations
This commit is contained in:
@@ -26,6 +26,13 @@ interface ChatMessageProps {
|
||||
onToolExpandedChange?: (toolId: string, expanded: boolean) => void;
|
||||
assistantAvatarSrc?: string;
|
||||
assistantAvatarAlt?: string;
|
||||
/**
|
||||
* Keep the assistant column aligned without painting a second avatar when
|
||||
* the message belongs to an execution trace that already owns the avatar.
|
||||
*/
|
||||
assistantAvatarMode?: 'visible' | 'reserved' | 'reserved-trace';
|
||||
/** Reduce the top whitespace when the reply follows its own execution trace. */
|
||||
compactAssistantReply?: boolean;
|
||||
textOverride?: string;
|
||||
suppressToolCards?: boolean;
|
||||
suppressProcessAttachments?: boolean;
|
||||
@@ -234,6 +241,8 @@ export const ChatMessage = memo(function ChatMessage({
|
||||
onToolExpandedChange,
|
||||
assistantAvatarSrc,
|
||||
assistantAvatarAlt = '',
|
||||
assistantAvatarMode = 'visible',
|
||||
compactAssistantReply = false,
|
||||
textOverride,
|
||||
suppressToolCards = false,
|
||||
suppressProcessAttachments = false,
|
||||
@@ -246,6 +255,10 @@ export const ChatMessage = memo(function ChatMessage({
|
||||
const deliveryStatus = isUser ? message._deliveryStatus : undefined;
|
||||
const role = typeof message.role === 'string' ? message.role.toLowerCase() : '';
|
||||
const isToolResult = role === 'toolresult' || role === 'tool_result';
|
||||
const shouldRenderAssistantAvatar = !isUser && assistantAvatarMode === 'visible';
|
||||
const shouldReserveAssistantAvatar = !isUser
|
||||
&& (assistantAvatarMode === 'reserved' || assistantAvatarMode === 'reserved-trace');
|
||||
const shouldAlignTraceContent = !isUser && assistantAvatarMode === 'reserved-trace';
|
||||
const text = textOverride ?? extractText(message);
|
||||
// When text is folded into an ExecutionGraphCard, treat the message as
|
||||
// having no text for rendering purposes. Keeping this behind a flag (vs
|
||||
@@ -350,12 +363,22 @@ export const ChatMessage = memo(function ChatMessage({
|
||||
)}
|
||||
>
|
||||
{/* Avatar */}
|
||||
{!isUser && (
|
||||
<div className="mt-1 flex h-8 w-8 shrink-0 items-center justify-center overflow-hidden rounded-md border-2 border-[#26384D] bg-[#E9EFF5] text-[#26384D] shadow-[2px_2px_0_#26384D]">
|
||||
{assistantAvatarSrc ? (
|
||||
<img src={assistantAvatarSrc} alt={assistantAvatarAlt} className="h-full w-full object-cover [image-rendering:pixelated]" />
|
||||
) : (
|
||||
<Sparkles className="h-4 w-4" />
|
||||
{(shouldRenderAssistantAvatar || shouldReserveAssistantAvatar) && (
|
||||
<div
|
||||
data-testid={shouldRenderAssistantAvatar ? 'chat-message-avatar-assistant' : 'chat-message-avatar-reserved'}
|
||||
aria-hidden={shouldReserveAssistantAvatar || undefined}
|
||||
className={cn(
|
||||
'mt-1 h-8 w-8 shrink-0',
|
||||
shouldRenderAssistantAvatar
|
||||
&& 'flex items-center justify-center overflow-hidden rounded-md border border-foreground/15 bg-surface-tertiary text-foreground shadow-soft',
|
||||
)}
|
||||
>
|
||||
{shouldRenderAssistantAvatar && (
|
||||
assistantAvatarSrc ? (
|
||||
<img src={assistantAvatarSrc} alt={assistantAvatarAlt} className="h-full w-full object-cover [image-rendering:pixelated]" />
|
||||
) : (
|
||||
<Sparkles className="h-4 w-4" />
|
||||
)
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
@@ -363,8 +386,9 @@ export const ChatMessage = memo(function ChatMessage({
|
||||
{/* Content */}
|
||||
<div
|
||||
className={cn(
|
||||
'flex flex-col w-full min-w-0 max-w-[80%] space-y-2',
|
||||
isUser ? 'items-end' : 'items-start',
|
||||
'flex w-full min-w-0 flex-col space-y-2',
|
||||
isUser ? 'max-w-[78%] items-end' : 'max-w-none flex-1 items-start',
|
||||
shouldAlignTraceContent && 'pl-9',
|
||||
)}
|
||||
>
|
||||
{isStreaming && !isUser && streamingTools.length > 0 && (
|
||||
@@ -442,6 +466,7 @@ export const ChatMessage = memo(function ChatMessage({
|
||||
isUser={isUser}
|
||||
isStreaming={isStreaming}
|
||||
deliveryStatus={deliveryStatus}
|
||||
compactAssistantReply={compactAssistantReply}
|
||||
/>
|
||||
)}
|
||||
|
||||
@@ -604,11 +629,13 @@ function MessageBubble({
|
||||
isUser,
|
||||
isStreaming,
|
||||
deliveryStatus,
|
||||
compactAssistantReply,
|
||||
}: {
|
||||
text: string;
|
||||
isUser: boolean;
|
||||
isStreaming: boolean;
|
||||
deliveryStatus?: RawMessage['_deliveryStatus'];
|
||||
compactAssistantReply: boolean;
|
||||
}) {
|
||||
const deliveryLabel = deliveryStatus === 'queued'
|
||||
? 'Queued'
|
||||
@@ -620,18 +647,19 @@ function MessageBubble({
|
||||
<div
|
||||
data-testid={isUser ? 'chat-message-bubble-user' : 'chat-message-bubble-assistant'}
|
||||
className={cn(
|
||||
'relative rounded-lg border-2 border-[#26384D] px-4 py-3 shadow-[3px_3px_0_#26384D]',
|
||||
'relative rounded-2xl border border-foreground/15 px-4 pb-3 shadow-soft',
|
||||
!isUser && 'w-full',
|
||||
isUser || !compactAssistantReply ? 'pt-3' : 'pt-1',
|
||||
isUser
|
||||
? 'bg-[#DCE6EF] text-[#26384D]'
|
||||
: 'bg-[#FFFFFF] text-[#26384D]',
|
||||
? 'chat-user-message-surface bg-brand-soft text-foreground'
|
||||
: 'chat-assistant-message-surface bg-background text-foreground',
|
||||
)}
|
||||
>
|
||||
{isUser ? (
|
||||
<>
|
||||
{deliveryLabel && (
|
||||
<div
|
||||
className="mb-2 flex items-center justify-end gap-1.5 text-[11px] font-medium leading-none text-[#26384D]/70"
|
||||
className="mb-2 flex items-center justify-end gap-1.5 text-[11px] font-medium leading-none text-foreground/70"
|
||||
data-testid={`message-delivery-${deliveryStatus}`}
|
||||
>
|
||||
{deliveryStatus === 'sending' ? (
|
||||
@@ -645,7 +673,7 @@ function MessageBubble({
|
||||
<p className="whitespace-pre-wrap break-words break-all text-sm">{text}</p>
|
||||
</>
|
||||
) : (
|
||||
<div className="prose prose-sm max-w-none break-words break-all text-[#26384D]">
|
||||
<div className="prose prose-sm max-w-none break-words break-all text-foreground">
|
||||
<ReactMarkdown
|
||||
remarkPlugins={[remarkGfm, remarkMath]}
|
||||
rehypePlugins={[[rehypeKatex, { strict: false, throwOnError: false, output: 'html' }]]}
|
||||
@@ -720,8 +748,8 @@ function FileCard({ file, onOpen }: { file: AttachedFileMeta; onOpen?: (file: At
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
"flex items-center gap-3 rounded-xl border border-black/10 dark:border-white/10 px-3 py-2.5 bg-black/5 dark:bg-white/5 max-w-[220px]",
|
||||
file.filePath && "cursor-pointer hover:bg-black/10 dark:hover:bg-white/10 transition-colors"
|
||||
"flex items-center gap-3 rounded-xl border border-border/70 px-3 py-2.5 bg-surface-subtle max-w-[220px]",
|
||||
file.filePath && "cursor-pointer hover:bg-surface-tertiary transition-colors"
|
||||
)}
|
||||
onClick={handleOpen}
|
||||
title={file.filePath ? "Open file" : undefined}
|
||||
@@ -757,7 +785,7 @@ function ImageThumbnail({
|
||||
void filePath; void base64; void mimeType;
|
||||
return (
|
||||
<div
|
||||
className="relative w-36 h-36 rounded-xl border overflow-hidden border-black/10 dark:border-white/10 bg-black/5 dark:bg-white/5 group/img cursor-zoom-in"
|
||||
className="relative w-36 h-36 rounded-xl border overflow-hidden border-border/70 bg-surface-subtle group/img cursor-zoom-in"
|
||||
onClick={onPreview}
|
||||
>
|
||||
<img src={src} alt={fileName} className="w-full h-full object-cover" />
|
||||
@@ -788,7 +816,7 @@ function ImagePreviewCard({
|
||||
void filePath; void base64; void mimeType;
|
||||
return (
|
||||
<div
|
||||
className="relative max-w-xs rounded-xl border overflow-hidden border-black/10 dark:border-white/10 bg-black/5 dark:bg-white/5 group/img cursor-zoom-in"
|
||||
className="relative max-w-xs rounded-xl border overflow-hidden border-border/70 bg-surface-subtle group/img cursor-zoom-in"
|
||||
onClick={onPreview}
|
||||
>
|
||||
<img src={src} alt={fileName} className="block w-full" />
|
||||
@@ -834,7 +862,7 @@ function ImageLightbox({
|
||||
|
||||
return createPortal(
|
||||
<div
|
||||
className="fixed inset-0 z-50 flex items-center justify-center bg-black/80 backdrop-blur-sm"
|
||||
className="fixed inset-0 z-50 flex items-center justify-center bg-foreground/65 backdrop-blur-sm"
|
||||
onClick={onClose}
|
||||
>
|
||||
{/* Image + buttons stacked */}
|
||||
@@ -895,7 +923,7 @@ function ToolCard({
|
||||
onExpandedChange,
|
||||
}: ToolCardProps) {
|
||||
return (
|
||||
<div className="rounded-xl border border-black/10 dark:border-white/10 bg-black/5 dark:bg-white/5 text-sm">
|
||||
<div className="rounded-xl border border-border/70 bg-surface-subtle text-sm">
|
||||
<button
|
||||
type="button"
|
||||
aria-expanded={expanded}
|
||||
|
||||
Reference in New Issue
Block a user