merge: upstream/experimental with citations feature

- Merge upstream changes including image search, tooltips, and UI improvements
- Keep citations feature with inline hover cards
- Resolve conflict in message-list-item.tsx: use upstream img max-width (90%) while preserving citations logic
- Maintain file upload improvements with citations support

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
ruitanglin
2026-01-29 12:55:43 +08:00
20 changed files with 771 additions and 112 deletions

View File

@@ -36,6 +36,7 @@ import { cn } from "@/lib/utils";
import { useArtifacts } from "../artifacts";
import { FlipDisplay } from "../flip-display";
import { Tooltip } from "../tooltip";
export function MessageGroup({
className,
@@ -217,6 +218,48 @@ function ToolCall({
)}
</ChainOfThoughtStep>
);
} else if (name === "image_search") {
let label: React.ReactNode = t.toolCalls.searchForRelatedImages;
if (typeof args.query === "string") {
label = t.toolCalls.searchForRelatedImagesFor(args.query);
}
const results = (
result as {
results: {
source_url: string;
thumbnail_url: string;
image_url: string;
title: string;
}[];
}
)?.results;
return (
<ChainOfThoughtStep key={id} label={label} icon={SearchIcon}>
{Array.isArray(results) && (
<ChainOfThoughtSearchResults>
{Array.isArray(results) &&
results.map((item) => (
<Tooltip key={item.image_url} content={item.title}>
<a
className="size-24 overflow-hidden rounded-lg object-cover"
href={item.source_url}
target="_blank"
rel="noreferrer"
>
<img
className="size-24 object-cover"
src={item.thumbnail_url}
alt={item.title}
width={100}
height={100}
/>
</a>
</Tooltip>
))}
</ChainOfThoughtSearchResults>
)}
</ChainOfThoughtStep>
);
} else if (name === "web_fetch") {
const url = (args as { url: string })?.url;
let title = url;

View File

@@ -243,7 +243,7 @@ function MessageContent_({
if (typeof src !== "string") {
return (
<img
className="max-w-full overflow-hidden rounded-lg"
className="max-w-[90%] overflow-hidden rounded-lg"
src={src}
alt={alt}
/>
@@ -256,7 +256,7 @@ function MessageContent_({
return (
<a href={url} target="_blank" rel="noopener noreferrer">
<img
className="max-w-full overflow-hidden rounded-lg"
className="max-w-[90%] overflow-hidden rounded-lg"
src={url}
alt={alt}
/>

View File

@@ -53,6 +53,17 @@ export function MessageList({
/>
);
}
if (group.type === "assistant:clarification") {
const message = group.messages[0];
if (message && hasContent(message)) {
return (
<MessageResponse key={group.id} rehypePlugins={rehypePlugins}>
{extractContentFromMessage(message)}
</MessageResponse>
);
}
return null;
}
if (group.type === "assistant:present-files") {
const files: string[] = [];
for (const message of group.messages) {