From 4a6d0da72abd66a0ed2ca76bf26c05e463d98191 Mon Sep 17 00:00:00 2001 From: Bot DocuBook Date: Tue, 12 Aug 2025 14:32:00 +0000 Subject: [PATCH] chore: Sync package version v1.16.1 --- app/page.tsx | 2 +- components/markdown/PreMdx.tsx | 114 +++++++++++++++++++++++++++++---- lib/markdown.ts | 47 +++++++++++++- package.json | 4 +- styles/globals.css | 3 +- styles/syntax.css | 84 ++++++++++++++++++++++++ 6 files changed, 237 insertions(+), 17 deletions(-) diff --git a/app/page.tsx b/app/page.tsx index fe59767..10c9130 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -25,7 +25,7 @@ export default function Home() { )} > - 🚀 New Version - Release v1.16.0 + 🚀 New Version - Release v1.16.1 diff --git a/components/markdown/PreMdx.tsx b/components/markdown/PreMdx.tsx index 172211c..6380210 100644 --- a/components/markdown/PreMdx.tsx +++ b/components/markdown/PreMdx.tsx @@ -1,19 +1,109 @@ -import { ComponentProps } from "react"; +import { type ComponentProps } from "react"; import Copy from "./CopyMdx"; +import { + SiJavascript, + SiTypescript, + SiReact, + SiPython, + SiGo, + SiPhp, + SiRuby, + SiSwift, + SiKotlin, + SiHtml5, + SiCss3, + SiSass, + SiPostgresql, + SiGraphql, + SiYaml, + SiToml, + SiDocker, + SiNginx, + SiGit, + SiGnubash, + SiMarkdown, +} from "react-icons/si"; +import { FaJava, FaCode } from "react-icons/fa"; +import { TbJson } from "react-icons/tb"; + +type PreProps = ComponentProps<"pre"> & { + raw?: string; + "data-title"?: string; +}; + +// Component to display an icon based on the programming language +const LanguageIcon = ({ lang }: { lang: string }) => { + const iconProps = { className: "w-4 h-4" }; + const languageToIconMap: Record = { + gitignore: , + docker: , + dockerfile: , + nginx: , + sql: , + graphql: , + yaml: , + yml: , + toml: , + json: , + md: , + markdown: , + bash: , + sh: , + shell: , + swift: , + kotlin: , + kt: , + kts: , + rb: , + ruby: , + php: , + go: , + py: , + python: , + java: , + tsx: , + typescript: , + ts: , + jsx: , + js: , + javascript: , + html: , + css: , + scss: , + sass: , + }; + return languageToIconMap[lang] || ; +}; + +// Function to extract the language from className +function getLanguage(className: string = ""): string { + const match = className.match(/language-(\w+)/); + return match ? match[1] : "default"; +} + +export default function Pre({ children, raw, ...rest }: PreProps) { + const { "data-title": title, className, ...restProps } = rest; + const language = getLanguage(className); + const hasTitle = !!title; -export default function Pre({ - children, - raw, - ...rest -}: ComponentProps<"pre"> & { raw?: string }) { return ( -
-
- +
+
+ {raw && }
-
-
{children}
+ {hasTitle && ( +
+
+ + {title} +
+
+ )} +
+
+          {children}
+        
); -} +} \ No newline at end of file diff --git a/lib/markdown.ts b/lib/markdown.ts index a59eca6..9c1ec01 100644 --- a/lib/markdown.ts +++ b/lib/markdown.ts @@ -8,7 +8,7 @@ import rehypeSlug from "rehype-slug"; import rehypeCodeTitles from "rehype-code-titles"; import { page_routes, ROUTES } from "./routes-config"; import { visit } from "unist-util-visit"; -import type { Node } from "unist"; +import type { Node, Parent } from "unist"; import matter from "gray-matter"; // Type definitions for unist-util-visit @@ -16,6 +16,7 @@ interface Element extends Node { type: string; tagName?: string; properties?: Record & { + className?: string[]; raw?: string; }; children?: Node[]; @@ -77,6 +78,49 @@ const components = { AccordionGroup }; +// helper function to handle rehype code titles, since by default we can't inject into the className of rehype-code-titles +const handleCodeTitles = () => (tree: Node) => { + visit(tree, "element", (node: Element, index: number | null, parent: Parent | null) => { + // Ensure the visited node is valid + if (!parent || index === null || node.tagName !== 'div') { + return; + } + + // Check if this is the title div from rehype-code-titles + const isTitleDiv = node.properties?.className?.includes('rehype-code-title'); + if (!isTitleDiv) { + return; + } + + // Find the next
 element, skipping over other nodes like whitespace text
