Skip to content

Commit

Permalink
markdown support for operator io label, description, and caption
Browse files Browse the repository at this point in the history
  • Loading branch information
imanjra committed Apr 16, 2024
1 parent cedf28d commit 65e8982
Show file tree
Hide file tree
Showing 3 changed files with 183 additions and 131 deletions.
13 changes: 10 additions & 3 deletions app/packages/core/src/plugins/SchemaIO/components/Header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { Box, Stack, StackProps, Typography } from "@mui/material";
import React from "react";
import { ErrorView, HelpTooltip } from ".";
import { getComponentProps } from "../utils";
import Markdown from "./Markdown";

export default function Header(props: HeaderProps) {
const {
Expand Down Expand Up @@ -64,7 +65,9 @@ export default function Header(props: HeaderProps) {
sx={{ display: "flex", alignItems: "center" }}
{...getComponentProps(viewProps, "label")}
>
{label}
<Markdown {...getComponentProps(viewProps, "label.markdown")}>
{label}
</Markdown>
{descriptionView === "tooltip" && (
<HelpTooltip
title={description}
Expand All @@ -80,7 +83,9 @@ export default function Header(props: HeaderProps) {
color="text.secondary"
{...getComponentProps(viewProps, "description")}
>
{description}
<Markdown {...getComponentProps(viewProps, "description.markdown")}>
{description}
</Markdown>
</Typography>
)}
{caption && !omitCaption && (
Expand All @@ -89,7 +94,9 @@ export default function Header(props: HeaderProps) {
color="text.tertiary"
{...getComponentProps(viewProps, "caption")}
>
{caption}
<Markdown {...getComponentProps(viewProps, "caption.markdown")}>
{caption}
</Markdown>
</Typography>
)}
{!omitErrors && <ErrorView schema={{}} data={errors} />}
Expand Down
166 changes: 166 additions & 0 deletions app/packages/core/src/plugins/SchemaIO/components/Markdown.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
import { CopyButton, useTheme } from "@fiftyone/components";
import {
Box,
Link,
Paper,
Table,
TableBody,
TableCell,
TableContainer,
TableHead,
TableRow,
Typography,
} from "@mui/material";
import React from "react";
import { useHover } from "react-laag";
import ReactMarkdown from "react-markdown";
import { ReactMarkdownOptions } from "react-markdown/lib/react-markdown";
import { Light as SyntaxHighlighter } from "react-syntax-highlighter";
import js from "react-syntax-highlighter/dist/esm/languages/hljs/javascript";
import python from "react-syntax-highlighter/dist/esm/languages/hljs/python";
import ts from "react-syntax-highlighter/dist/esm/languages/hljs/typescript";
import tomorrow from "react-syntax-highlighter/dist/esm/styles/hljs/tomorrow";
import vs2015 from "react-syntax-highlighter/dist/esm/styles/hljs/vs2015";
import remarkGfm from "remark-gfm";
import styled from "styled-components";

SyntaxHighlighter.registerLanguage("javascript", js);
SyntaxHighlighter.registerLanguage("typescript", ts);
SyntaxHighlighter.registerLanguage("python", python);

const InlineCode = styled.span`
background: ${({ theme }) => theme.background.level1};
color: ${({ theme }) => theme.voxel[500]};
border-radius: 3px;
padding: 0.2em 0.4em;
font-size: 85%;
font-family: Roboto Mono, monospace;
`;

const CodeContainer = styled(Box)`
border: 1px solid ${({ theme }) => theme.background.level1};
pre {
margin: 0;
padding: 1rem !important;
}
border-radius: 3px;
`;

const CodeHeader = styled.div`
display: flex;
justify-content: space-between;
align-items: center;
padding: 0rem 1rem;
border-bottom: 1px solid ${({ theme }) => theme.background.level1};
background: ${({ theme }) => theme.background.level2};
`;

const componentsMap = {
a({ children, ...props }) {
if (
props.href &&
props.href.startsWith("http") &&
!props.href.includes(window.location.host)
) {
props = {
...props,
target: "_blank",
};
}

return <Link {...props}>{children}</Link>;
},
table({ children }) {
return (
<TableContainer component={Paper}>
<Table>{children}</Table>
</TableContainer>
);
},
td: TableCell,
th: TableCell,
tbody: TableBody,
thead: TableHead,
tr: TableRow,
code({ node, inline, className, children, ...props }) {
const theme = useTheme();
const [hovered, hoverProps] = useHover();
const isDarkMode = theme.mode === "dark";
const highlightTheme = isDarkMode ? vs2015 : tomorrow;
const match = /language-(\w+)/.exec(className || "");
let language = match ? match[1] : "text";
if (language === "js") {
language = "javascript";
}
if (language === "ts") {
language = "typescript";
}
if (language === "py") {
language = "python";
}
return !inline && match ? (
<CodeContainer {...hoverProps}>
<CodeHeader>
<Typography component="span">{language}</Typography>
<CopyButton
text={children}
sx={{ visibility: hovered ? "visible" : "hidden" }}
/>
</CodeHeader>

<SyntaxHighlighter language={language} style={highlightTheme}>
{children}
</SyntaxHighlighter>
</CodeContainer>
) : (
<InlineCode className={className} {...props}>
{children}
</InlineCode>
);
},
p: ({ children, ...props }) => <Typography>{children}</Typography>,
h1: ({ children, ...props }) => (
<Typography variant="h1" {...props}>
{children}
</Typography>
),
h2: ({ children, ...props }) => (
<Typography variant="h2" {...props}>
{children}
</Typography>
),
h3: ({ children, ...props }) => (
<Typography variant="h3" {...props}>
{children}
</Typography>
),
h4: ({ children, ...props }) => (
<Typography variant="h4" {...props}>
{children}
</Typography>
),
h5: ({ children, ...props }) => (
<Typography variant="h5" {...props}>
{children}
</Typography>
),
h6: ({ children, ...props }) => (
<Typography variant="h6" {...props}>
{children}
</Typography>
),
};

export default function Markdown(props: ReactMarkdownOptions) {
const { children, ...otherProps } = props;

return (
<ReactMarkdown
{...otherProps}
components={componentsMap}
remarkPlugins={[remarkGfm]}
>
{children}
</ReactMarkdown>
);
}
135 changes: 7 additions & 128 deletions app/packages/core/src/plugins/SchemaIO/components/MarkdownView.tsx
Original file line number Diff line number Diff line change
@@ -1,139 +1,18 @@
import { CopyButton, useTheme } from "@fiftyone/components";
import {
Box,
Link,
Typography,
Table,
TableBody,
TableCell,
TableContainer,
TableHead,
TableRow,
Paper,
} from "@mui/material";
import { useHover } from "react-laag";
import ReactMarkdown from "react-markdown";
import { Light as SyntaxHighlighter } from "react-syntax-highlighter";
import js from "react-syntax-highlighter/dist/esm/languages/hljs/javascript";
import python from "react-syntax-highlighter/dist/esm/languages/hljs/python";
import ts from "react-syntax-highlighter/dist/esm/languages/hljs/typescript";
import tomorrow from "react-syntax-highlighter/dist/esm/styles/hljs/tomorrow";
import vs2015 from "react-syntax-highlighter/dist/esm/styles/hljs/vs2015";
import styled from "styled-components";
import { Box } from "@mui/material";
import React from "react";
import { HeaderView } from ".";
import { getComponentProps } from "../utils";
import remarkGfm from "remark-gfm";
import React from "react";

SyntaxHighlighter.registerLanguage("javascript", js);
SyntaxHighlighter.registerLanguage("typescript", ts);
SyntaxHighlighter.registerLanguage("python", python);

const InlineCode = styled.span`
background: ${({ theme }) => theme.background.level1};
color: ${({ theme }) => theme.voxel[500]};
border-radius: 3px;
padding: 0.2em 0.4em;
font-size: 85%;
font-family: Roboto Mono, monospace;
`;

const CodeContainer = styled(Box)`
border: 1px solid ${({ theme }) => theme.background.level1};
pre {
margin: 0;
padding: 1rem !important;
}
border-radius: 3px;
`;

const CodeHeader = styled.div`
display: flex;
justify-content: space-between;
align-items: center;
padding: 0rem 1rem;
border-bottom: 1px solid ${({ theme }) => theme.background.level1};
background: ${({ theme }) => theme.background.level2};
`;

const componentsMap = {
a({ children, ...props }) {
if (
props.href &&
props.href.startsWith("http") &&
!props.href.includes(window.location.host)
) {
props = {
...props,
target: "_blank",
};
}

return <Link {...props}>{children}</Link>;
},
table({ children }) {
return (
<TableContainer component={Paper}>
<Table>{children}</Table>
</TableContainer>
);
},
td: TableCell,
th: TableCell,
tbody: TableBody,
thead: TableHead,
tr: TableRow,
code({ node, inline, className, children, ...props }) {
const theme = useTheme();
const [hovered, hoverProps] = useHover();
const isDarkMode = theme.mode === "dark";
const highlightTheme = isDarkMode ? vs2015 : tomorrow;
const match = /language-(\w+)/.exec(className || "");
let language = match ? match[1] : "text";
if (language === "js") {
language = "javascript";
}
if (language === "ts") {
language = "typescript";
}
if (language === "py") {
language = "python";
}
return !inline && match ? (
<CodeContainer {...hoverProps}>
<CodeHeader>
<Typography component="span">{language}</Typography>
<CopyButton
text={children}
sx={{ visibility: hovered ? "visible" : "hidden" }}
/>
</CodeHeader>

<SyntaxHighlighter language={language} style={highlightTheme}>
{children}
</SyntaxHighlighter>
</CodeContainer>
) : (
<InlineCode className={className} {...props}>
{children}
</InlineCode>
);
},
};
import Markdown from "./Markdown";

export default function MarkdownView(props) {
const { data } = props;
const { data, schema } = props;

return (
<Box {...getComponentProps(props, "container")}>
<HeaderView {...props} nested />
<ReactMarkdown
components={componentsMap}
remarkPlugins={[remarkGfm]}
{...getComponentProps(props, "markdown")}
>
{data}
</ReactMarkdown>
<Markdown {...getComponentProps(props, "markdown")}>
{data ?? schema?.default}
</Markdown>
</Box>
);
}

0 comments on commit 65e8982

Please sign in to comment.