Feat: An image carousel is displayed at the bottom of the agent's chat messages. #12076 (#12215)

### What problem does this PR solve?

Feat: An image carousel is displayed at the bottom of the agent's chat
messages. #12076

### Type of change


- [x] New Feature (non-breaking change which adds functionality)
This commit is contained in:
balibabu
2025-12-25 19:02:49 +08:00
committed by GitHub
parent cfd1250615
commit c7b5bfb809
7 changed files with 1665 additions and 440 deletions

View File

@ -17,10 +17,10 @@ import { useTranslation } from 'react-i18next';
import 'katex/dist/katex.min.css'; // `rehype-katex` does not import the CSS for you
import {
currentReg,
preprocessLaTeX,
replaceTextByOldReg,
replaceThinkToSection,
showImage,
} from '@/utils/chat';
import { useFetchDocumentThumbnailsByIds } from '@/hooks/use-document-request';
@ -28,12 +28,7 @@ import { cn } from '@/lib/utils';
import classNames from 'classnames';
import { omit } from 'lodash';
import { pipe } from 'lodash/fp';
import { CircleAlert } from 'lucide-react';
import { ImageCarousel } from '../markdown-content/image-carousel';
import {
groupConsecutiveReferences,
shouldShowCarousel,
} from '../markdown-content/reference-utils';
import reactStringReplace from 'react-string-replace';
import { Button } from '../ui/button';
import {
HoverCard,
@ -42,19 +37,6 @@ import {
} from '../ui/hover-card';
import styles from './index.less';
// Helper function to convert IReferenceObject to IReference
const convertReferenceObjectToReference = (
referenceObject: IReferenceObject,
) => {
const chunks = Object.values(referenceObject.chunks);
const docAggs = Object.values(referenceObject.doc_aggs);
return {
chunks,
doc_aggs: docAggs,
total: chunks.length,
};
};
const getChunkIndex = (match: string) => Number(match);
// TODO: The display of the table is inconsistent with the display previously placed in the MessageItem.
function MarkdownContent({
@ -227,95 +209,26 @@ function MarkdownContent({
const renderReference = useCallback(
(text: string) => {
const groups = groupConsecutiveReferences(text);
const elements = [];
let lastIndex = 0;
let replacedText = reactStringReplace(text, currentReg, (match, i) => {
const chunkIndex = getChunkIndex(match);
const convertedReference = reference
? convertReferenceObjectToReference(reference)
: null;
groups.forEach((group, groupIndex) => {
if (group[0].start > lastIndex) {
elements.push(text.substring(lastIndex, group[0].start));
}
if (
convertedReference &&
shouldShowCarousel(group, convertedReference)
) {
elements.push(
<ImageCarousel
key={`carousel-${groupIndex}`}
group={group}
reference={convertedReference}
fileThumbnails={fileThumbnails}
onImageClick={handleDocumentButtonClick}
/>,
);
} else {
group.forEach((ref) => {
const chunkIndex = getChunkIndex(ref.id);
const {
documentUrl,
fileExtension,
imageId,
chunkItem,
documentId,
} = getReferenceInfo(chunkIndex);
const docType = chunkItem?.doc_type;
if (showImage(docType)) {
elements.push(
<section key={ref.id}>
<Image
id={imageId}
className={styles.referenceInnerChunkImage}
onClick={
documentId
? handleDocumentButtonClick(
documentId,
chunkItem,
fileExtension === 'pdf',
documentUrl,
)
: () => {}
}
/>
<span className="text-accent-primary"> {imageId}</span>
</section>,
);
} else {
elements.push(
<HoverCard key={ref.id}>
<HoverCardTrigger>
<CircleAlert className="size-4 inline-block" />
</HoverCardTrigger>
<HoverCardContent className="max-w-3xl">
{renderPopoverContent(chunkIndex)}
</HoverCardContent>
</HoverCard>,
);
}
});
}
lastIndex = group[group.length - 1].end;
return (
<HoverCard key={i}>
<HoverCardTrigger>
<span className="text-text-secondary bg-bg-card rounded-2xl px-1 mx-1 text-nowrap">
Fig. {chunkIndex + 1}
</span>
</HoverCardTrigger>
<HoverCardContent className="max-w-3xl">
{renderPopoverContent(chunkIndex)}
</HoverCardContent>
</HoverCard>
);
});
if (lastIndex < text.length) {
elements.push(text.substring(lastIndex));
}
return elements;
return replacedText;
},
[
renderPopoverContent,
getReferenceInfo,
handleDocumentButtonClick,
reference,
fileThumbnails,
],
[renderPopoverContent],
);
return (