feat(ai): add markdown in ai chat (#13402)

This commit is contained in:
Antoine Moreaux
2025-07-29 17:13:00 +02:00
committed by GitHub
parent cbf731dba7
commit 40f529f1ac
7 changed files with 195 additions and 20 deletions
@@ -5,6 +5,7 @@ import { Avatar, IconDotsVertical, IconSparkles } from 'twenty-ui/display';
import { LightCopyIconButton } from '@/object-record/record-field/components/LightCopyIconButton';
import { AgentChatFilePreview } from '@/ai/components/internal/AgentChatFilePreview';
import { AgentChatMessageRole } from '@/ai/constants/agent-chat-message-role';
import { LazyMarkdownRenderer } from '@/ai/components/LazyMarkdownRenderer';
import { AgentChatMessage } from '~/generated/graphql';
import { beautifyPastDateRelativeToNow } from '~/utils/date-utils';
@@ -124,13 +125,17 @@ export const AIChatMessage = ({
}) => {
const theme = useTheme();
const markdownRender = (text: string) => {
return <LazyMarkdownRenderer text={text} />;
};
const getAssistantMessageContent = (message: AgentChatMessage) => {
if (message.content !== '') {
return message.content;
return markdownRender(message.content);
}
if (agentStreamingMessage.streamingText !== '') {
return agentStreamingMessage.streamingText;
return markdownRender(agentStreamingMessage.streamingText);
}
if (agentStreamingMessage.toolCall !== '') {
@@ -0,0 +1,68 @@
import { lazy, Suspense } from 'react';
import { useTheme } from '@emotion/react';
import styled from '@emotion/styled';
import Skeleton, { SkeletonTheme } from 'react-loading-skeleton';
import { SKELETON_LOADER_HEIGHT_SIZES } from '@/activities/components/SkeletonLoader';
const MarkdownRenderer = lazy(async () => {
const [{ default: Markdown }, { default: remarkGfm }] = await Promise.all([
import('react-markdown'),
import('remark-gfm'),
]);
return {
default: ({ children }: { children: string }) => (
<Markdown remarkPlugins={[remarkGfm]}>{children}</Markdown>
),
};
});
const StyledSkeletonContainer = styled.div`
display: flex;
flex-direction: column;
gap: ${({ theme }) => theme.spacing(2)};
width: 100%;
`;
const LoadingSkeleton = () => {
const theme = useTheme();
return (
<SkeletonTheme
baseColor={theme.background.tertiary}
highlightColor={theme.background.transparent.lighter}
borderRadius={theme.border.radius.sm}
>
<StyledSkeletonContainer>
<Skeleton
width="70%"
height={SKELETON_LOADER_HEIGHT_SIZES.standard.m}
/>
<Skeleton height={SKELETON_LOADER_HEIGHT_SIZES.standard.s} />
<Skeleton height={SKELETON_LOADER_HEIGHT_SIZES.standard.s} />
<Skeleton
width="90%"
height={SKELETON_LOADER_HEIGHT_SIZES.standard.s}
/>
<Skeleton
width="85%"
height={SKELETON_LOADER_HEIGHT_SIZES.standard.s}
/>
<Skeleton
width="80%"
height={SKELETON_LOADER_HEIGHT_SIZES.standard.s}
/>
</StyledSkeletonContainer>
</SkeletonTheme>
);
};
export const LazyMarkdownRenderer = ({ text }: { text: string }) => {
return (
<Suspense fallback={<LoadingSkeleton />}>
<MarkdownRenderer>{text}</MarkdownRenderer>
</Suspense>
);
};