minyeamer commented on code in PR #66809:
URL: https://github.com/apache/airflow/pull/66809#discussion_r3239154704
##########
airflow-core/src/airflow/ui/src/components/ReactMarkdown.tsx:
##########
@@ -106,81 +122,107 @@ const UlComponent = ({ children }: PropsWithChildren) =>
(
</List.Root>
);
-// Factory function for the code component that needs style
-const createCodeComponent =
- (style: typeof oneDark | typeof oneLight) =>
- ({
- children,
- className,
- inline,
- }: {
- readonly children: ReactNode;
- readonly className?: string;
- readonly inline?: boolean;
- }) => {
- if (inline) {
- return (
- <Code display="inline" p={2}>
- {children}
- </Code>
- );
+type CodeElementProps = {
+ readonly children?: ReactNode;
+ readonly className?: string;
+};
+
+type MermaidDiagramProps = {
+ readonly chart: string;
+ readonly theme: "dark" | "default";
+};
+
+type MarkdownRendererProps = {
+ readonly mermaidTheme: MermaidDiagramProps["theme"];
+ readonly style: SyntaxTheme;
+};
+
+const extractTextContent = (children: CodeElementProps["children"]): string =>
{
+ if (typeof children === "number" || typeof children === "string") {
+ return String(children);
+ }
+
+ if (Array.isArray(children)) {
+ return children
+ .map((child) => (typeof child === "number" || typeof child === "string"
? String(child) : ""))
+ .join("");
+ }
+
+ return "";
+};
+
+const CodeComponent = ({ children }: PropsWithChildren) => <Code
display="inline">{children}</Code>;
+
+// Factory function for the pre component that needs style
+const createPreComponent =
+ (style: SyntaxTheme, mermaidTheme: MermaidDiagramProps["theme"]) =>
+ ({ children }: { readonly children?: ReactNode }) => {
+ const [codeElement] = Children.toArray(children);
+
+ if (!isValidElement<CodeElementProps>(codeElement)) {
+ return <Box my={3}>{children}</Box>;
}
// Extract language from className (format: "language-python")
- const match = /language-(?<lang>\w+)/u.exec(className ?? "");
+ const { children: codeChildren, className } = codeElement.props;
+ const match = /language-(?<lang>[-\w]+)/u.exec(className ?? "");
const language = match?.groups?.lang;
+ const childString = extractTextContent(codeChildren).replace(/\n$/u, "");
- // Safely extract string content from children
- let childString = "";
-
- if (typeof children === "string") {
- childString = children;
- } else if (Array.isArray(children)) {
- childString = children.filter((child) => typeof child ===
"string").join("");
+ if (language === "mermaid") {
+ return <MarkdownMermaid chart={childString} fallbackStyle={style}
theme={mermaidTheme} />;
}
- return (
- <SyntaxHighlighter language={language ?? "text"} PreTag="div"
style={style} wrapLongLines>
- {childString.replace(/\n$/u, "")}
- </SyntaxHighlighter>
- );
+ return <MarkdownCodeBlock language={language} style={style}
value={childString} />;
};
+const createMarkdownComponents = ({ mermaidTheme, style }:
MarkdownRendererProps): Components => ({
+ // eslint-disable-next-line id-length
+ a: LinkComponent,
+ blockquote: BlockquoteComponent,
+ code: CodeComponent,
+ del: DelComponent,
+ em: EmComponent,
+ h1: makeHeading("h1"),
+ h2: makeHeading("h2"),
+ h3: makeHeading("h3"),
+ h4: makeHeading("h4"),
+ h5: makeHeading("h5"),
+ h6: makeHeading("h6"),
+ hr: HrComponent,
+ img: ImgComponent,
+ li: LiComponent,
+ ol: OlComponent,
+ // eslint-disable-next-line id-length
+ p: PComponent,
+ pre: createPreComponent(style, mermaidTheme),
+ table: TableComponent,
+ tbody: Table.Body,
+ td: Table.Cell,
+ text: TextComponent,
+ th: Table.ColumnHeader,
+ thead: Table.Header,
+ tr: Table.Row,
+ ul: UlComponent,
+});
+
const ReactMarkdown = (props: Options) => {
const { colorMode } = useColorMode();
const style = colorMode === "dark" ? oneDark : oneLight;
-
- const components = {
- // eslint-disable-next-line id-length
- a: LinkComponent,
- blockquote: BlockquoteComponent,
- code: createCodeComponent(style),
- del: DelComponent,
- em: EmComponent,
- h1: makeHeading("h1"),
- h2: makeHeading("h2"),
- h3: makeHeading("h3"),
- h4: makeHeading("h4"),
- h5: makeHeading("h5"),
- h6: makeHeading("h6"),
- hr: HrComponent,
- img: ImgComponent,
- li: LiComponent,
- ol: OlComponent,
- // eslint-disable-next-line id-length
- p: PComponent,
- pre: PreComponent,
- table: TableComponent,
- tbody: Table.Body,
- td: Table.Cell,
- text: TextComponent,
- th: Table.ColumnHeader,
- thead: Table.Header,
- tr: Table.Row,
- ul: UlComponent,
- };
-
- return <ReactMD components={components as Components} {...props}
remarkPlugins={[remarkGfm]} skipHtml />;
+ const mermaidTheme = colorMode === "dark" ? "dark" : "default";
+ const components = createMarkdownComponents({ mermaidTheme, style });
+
+ return (
+ <Box alignSelf="stretch" css={markdownContentStyles} maxWidth="100%"
minWidth={0} width="100%">
+ <ReactMD
+ components={components}
+ {...props}
+ rehypePlugins={[rehypeKatex]}
+ remarkPlugins={[remarkGfm, remarkMath]}
Review Comment:
Thank you for pointing this out. this was something I hadn't fully
considered.
I will set `singleDollarTextMath: false`
([docs](https://unifiedjs.com/explore/package/remark-math/#fields)) to prevent
single `$` from being parsed as inline math. Block math with `$$...$$` will
still work as expected.
Looking ahead, re-enabling single-dollar inline math would require existing
documents to escape bare dollar signs (e.g., `\$5`). I think it is worth
opening a community discussion or vote on whether that trade-off is acceptable
before reintroducing it.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]