pierrejeambrun commented on code in PR #66809:
URL: https://github.com/apache/airflow/pull/66809#discussion_r3349235909
##########
airflow-core/src/airflow/ui/package.json:
##########
@@ -119,7 +123,6 @@
"web-worker": "^1.5.0"
},
"pnpm": {
- "minimumReleaseAge": 5760,
Review Comment:
Why?
##########
airflow-core/3rd-party-licenses/LICENSE-mermaid.txt:
##########
Review Comment:
This 3rd party licenses thing, I believe are artifacts from Airflow 2.x UI.
(Now I don't think that's needed).
@potiuk for confirmation.
##########
airflow-core/src/airflow/ui/src/context/mermaid/MermaidProvider.tsx:
##########
@@ -0,0 +1,55 @@
+/*!
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import type { PropsWithChildren } from "react";
+
+import { useColorMode } from "src/context/colorMode";
+
+import { MermaidContext, type MermaidRenderParams, type MermaidTheme } from
"./Context";
+
+type MermaidApi = {
+ initialize: (config: { securityLevel: "strict"; startOnLoad: false; theme:
MermaidTheme }) => void;
+ render: (diagramId: string, chart: string) => Promise<{ svg: string }>;
+};
+
+let mermaidModulePromise: Promise<MermaidApi> | undefined;
+
+const getMermaid = async (): Promise<MermaidApi> => {
+ mermaidModulePromise ??= import("mermaid").then(({ default: mermaid }) =>
mermaid as MermaidApi);
+
+ return mermaidModulePromise;
+};
+
+export const MermaidProvider = ({ children }: PropsWithChildren) => {
+ const { colorMode } = useColorMode();
+ const theme: MermaidTheme = colorMode === "dark" ? "dark" : "default";
+
+ const renderDiagram = async ({ chart, diagramId }: MermaidRenderParams):
Promise<string> => {
+ const mermaid = await getMermaid();
+
+ mermaid.initialize({ securityLevel: "strict", startOnLoad: false, theme });
Review Comment:
That's done on every 'renderDiagram' ? Maybe just initialize once, and
detect the theme change.
##########
airflow-core/LICENSE:
##########
@@ -239,6 +239,10 @@ The text of each license is also included at
3rd-party-licenses/LICENSE-[project
(MIT License) Monaco Editor v0.52.2
(https://github.com/microsoft/monaco-editor)
(MIT License) eonasdan-bootstrap-datetimepicker v4.17.49
(https://github.com/eonasdan/bootstrap-datetimepicker/)
(MIT License) Chakra UI v3.35.0 (https://github.com/chakra-ui/chakra-ui)
+ (MIT License) KaTeX v0.16.45 (https://katex.org)
+ (MIT License) mermaid v11.14.0 (https://github.com/mermaid-js/mermaid)
+ (MIT License) rehype-katex v7.0.1
(https://github.com/remarkjs/remark-math/tree/main/packages/rehype-katex)
+ (MIT License) remark-math v6.0.0
(https://github.com/remarkjs/remark-math/tree/main/packages/remark-math)
Review Comment:
Same here, not needed I think.
##########
airflow-core/src/airflow/ui/src/components/ReactMarkdown.tsx:
##########
@@ -106,81 +134,103 @@ 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 MarkdownCodeElementProps = {
+ readonly children?: ReactNode;
+ readonly className?: string;
+};
+
+const hasDisplayMath = (children: Options["children"]): boolean =>
+ typeof children === "string" && children.includes("$$");
+
+const InlineCodeComponent = ({ children }: PropsWithChildren) => <Code
display="inline">{children}</Code>;
+
+// Factory function for the pre component that needs style
+const createPreComponent =
+ (style: SyntaxTheme) =>
+ ({ children }: { readonly children?: ReactNode }) => {
+ const [codeElement] = Children.toArray(children);
+
+ if (!isValidElement<MarkdownCodeElementProps>(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;
- // Safely extract string content from children
- let childString = "";
+ const codeText = Array.isArray(codeChildren)
+ ? codeChildren.map((child) => (typeof child === "string" ? child :
"")).join("")
+ : typeof codeChildren === "string"
+ ? codeChildren
+ : "";
- if (typeof children === "string") {
- childString = children;
- } else if (Array.isArray(children)) {
- childString = children.filter((child) => typeof child ===
"string").join("");
+ const childString = codeText.replace(/\n$/u, "");
+
+ if (language === "mermaid") {
+ return <MarkdownMermaid chart={childString} fallbackStyle={style} />;
}
- return (
- <SyntaxHighlighter language={language ?? "text"} PreTag="div"
style={style} wrapLongLines>
- {childString.replace(/\n$/u, "")}
- </SyntaxHighlighter>
- );
+ return <MarkdownCodeBlock language={language} style={style}
value={childString} />;
};
-const ReactMarkdown = (props: Options) => {
+const createMarkdownComponents = (style: SyntaxTheme): Components => ({
+ // eslint-disable-next-line id-length
+ a: LinkComponent,
+ blockquote: BlockquoteComponent,
+ code: InlineCodeComponent,
+ 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),
+ table: TableComponent,
+ tbody: Table.Body,
+ td: Table.Cell,
+ text: TextComponent,
+ th: Table.ColumnHeader,
+ thead: Table.Header,
+ tr: Table.Row,
+ ul: UlComponent,
+});
+
+const ReactMarkdown = ({ children, ...props }: Options) => {
const { colorMode } = useColorMode();
const style = colorMode === "dark" ? oneDark : oneLight;
+ const components = createMarkdownComponents(style);
+ const shouldEnableMath = hasDisplayMath(children);
- 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 />;
+ useEffect(() => {
+ if (shouldEnableMath) {
+ void katexStyleLoader.load();
+ }
+ }, [shouldEnableMath]);
+
+ return (
+ <Box alignSelf="stretch" css={markdownContentStyles} maxWidth="100%"
minWidth={0} width="100%">
+ <ReactMD
+ {...props}
+ components={components}
Review Comment:
The spread order changed here so components always wins over a
caller-supplied components prop (it used to be overridable). I checked the
consumers and none pass components, so this looks harmless — is that
intentional? (otherwise restore 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]