+    let nextElement = null;
+    for (let i = index + 1; i < parent.children.length; i++) {
+      const sibling = parent.children[i];
+      if (sibling.type === 'element') {
+        nextElement = sibling as Element;
+        break;
+      }
+    }
+
+    // If the next element is a 
, move the title to it
+    if (nextElement && nextElement.tagName === 'pre') {
+      const titleNode = node.children?.[0] as TextNode;
+      if (titleNode && titleNode.type === 'text') {
+        if (!nextElement.properties) {
+          nextElement.properties = {};
+        }
+        nextElement.properties['data-title'] = titleNode.value;
+        
+        // Remove the original title div
+        parent.children.splice(index, 1);
+        
+        // Return the same index to continue visiting from the correct position
+        return index; 
+      }
+    }
+  });
+};
+
 // can be used for other pages like blogs, Guides etc
 async function parseMdx(rawMdx: string) {
   return await compileMDX({
@@ -87,6 +131,7 @@ async function parseMdx(rawMdx: string) {
         rehypePlugins: [
           preProcess,
           rehypeCodeTitles,
+          handleCodeTitles,
           rehypePrism,
           rehypeSlug,
           rehypeAutolinkHeadings,
diff --git a/package.json b/package.json
index 303530a..17250be 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
   "name": "docubook",
-  "version": "1.16.0",
+  "version": "1.16.1",
   "private": true,
   "scripts": {
     "dev": "next dev",
@@ -30,13 +30,13 @@
     "framer-motion": "^12.4.1",
     "geist": "^1.3.1",
     "gray-matter": "^4.0.3",
-    "install": "^0.13.0",
     "lucide-react": "^0.511.0",
     "next": "^14.2.6",
     "next-mdx-remote": "^5.0.0",
     "next-themes": "^0.3.0",
     "react": "^18.3.1",
     "react-dom": "^18.3.1",
+    "react-icons": "^5.5.0",
     "rehype-autolink-headings": "^7.1.0",
     "rehype-code-titles": "^1.2.0",
     "rehype-prism-plus": "^2.0.0",
diff --git a/styles/globals.css b/styles/globals.css
index 4903c18..84669c5 100644
--- a/styles/globals.css
+++ b/styles/globals.css
@@ -90,6 +90,7 @@ pre>code {
   display: grid;
   max-width: inherit !important;
   padding: 14px 0 !important;
+  border: 0 !important;
 }
 
 .code-line {
@@ -138,4 +139,4 @@ pre>code {
         background-position: 0% 0%;
       }
     }
-  }
\ No newline at end of file
+  }
diff --git a/styles/syntax.css b/styles/syntax.css
index 0459326..c6b15c7 100644
--- a/styles/syntax.css
+++ b/styles/syntax.css
@@ -93,3 +93,87 @@
   border: none;
   border-radius: 8px; /* Sudut melengkung pada iframe */
 }
+
+/* ======================================================================== */
+/* Custom styling for code blocks */
+/* ======================================================================== */
+
+.code-block-container {
+  position: relative;
+  margin: 1.5rem 0;
+  border: 1px solid hsl(var(--border));
+  overflow: hidden;
+  font-size: 0.875rem;
+  border-radius: 0.75rem;
+}
+
+.code-block-header {
+  display: flex;
+  align-items: center;
+  gap: 0.5rem;
+  background-color: hsl(var(--muted)); 
+  padding: 0.5rem 1rem;
+  border-bottom: 1px solid hsl(var(--border));
+  color: hsl(var(--muted-foreground));
+  font-family: monospace;
+  font-size: 0.8rem;
+}
+
+.code-block-actions {
+  position: absolute;
+  top: 0.5rem;
+  right: 0.75rem;
+  z-index: 10;
+}
+.code-block-actions button {
+  color: hsl(var(--muted-foreground));
+  transition: color 0.2s ease-in-out;
+}
+.code-block-actions button:hover {
+  color: hsl(var(--foreground));
+}
+
+
+.code-block-body pre[class*="language-"] {
+  margin: 0 !important;
+  padding: 0 !important;
+  background: transparent !important;
+}
+
+.line-numbers-wrapper {
+  position: absolute;
+  top: 0;
+  left: 0;
+  width: 3rem;
+  padding-top: 1rem;
+  text-align: right;
+  color: var(--line-number-color); 
+  user-select: none;
+}
+
+.line-highlight {
+  position: absolute;
+  left: 0;
+  right: 0;
+  background: hsl(var(--primary) / 0.1); 
+  border-left: 2px solid hsl(var(--primary));
+  pointer-events: none;
+}
+
+.code-block-body pre[data-line-numbers="true"] .line-highlight {
+  padding-left: 3.5rem;
+}
+
+.code-block-body::-webkit-scrollbar {
+  height: 8px;
+}
+.code-block-body::-webkit-scrollbar-track {
+  background: transparent;
+}
+.code-block-body::-webkit-scrollbar-thumb {
+  background: hsl(var(--border));
+  border-radius: 4px;
+}
+.code-block-body::-webkit-scrollbar-thumb:hover {
+  background: hsl(var(--muted));
+}
\ No newline at end of file