Skip to content

Commit

Permalink
fix(components): extra new line in highlighted code bock
Browse files Browse the repository at this point in the history
  • Loading branch information
iipanda committed Nov 13, 2024
1 parent 2d6e024 commit ba20c53
Show file tree
Hide file tree
Showing 2 changed files with 2 additions and 2 deletions.
2 changes: 1 addition & 1 deletion apps/www/public/r/markdown-renderer.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
"files": [
{
"path": "ui/markdown-renderer.tsx",
"content": "import React, { Suspense } from \"react\"\nimport Markdown from \"react-markdown\"\nimport remarkGfm from \"remark-gfm\"\n\nimport { cn } from \"@/lib/utils\"\nimport { CopyButton } from \"@/registry/default/ui/copy-button\"\n\ninterface MarkdownRendererProps {\n children: string\n}\n\nexport function MarkdownRenderer({ children }: MarkdownRendererProps) {\n return (\n <Markdown\n remarkPlugins={[remarkGfm]}\n components={COMPONENTS}\n className=\"space-y-3\"\n >\n {children}\n </Markdown>\n )\n}\n\ninterface HighlightedPre extends React.HTMLAttributes<HTMLPreElement> {\n children: string\n language: string\n}\n\nconst HighlightedPre = React.memo(\n async ({ children, language, ...props }: HighlightedPre) => {\n const { codeToTokens, bundledLanguages } = await import(\"shiki\")\n\n if (!(language in bundledLanguages)) {\n return <pre {...props}>{children}</pre>\n }\n\n const { tokens } = await codeToTokens(children, {\n lang: language as keyof typeof bundledLanguages,\n defaultColor: false,\n themes: {\n light: \"github-light\",\n dark: \"github-dark\",\n },\n })\n\n return (\n <pre {...props}>\n <code>\n {tokens.map((line, lineIndex) => (\n <>\n <span key={lineIndex}>\n {line.map((token, tokenIndex) => {\n const style =\n typeof token.htmlStyle === \"string\"\n ? undefined\n : token.htmlStyle\n\n return (\n <span\n key={tokenIndex}\n className=\"text-shiki-light bg-shiki-light-bg dark:text-shiki-dark dark:bg-shiki-dark-bg\"\n style={style}\n >\n {token.content}\n </span>\n )\n })}\n </span>\n {\"\\n\"}\n </>\n ))}\n </code>\n </pre>\n )\n }\n)\nHighlightedPre.displayName = \"HighlightedCode\"\n\ninterface CodeBlockProps extends React.HTMLAttributes<HTMLPreElement> {\n children: React.ReactNode\n className?: string\n language: string\n}\n\nconst CodeBlock = ({\n children,\n className,\n language,\n ...restProps\n}: CodeBlockProps) => {\n const code =\n typeof children === \"string\"\n ? children\n : childrenTakeAllStringContents(children)\n\n const preClass = cn(\n \"overflow-x-scroll rounded-md border bg-background/50 p-4 font-mono text-sm [scrollbar-width:none]\",\n className\n )\n\n return (\n <div className=\"group/code relative mb-4\">\n <Suspense\n fallback={\n <pre className={preClass} {...restProps}>\n {children}\n </pre>\n }\n >\n <HighlightedPre language={language} className={preClass}>\n {code}\n </HighlightedPre>\n </Suspense>\n\n <div className=\"invisible absolute right-2 top-2 flex space-x-1 rounded-lg p-1 opacity-0 transition-all duration-200 group-hover/code:visible group-hover/code:opacity-100\">\n <CopyButton content={code} copyMessage=\"Copied code to clipboard\" />\n </div>\n </div>\n )\n}\n\nfunction childrenTakeAllStringContents(element: any): string {\n if (typeof element === \"string\") {\n return element\n }\n\n if (element?.props?.children) {\n let children = element.props.children\n\n if (Array.isArray(children)) {\n return children\n .map((child) => childrenTakeAllStringContents(child))\n .join(\"\")\n } else {\n return childrenTakeAllStringContents(children)\n }\n }\n\n return \"\"\n}\n\nconst COMPONENTS = {\n h1: withClass(\"h1\", \"text-2xl font-semibold\"),\n h2: withClass(\"h2\", \"font-semibold text-xl\"),\n h3: withClass(\"h3\", \"font-semibold text-lg\"),\n h4: withClass(\"h4\", \"font-semibold text-base\"),\n h5: withClass(\"h5\", \"font-medium\"),\n strong: withClass(\"strong\", \"font-semibold\"),\n a: withClass(\"a\", \"text-primary underline underline-offset-2\"),\n blockquote: withClass(\"blockquote\", \"border-l-2 border-primary pl-4\"),\n code: ({ children, className, node, ...rest }: any) => {\n const match = /language-(\\w+)/.exec(className || \"\")\n return match ? (\n <CodeBlock className={className} language={match[1]} {...rest}>\n {children}\n </CodeBlock>\n ) : (\n <code\n className={cn(\n \"font-mono [:not(pre)>&]:rounded-md [:not(pre)>&]:bg-background/50 [:not(pre)>&]:px-1 [:not(pre)>&]:py-0.5\"\n )}\n {...rest}\n >\n {children}\n </code>\n )\n },\n pre: ({ children }: any) => children,\n ol: withClass(\"ol\", \"list-decimal space-y-2 pl-6\"),\n ul: withClass(\"ul\", \"list-disc space-y-2 pl-6\"),\n li: withClass(\"li\", \"my-1.5\"),\n table: withClass(\n \"table\",\n \"w-full border-collapse overflow-y-auto rounded-md border border-foreground/20\"\n ),\n th: withClass(\n \"th\",\n \"border border-foreground/20 px-4 py-2 text-left font-bold [&[align=center]]:text-center [&[align=right]]:text-right\"\n ),\n td: withClass(\n \"td\",\n \"border border-foreground/20 px-4 py-2 text-left [&[align=center]]:text-center [&[align=right]]:text-right\"\n ),\n tr: withClass(\"tr\", \"m-0 border-t p-0 even:bg-muted\"),\n p: withClass(\"p\", \"whitespace-pre-wrap\"),\n hr: withClass(\"hr\", \"border-foreground/20\"),\n}\n\nfunction withClass(Tag: keyof JSX.IntrinsicElements, classes: string) {\n const Component = ({ node, ...props }: any) => (\n <Tag className={classes} {...props} />\n )\n Component.displayName = Tag\n return Component\n}\n\nexport default MarkdownRenderer\n",
"content": "import React, { Suspense } from \"react\"\nimport Markdown from \"react-markdown\"\nimport remarkGfm from \"remark-gfm\"\n\nimport { cn } from \"@/lib/utils\"\nimport { CopyButton } from \"@/registry/default/ui/copy-button\"\n\ninterface MarkdownRendererProps {\n children: string\n}\n\nexport function MarkdownRenderer({ children }: MarkdownRendererProps) {\n return (\n <Markdown\n remarkPlugins={[remarkGfm]}\n components={COMPONENTS}\n className=\"space-y-3\"\n >\n {children}\n </Markdown>\n )\n}\n\ninterface HighlightedPre extends React.HTMLAttributes<HTMLPreElement> {\n children: string\n language: string\n}\n\nconst HighlightedPre = React.memo(\n async ({ children, language, ...props }: HighlightedPre) => {\n const { codeToTokens, bundledLanguages } = await import(\"shiki\")\n\n if (!(language in bundledLanguages)) {\n return <pre {...props}>{children}</pre>\n }\n\n const { tokens } = await codeToTokens(children, {\n lang: language as keyof typeof bundledLanguages,\n defaultColor: false,\n themes: {\n light: \"github-light\",\n dark: \"github-dark\",\n },\n })\n\n return (\n <pre {...props}>\n <code>\n {tokens.map((line, lineIndex) => (\n <>\n <span key={lineIndex}>\n {line.map((token, tokenIndex) => {\n const style =\n typeof token.htmlStyle === \"string\"\n ? undefined\n : token.htmlStyle\n\n return (\n <span\n key={tokenIndex}\n className=\"text-shiki-light bg-shiki-light-bg dark:text-shiki-dark dark:bg-shiki-dark-bg\"\n style={style}\n >\n {token.content}\n </span>\n )\n })}\n </span>\n {lineIndex !== tokens.length - 1 && \"\\n\"}\n </>\n ))}\n </code>\n </pre>\n )\n }\n)\nHighlightedPre.displayName = \"HighlightedCode\"\n\ninterface CodeBlockProps extends React.HTMLAttributes<HTMLPreElement> {\n children: React.ReactNode\n className?: string\n language: string\n}\n\nconst CodeBlock = ({\n children,\n className,\n language,\n ...restProps\n}: CodeBlockProps) => {\n const code =\n typeof children === \"string\"\n ? children\n : childrenTakeAllStringContents(children)\n\n const preClass = cn(\n \"overflow-x-scroll rounded-md border bg-background/50 p-4 font-mono text-sm [scrollbar-width:none]\",\n className\n )\n\n return (\n <div className=\"group/code relative mb-4\">\n <Suspense\n fallback={\n <pre className={preClass} {...restProps}>\n {children}\n </pre>\n }\n >\n <HighlightedPre language={language} className={preClass}>\n {code}\n </HighlightedPre>\n </Suspense>\n\n <div className=\"invisible absolute right-2 top-2 flex space-x-1 rounded-lg p-1 opacity-0 transition-all duration-200 group-hover/code:visible group-hover/code:opacity-100\">\n <CopyButton content={code} copyMessage=\"Copied code to clipboard\" />\n </div>\n </div>\n )\n}\n\nfunction childrenTakeAllStringContents(element: any): string {\n if (typeof element === \"string\") {\n return element\n }\n\n if (element?.props?.children) {\n let children = element.props.children\n\n if (Array.isArray(children)) {\n return children\n .map((child) => childrenTakeAllStringContents(child))\n .join(\"\")\n } else {\n return childrenTakeAllStringContents(children)\n }\n }\n\n return \"\"\n}\n\nconst COMPONENTS = {\n h1: withClass(\"h1\", \"text-2xl font-semibold\"),\n h2: withClass(\"h2\", \"font-semibold text-xl\"),\n h3: withClass(\"h3\", \"font-semibold text-lg\"),\n h4: withClass(\"h4\", \"font-semibold text-base\"),\n h5: withClass(\"h5\", \"font-medium\"),\n strong: withClass(\"strong\", \"font-semibold\"),\n a: withClass(\"a\", \"text-primary underline underline-offset-2\"),\n blockquote: withClass(\"blockquote\", \"border-l-2 border-primary pl-4\"),\n code: ({ children, className, node, ...rest }: any) => {\n const match = /language-(\\w+)/.exec(className || \"\")\n return match ? (\n <CodeBlock className={className} language={match[1]} {...rest}>\n {children}\n </CodeBlock>\n ) : (\n <code\n className={cn(\n \"font-mono [:not(pre)>&]:rounded-md [:not(pre)>&]:bg-background/50 [:not(pre)>&]:px-1 [:not(pre)>&]:py-0.5\"\n )}\n {...rest}\n >\n {children}\n </code>\n )\n },\n pre: ({ children }: any) => children,\n ol: withClass(\"ol\", \"list-decimal space-y-2 pl-6\"),\n ul: withClass(\"ul\", \"list-disc space-y-2 pl-6\"),\n li: withClass(\"li\", \"my-1.5\"),\n table: withClass(\n \"table\",\n \"w-full border-collapse overflow-y-auto rounded-md border border-foreground/20\"\n ),\n th: withClass(\n \"th\",\n \"border border-foreground/20 px-4 py-2 text-left font-bold [&[align=center]]:text-center [&[align=right]]:text-right\"\n ),\n td: withClass(\n \"td\",\n \"border border-foreground/20 px-4 py-2 text-left [&[align=center]]:text-center [&[align=right]]:text-right\"\n ),\n tr: withClass(\"tr\", \"m-0 border-t p-0 even:bg-muted\"),\n p: withClass(\"p\", \"whitespace-pre-wrap\"),\n hr: withClass(\"hr\", \"border-foreground/20\"),\n}\n\nfunction withClass(Tag: keyof JSX.IntrinsicElements, classes: string) {\n const Component = ({ node, ...props }: any) => (\n <Tag className={classes} {...props} />\n )\n Component.displayName = Tag\n return Component\n}\n\nexport default MarkdownRenderer\n",
"type": "registry:ui",
"target": ""
}
Expand Down
2 changes: 1 addition & 1 deletion apps/www/registry/default/ui/markdown-renderer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ const HighlightedPre = React.memo(
)
})}
</span>
{"\n"}
{lineIndex !== tokens.length - 1 && "\n"}
</>
))}
</code>
Expand Down

0 comments on commit ba20c53

Please sign in to comment.