Skip to content

Commit 44e5794

Browse files
committed
fixup! review comments, render mermaid errors
Signed-off-by: Petr Kadlec <petr@puradesign.cz>
1 parent 7827409 commit 44e5794

File tree

11 files changed

+66
-45
lines changed

11 files changed

+66
-45
lines changed

.vscode/settings.json

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
{
2+
"editor.codeActionsOnSave": {
3+
"source.fixAll.eslint": "always"
4+
},
5+
"[markdown]": {
6+
"editor.formatOnSave": false
7+
},
8+
"[yaml]": {
9+
"editor.formatOnSave": false
10+
},
11+
"search.exclude": {
12+
"apps/agentstack-ui/dist": true,
13+
"apps/agentstack-ui/.next": true
14+
},
15+
"stylelint.validate": ["css", "scss"]
16+
}

apps/agentstack-ui/src/components/MarkdownContent/MarkdownContent.tsx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import { urlTransform } from './utils';
2222

2323
export interface MarkdownContentProps {
2424
codeBlocksExpanded?: boolean;
25+
isStreaming?: boolean;
2526
children?: string;
2627
className?: string;
2728
remarkPlugins?: PluggableList;
@@ -30,6 +31,7 @@ export interface MarkdownContentProps {
3031

3132
export function MarkdownContent({
3233
codeBlocksExpanded,
34+
isStreaming,
3335
className,
3436
remarkPlugins: remarkPluginsProps,
3537
components: componentsProps,
@@ -39,10 +41,10 @@ export function MarkdownContent({
3941
() => ({
4042
...components,
4143
code: ({ ...props }) => <Code {...props} forceExpand={codeBlocksExpanded} />,
42-
mermaidDiagram: (props) => <MermaidDiagram {...props} />,
44+
mermaidDiagram: (props) => <MermaidDiagram {...props} isStreaming={isStreaming} />,
4345
...componentsProps,
4446
}),
45-
[codeBlocksExpanded, componentsProps],
47+
[codeBlocksExpanded, componentsProps, isStreaming],
4648
);
4749

4850
const extendedRemarkPlugins = useMemo(() => [...remarkPlugins, ...(remarkPluginsProps ?? [])], [remarkPluginsProps]);

apps/agentstack-ui/src/components/MarkdownContent/components/MermaidDiagram.module.scss

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,3 +23,9 @@
2323
justify-content: center;
2424
}
2525
}
26+
27+
.error {
28+
:global(.cds--inline-notification__subtitle) {
29+
@include line-clamp(2);
30+
}
31+
}

apps/agentstack-ui/src/components/MarkdownContent/components/MermaidDiagram.tsx

Lines changed: 23 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
* SPDX-License-Identifier: Apache-2.0
44
*/
55

6-
import { InlineLoading } from '@carbon/react';
6+
import { InlineLoading, InlineNotification } from '@carbon/react';
77
import mermaid from 'mermaid';
88
import { type HTMLAttributes, useEffect, useId } from 'react';
99
import type { ExtraProps } from 'react-markdown';
@@ -15,9 +15,10 @@ import { useMermaid } from '../contexts';
1515
import { Code } from './Code';
1616
import classes from './MermaidDiagram.module.scss';
1717

18-
export type MermaidDiagramProps = HTMLAttributes<HTMLElement> & ExtraProps & { mermaidIndex?: number };
18+
export type MermaidDiagramProps = HTMLAttributes<HTMLElement> &
19+
ExtraProps & { mermaidIndex?: number; isStreaming?: boolean };
1920

20-
export function MermaidDiagram({ children, mermaidIndex }: MermaidDiagramProps) {
21+
export function MermaidDiagram({ children, mermaidIndex, isStreaming }: MermaidDiagramProps) {
2122
const id = useId();
2223
const { theme } = useTheme();
2324
const { diagrams, setDiagram } = useMermaid();
@@ -29,8 +30,14 @@ export function MermaidDiagram({ children, mermaidIndex }: MermaidDiagramProps)
2930

3031
const diagram = diagrams.get(index);
3132

33+
console.log({ index, isStreaming, diagram });
34+
3235
useEffect(() => {
33-
mermaid.initialize({ startOnLoad: false, theme: theme === Theme.Dark ? 'dark' : 'default' });
36+
mermaid.initialize({
37+
startOnLoad: false,
38+
theme: theme === Theme.Dark ? 'dark' : 'default',
39+
suppressErrorRendering: true,
40+
});
3441
}, [theme]);
3542

3643
useEffect(() => {
@@ -48,8 +55,9 @@ export function MermaidDiagram({ children, mermaidIndex }: MermaidDiagramProps)
4855
setDiagram(index, svg);
4956
}
5057
} catch (error) {
51-
if (isMounted) {
58+
if (isMounted && !isStreaming) {
5259
console.warn(error);
60+
setDiagram(index, error instanceof Error ? error : new Error('Unknown error rendering Mermaid diagram'));
5361
}
5462
}
5563
}
@@ -59,14 +67,22 @@ export function MermaidDiagram({ children, mermaidIndex }: MermaidDiagramProps)
5967
return () => {
6068
isMounted = false;
6169
};
62-
}, [children, theme, id, setDiagram, index]);
70+
}, [children, theme, id, setDiagram, index, isStreaming]);
6371

6472
return (
6573
<div className={classes.root}>
6674
<Code className="language-mermaid">{children}</Code>
6775

68-
{diagram ? (
76+
{typeof diagram === 'string' ? (
6977
<div dangerouslySetInnerHTML={{ __html: diagram }} className={classes.diagram} />
78+
) : diagram instanceof Error && !isStreaming ? (
79+
<InlineNotification
80+
kind="error"
81+
title={'Failed to render Mermaid diagram'}
82+
subtitle={diagram.message}
83+
lowContrast
84+
className={classes.error}
85+
/>
7086
) : (
7187
<div className={classes.loading}>
7288
<InlineLoading description="Rendering diagram..." />

apps/agentstack-ui/src/components/MarkdownContent/contexts/MermaidProvider.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import type { MermaidContextValue } from './mermaid-context';
1010
import { MermaidContext } from './mermaid-context';
1111

1212
export function MermaidProvider({ children }: PropsWithChildren) {
13-
const [diagrams, setDiagrams] = useState<Map<number, string>>(new Map());
13+
const [diagrams, setDiagrams] = useState<Map<number, string | Error>>(new Map());
1414

1515
const setDiagram = useCallback((index: number, svg: string) => {
1616
setDiagrams((prev) => {

apps/agentstack-ui/src/components/MarkdownContent/contexts/mermaid-context.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,6 @@ import { createContext } from 'react';
88
export const MermaidContext = createContext<MermaidContextValue | null>(null);
99

1010
export interface MermaidContextValue {
11-
diagrams: Map<number, string>;
12-
setDiagram: (index: number, svg: string) => void;
11+
diagrams: Map<number, string | Error>;
12+
setDiagram: (index: number, value: string | Error) => void;
1313
}

apps/agentstack-ui/src/components/MarkdownContent/remark/index.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,10 @@ import type { PluggableList } from 'unified';
99

1010
import { remarkExternalLink } from './remarkExternalLink';
1111
import { remarkMermaid } from './remarkMermaid';
12-
import { remarkMermaidIndex } from './remarkMermaidIndex';
1312

1413
export const remarkPlugins = [
1514
remarkGfm,
1615
[remarkMath, { singleDollarTextMath: false }],
17-
remarkMermaidIndex,
1816
remarkMermaid,
1917
remarkExternalLink,
2018
] satisfies PluggableList;

apps/agentstack-ui/src/components/MarkdownContent/remark/remarkMermaid.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,19 @@ import { visit } from 'unist-util-visit';
99

1010
export function remarkMermaid() {
1111
return (tree: Root) => {
12+
let mermaidIndex = 0;
13+
1214
visit(tree, 'code', (node: Code) => {
1315
if (node.lang === 'mermaid') {
1416
node.data = {
1517
...node.data,
1618
hName: 'mermaidDiagram',
19+
hProperties: {
20+
mermaidIndex,
21+
},
1722
};
23+
24+
mermaidIndex++;
1825
}
1926
});
2027
};

apps/agentstack-ui/src/components/MarkdownContent/remark/remarkMermaidIndex.ts

Lines changed: 0 additions & 26 deletions
This file was deleted.

apps/agentstack-ui/src/modules/messages/components/MessageContent.tsx

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ import { memo } from 'react';
99
import type { UIMessage } from '#modules/messages/types.ts';
1010
import { ChatMarkdownContent } from '#modules/runs/components/ChatMarkdownContent/ChatMarkdownContent.tsx';
1111

12-
import { useAgentRun } from '../../runs/contexts/agent-run';
1312
import { Role } from '../api/types';
1413
import { checkMessageStatus, getMessageContent, getMessageSecret, getMessageSources, isAgentMessage } from '../utils';
1514
import classes from './MessageContent.module.scss';
@@ -20,8 +19,6 @@ interface Props {
2019
}
2120

2221
export const MessageContent = memo(function MessageContent({ message }: Props) {
23-
const { isPending } = useAgentRun();
24-
2522
const content = getMessageContent(message);
2623
const form = message.role === Role.User ? message.form : null;
2724
const auth = message.role === Role.User ? message.auth : null;
@@ -42,7 +39,12 @@ export const MessageContent = memo(function MessageContent({ message }: Props) {
4239
}
4340

4441
return (
45-
<ChatMarkdownContent className={classes.root} sources={sources} codeBlocksExpanded={isPending}>
42+
<ChatMarkdownContent
43+
className={classes.root}
44+
sources={sources}
45+
codeBlocksExpanded={status?.isInProgress}
46+
isStreaming={status?.isInProgress}
47+
>
4648
{content}
4749
</ChatMarkdownContent>
4850
);

0 commit comments

Comments
 (0)