feat: update Makelore modules and conversations
Some checks failed
Electron E2E / Electron E2E (macos-latest) (push) Has been cancelled
Electron E2E / Electron E2E (ubuntu-latest) (push) Has been cancelled
Electron E2E / Electron E2E (windows-latest) (push) Has been cancelled

This commit is contained in:
inman
2026-07-31 10:08:41 +08:00
parent b8ca3f8eea
commit 80e8386fa6
175 changed files with 8036 additions and 10581 deletions

View File

@@ -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}