From 700098f20da4c3526f359ae68454479fceff650e Mon Sep 17 00:00:00 2001
From: Moritz <moritz@schwrr.com>
Date: Fri, 24 Jan 2025 10:16:05 +0100
Subject: [PATCH] feat: add title prop to SyntaxHighlight (#589)

* feat: add title prop to SyntaxHighlight

* fix type

* correct prerendering
---
 .../zudoku/src/lib/components/SyntaxHighlight.tsx  | 14 ++++++++++++++
 packages/zudoku/src/lib/util/MdxComponents.tsx     |  3 ++-
 2 files changed, 16 insertions(+), 1 deletion(-)

diff --git a/packages/zudoku/src/lib/components/SyntaxHighlight.tsx b/packages/zudoku/src/lib/components/SyntaxHighlight.tsx
index 15f0c6cb..8867d5bb 100644
--- a/packages/zudoku/src/lib/components/SyntaxHighlight.tsx
+++ b/packages/zudoku/src/lib/components/SyntaxHighlight.tsx
@@ -41,6 +41,7 @@ type SyntaxHighlightProps = {
   copyable?: boolean;
   showLanguageIndicator?: boolean;
   language?: string;
+  title?: string;
 } & Omit<HighlightProps, "children" | "language">;
 
 const remapLang = {
@@ -50,6 +51,7 @@ const remapLang = {
 export const SyntaxHighlight = ({
   copyable = true,
   language = "plain",
+  title,
   ...props
 }: SyntaxHighlightProps) => {
   const { resolvedTheme } = useTheme();
@@ -70,12 +72,18 @@ export const SyntaxHighlight = ({
     <ClientOnly
       fallback={
         <div className="relative group">
+          {title && (
+            <div className="text-xs text-muted-foreground absolute top-2 font-mono border-b w-full pb-2 px-4 ">
+              {title}
+            </div>
+          )}
           <pre
             className={cn(
               "relative scrollbar overflow-x-auto",
               props.className,
               props.noBackground ? "!bg-transparent" : themeColorClasses,
               props.wrapLines && "whitespace-pre-wrap break-words",
+              title && "pt-10",
             )}
           >
             {props.code}
@@ -95,6 +103,11 @@ export const SyntaxHighlight = ({
       >
         {({ className, style, tokens, getLineProps, getTokenProps }) => (
           <div className="relative group">
+            {title && (
+              <div className="text-xs text-muted-foreground absolute top-2 font-mono border-b w-full pb-2 px-4 ">
+                {title}
+              </div>
+            )}
             <pre
               className={cn(
                 "relative scrollbar overflow-x-auto",
@@ -102,6 +115,7 @@ export const SyntaxHighlight = ({
                 props.className,
                 props.noBackground && "!bg-transparent",
                 props.wrapLines && "whitespace-pre-wrap break-words",
+                title && "pt-10",
               )}
               style={style}
             >
diff --git a/packages/zudoku/src/lib/util/MdxComponents.tsx b/packages/zudoku/src/lib/util/MdxComponents.tsx
index 2c738e40..38051b28 100644
--- a/packages/zudoku/src/lib/util/MdxComponents.tsx
+++ b/packages/zudoku/src/lib/util/MdxComponents.tsx
@@ -61,7 +61,7 @@ export const MdxComponents = {
   code: ({ className, children, ...props }) => {
     // `inline` provided by the rehype plugin, as react-markdown removed support for that
     // eslint-disable-next-line @typescript-eslint/no-explicit-any
-    const inline = (props as any).inline;
+    const { inline, title } = props as Record<string, string | boolean>;
 
     if (inline === true || inline === "true") {
       return <InlineCode className={className}>{children}</InlineCode>;
@@ -75,6 +75,7 @@ export const MdxComponents = {
         className="rounded-xl p-4 border dark:!bg-foreground/10 dark:border-transparent"
         showLanguageIndicator
         code={String(children).trim()}
+        title={String(title) ?? undefined}
       />
     );
   },