diff --git a/site/src/components/Markdown/InlineMarkdown.stories.tsx b/site/src/components/Markdown/InlineMarkdown.stories.tsx new file mode 100644 index 0000000000..480fd61d74 --- /dev/null +++ b/site/src/components/Markdown/InlineMarkdown.stories.tsx @@ -0,0 +1,28 @@ +import type { Meta, StoryObj } from "@storybook/react-vite"; +import { InlineMarkdown } from "./InlineMarkdown"; + +const meta: Meta = { + title: "components/Markdown/InlineMarkdown", + component: InlineMarkdown, +}; + +export default meta; +type Story = StoryObj; + +export const WithFormatting: Story = { + args: { + children: "This supports **bold** and *italic* text.", + }, +}; + +export const WithLink: Story = { + args: { + children: "Read the [documentation](https://coder.com/docs).", + }, +}; + +export const WithCode: Story = { + args: { + children: "Run `coder templates push` to publish your template.", + }, +}; diff --git a/site/src/components/Markdown/InlineMarkdown.tsx b/site/src/components/Markdown/InlineMarkdown.tsx new file mode 100644 index 0000000000..34801ec8d1 --- /dev/null +++ b/site/src/components/Markdown/InlineMarkdown.tsx @@ -0,0 +1,74 @@ +import Link from "@mui/material/Link"; +import isEqual from "lodash/isEqual"; +import { type FC, memo } from "react"; +import ReactMarkdown, { type Options } from "react-markdown"; + +interface InlineMarkdownProps { + /** + * The Markdown text to parse and render + */ + children: string; + + /** + * Additional element types to allow. + * Allows italic, bold, links, and inline code snippets by default. + * eg. `["ol", "ul", "li"]` to support lists. + */ + allowedElements?: readonly string[]; + + className?: string; + + /** + * Can override the behavior of the generated elements + */ + components?: Options["components"]; +} + +/** + * Supports a strict subset of Markdown that behaves well as inline/confined + * content. Separated from the full Markdown component so that importing it + * does not pull in the heavy PrismJS syntax-highlighting bundle. + */ +export const InlineMarkdown: FC = (props) => { + const { children, allowedElements = [], className, components = {} } = props; + + return ( + <>{children}, + + a: ({ href, target, children }) => ( + + {children} + + ), + + code: ({ node, className, children, style, ...props }) => ( + + {children} + + ), + + ...components, + }} + > + {children} + + ); +}; + +export const MemoizedInlineMarkdown = memo(InlineMarkdown, isEqual); diff --git a/site/src/components/Markdown/Markdown.stories.tsx b/site/src/components/Markdown/Markdown.stories.tsx index d4dc77b61e..f89ced11ff 100644 --- a/site/src/components/Markdown/Markdown.stories.tsx +++ b/site/src/components/Markdown/Markdown.stories.tsx @@ -102,6 +102,6 @@ export const GFMAlertWithInlineFormatting: Story = { > [!IMPORTANT] > Larger **instances** cost more. Choose based on your workload. > Test line two - `, + `, }, }; diff --git a/site/src/components/Markdown/Markdown.tsx b/site/src/components/Markdown/Markdown.tsx index 1a197d7a0c..3332e4936a 100644 --- a/site/src/components/Markdown/Markdown.tsx +++ b/site/src/components/Markdown/Markdown.tsx @@ -85,14 +85,8 @@ export const Markdown: FC = (props) => { ) : ( ({ - padding: "1px 4px", - background: theme.palette.divider, - borderRadius: 4, - color: theme.palette.text.primary, - fontSize: 14, - })} - {...props} + className="rounded-sm bg-border px-1 py-px text-[14px] text-content-primary" + {...restProps} > {children} @@ -155,80 +149,7 @@ export const Markdown: FC = (props) => { ); }; -interface InlineMarkdownProps { - /** - * The Markdown text to parse and render - */ - children: string; - - /** - * Additional element types to allow. - * Allows italic, bold, links, and inline code snippets by default. - * eg. `["ol", "ul", "li"]` to support lists. - */ - allowedElements?: readonly string[]; - - className?: string; - - /** - * Can override the behavior of the generated elements - */ - components?: Options["components"]; -} - -/** - * Supports a strict subset of Markdown that behaves well as inline/confined content. - */ -export const InlineMarkdown: FC = (props) => { - const { children, allowedElements = [], className, components = {} } = props; - - return ( - <>{children}, - - a: ({ href, target, children }) => ( - - {children} - - ), - - code: ({ node, className, children, style, ...props }) => ( - ({ - padding: "1px 4px", - background: theme.palette.divider, - borderRadius: 4, - color: theme.palette.text.primary, - fontSize: 14, - })} - {...props} - > - {children} - - ), - - ...components, - }} - > - {children} - - ); -}; - export const MemoizedMarkdown = memo(Markdown, isEqual); -export const MemoizedInlineMarkdown = memo(InlineMarkdown, isEqual); const githubFlavoredMarkdownAlertTypes = [ "tip", diff --git a/site/src/modules/dashboard/AnnouncementBanners/AnnouncementBannerView.tsx b/site/src/modules/dashboard/AnnouncementBanners/AnnouncementBannerView.tsx index 6dd3b93137..cb8a2d8ea0 100644 --- a/site/src/modules/dashboard/AnnouncementBanners/AnnouncementBannerView.tsx +++ b/site/src/modules/dashboard/AnnouncementBanners/AnnouncementBannerView.tsx @@ -1,5 +1,5 @@ import type { FC } from "react"; -import { InlineMarkdown } from "#/components/Markdown/Markdown"; +import { InlineMarkdown } from "#/components/Markdown/InlineMarkdown"; import { readableForegroundColor } from "#/utils/colors"; interface AnnouncementBannerViewProps { diff --git a/site/src/modules/resources/ResourceCard.tsx b/site/src/modules/resources/ResourceCard.tsx index 5beeb8f8ca..71e99544ed 100644 --- a/site/src/modules/resources/ResourceCard.tsx +++ b/site/src/modules/resources/ResourceCard.tsx @@ -4,7 +4,7 @@ import type { WorkspaceAgent, WorkspaceResource } from "#/api/typesGenerated"; import { ChevronDownIcon } from "#/components/AnimatedIcons/ChevronDown"; import { Button } from "#/components/Button/Button"; import { CopyableValue } from "#/components/CopyableValue/CopyableValue"; -import { MemoizedInlineMarkdown } from "#/components/Markdown/Markdown"; +import { MemoizedInlineMarkdown } from "#/components/Markdown/InlineMarkdown"; import { Stack } from "#/components/Stack/Stack"; import { Tooltip, diff --git a/site/src/modules/workspaces/WorkspaceUpdateDialogs.tsx b/site/src/modules/workspaces/WorkspaceUpdateDialogs.tsx index 66b53b43b0..59d5348884 100644 --- a/site/src/modules/workspaces/WorkspaceUpdateDialogs.tsx +++ b/site/src/modules/workspaces/WorkspaceUpdateDialogs.tsx @@ -9,7 +9,7 @@ import type { WorkspaceBuildParameter, } from "#/api/typesGenerated"; import { ConfirmDialog } from "#/components/Dialogs/ConfirmDialog/ConfirmDialog"; -import { MemoizedInlineMarkdown } from "#/components/Markdown/Markdown"; +import { MemoizedInlineMarkdown } from "#/components/Markdown/InlineMarkdown"; import { UpdateBuildParametersDialog } from "#/modules/workspaces/WorkspaceMoreActions/UpdateBuildParametersDialog"; import { UpdateBuildParametersDialogExperimental } from "#/modules/workspaces/WorkspaceMoreActions/UpdateBuildParametersDialogExperimental"; diff --git a/site/src/pages/TemplatePage/TemplatePageHeader.tsx b/site/src/pages/TemplatePage/TemplatePageHeader.tsx index f5b9a6d4dd..90b30cceb4 100644 --- a/site/src/pages/TemplatePage/TemplatePageHeader.tsx +++ b/site/src/pages/TemplatePage/TemplatePageHeader.tsx @@ -31,7 +31,7 @@ import { DropdownMenuTrigger, } from "#/components/DropdownMenu/DropdownMenu"; import { Margins } from "#/components/Margins/Margins"; -import { MemoizedInlineMarkdown } from "#/components/Markdown/Markdown"; +import { MemoizedInlineMarkdown } from "#/components/Markdown/InlineMarkdown"; import { PageHeader, PageHeaderSubtitle, diff --git a/site/src/pages/WorkspacePage/ResourceMetadata.tsx b/site/src/pages/WorkspacePage/ResourceMetadata.tsx index e4b698c26d..fed4e41786 100644 --- a/site/src/pages/WorkspacePage/ResourceMetadata.tsx +++ b/site/src/pages/WorkspacePage/ResourceMetadata.tsx @@ -2,7 +2,7 @@ import type { Interpolation, Theme } from "@emotion/react"; import { Children, type FC, type HTMLAttributes } from "react"; import type { WorkspaceResource } from "#/api/typesGenerated"; import { CopyableValue } from "#/components/CopyableValue/CopyableValue"; -import { MemoizedInlineMarkdown } from "#/components/Markdown/Markdown"; +import { MemoizedInlineMarkdown } from "#/components/Markdown/InlineMarkdown"; import { SensitiveValue } from "#/modules/resources/SensitiveValue"; type ResourceMetadataProps = Omit, "resource"> & { diff --git a/site/src/pages/WorkspacePage/WorkspaceNotifications/WorkspaceNotifications.tsx b/site/src/pages/WorkspacePage/WorkspaceNotifications/WorkspaceNotifications.tsx index 607c95af8e..bc34eb6c97 100644 --- a/site/src/pages/WorkspacePage/WorkspaceNotifications/WorkspaceNotifications.tsx +++ b/site/src/pages/WorkspacePage/WorkspaceNotifications/WorkspaceNotifications.tsx @@ -10,7 +10,7 @@ import type { Workspace, WorkspaceBuild, } from "#/api/typesGenerated"; -import { MemoizedInlineMarkdown } from "#/components/Markdown/Markdown"; +import { MemoizedInlineMarkdown } from "#/components/Markdown/InlineMarkdown"; import { useDashboard } from "#/modules/dashboard/useDashboard"; import { TemplateUpdateMessage } from "#/modules/templates/TemplateUpdateMessage"; import { getAgentHealthIssue } from "#/modules/workspaces/health";