diff --git a/README.md b/README.md index 8dcb9ce..7986e7a 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ Blog build on [Next.js](https://nextjs.org/) and [Notion Public API](https://www ## Features -1. Built using Next.js, TS, Tailwind CSS and other plugins(Prism、React-pdf and others). +1. Built using Next.js, TS, Tailwind CSS and other plugins(Shiki、React-pdf and others). 2. Use [Notion Public API](https://developers.notion.com/)。 3. Full support for dark mode 4. Support [Static Site Generation](https://nextjs.org/docs/pages/building-your-application/rendering/static-site-generation) @@ -52,7 +52,7 @@ Most common block types are supported. But some blocks information not supported | Callout | ✅ Yes | | | Child Databases | ❌ Missing | Not planned. | | Child page | ✅ Yes | | -| Code | ✅ Yes | Use [prismjs](https://prismjs.com/) | +| Code | ✅ Yes | Use [shiki](https://shiki.style/) | | Column list and column | ✅ Yes | | | Divider | ❌ Missing | | | Embed | ✅ Yes | | diff --git a/app/notion/_components/code.tsx b/app/notion/_components/code.tsx index 6f46d26..ffb0013 100644 --- a/app/notion/_components/code.tsx +++ b/app/notion/_components/code.tsx @@ -1,31 +1,35 @@ -"use client"; +// "use client"; import React from "react"; -import { highlightElement } from "prismjs"; +// import { highlightElement } from "prismjs"; // import { languages as Lan } from "prismjs"; // TODO: make it to dynamic -import "prismjs/components/prism-c.min.js"; -import "prismjs/components/prism-cpp.min.js"; -import "prismjs/components/prism-css-extras.min.js"; -import "prismjs/components/prism-css.min.js"; -import "prismjs/components/prism-javascript.min.js"; -import "prismjs/components/prism-js-extras.min.js"; -import "prismjs/components/prism-json.min.js"; -import "prismjs/components/prism-jsx.min.js"; -import "prismjs/components/prism-tsx.min.js"; -import "prismjs/components/prism-typescript.min.js"; -import "prismjs/components/prism-go.js"; -import "prismjs/components/prism-ini.js"; -import "prismjs/components/prism-shell-session.js"; -import "prismjs/components/prism-bash.min.js"; -import "prismjs/components/prism-java.min.js"; -import "prismjs/components/prism-python.min.js"; - -import "prismjs/themes/prism-tomorrow.css"; +// import "prismjs/components/prism-c.min.js"; +// import "prismjs/components/prism-cpp.min.js"; +// import "prismjs/components/prism-css-extras.min.js"; +// import "prismjs/components/prism-css.min.js"; +// import "prismjs/components/prism-javascript.min.js"; +// import "prismjs/components/prism-js-extras.min.js"; +// import "prismjs/components/prism-json.min.js"; +// import "prismjs/components/prism-jsx.min.js"; +// import "prismjs/components/prism-tsx.min.js"; +// import "prismjs/components/prism-typescript.min.js"; +// import "prismjs/components/prism-go.js"; +// import "prismjs/components/prism-ini.js"; +// import "prismjs/components/prism-shell-session.js"; +// import "prismjs/components/prism-bash.min.js"; +// import "prismjs/components/prism-java.min.js"; +// import "prismjs/components/prism-python.min.js"; + +// import "prismjs/themes/prism-tomorrow.css"; import { cn } from "@/lib/utils"; import RichText from "../text"; +import { bundledLanguages, getHighlighter } from "shiki"; +// import type { Highlighter, Lang, Theme } from 'shiki' + +// import type { BundledLanguage, BundledTheme } from "shiki"; // Import the types from shiki interface CodeBlockProps { block: any; @@ -33,47 +37,109 @@ interface CodeBlockProps { className?: string | undefined; } -export function CodeRender({ block, defaultLanguage, className }: CodeBlockProps) { - const codeRef = React.useRef(null); - React.useEffect(() => { - if (codeRef.current) { - try { - highlightElement(codeRef.current); - } catch (err) { - console.warn("prismjs highlight error", err); - } - } - }, [codeRef]); +// export function CodeRender({ block, defaultLanguage, className }: CodeBlockProps) { +// const codeRef = React.useRef(null); +// React.useEffect(() => { +// if (codeRef.current) { +// try { +// highlightElement(codeRef.current); +// } catch (err) { +// console.warn("prismjs highlight error", err); +// } +// } +// }, [codeRef]); + +// const { code, id, type } = block; +// if (type != "code" || !code) { +// return null; +// } + +// let lang = (code.language || defaultLanguage).toLowerCase(); +// if (lang == "c++") { +// lang = "cpp"; +// } +// const caption = code.caption; +// // Text cann't be nested in code element. So there are two ways to render code +// // Solution 1: Replace code with div tag. +// // Solution 2: Render all raw plain-text +// let codes = ""; +// code.rich_text?.map((item: any) => { +// codes += item?.plain_text; +// }); + +// // TODO: 1. add copy to clipboard feature +// // TODO: 2. support mermaid diagram. see https://stackblitz.com/edit/react-ts-mermaid?file=Mermaid.tsx +// return ( +//
+//
+//         
+//           {codes}
+//         
+//       
+ +// {caption && caption.length > 0 && ( +//
+// +//
+// )} +//
+ // ); + + // Solution 3: https://drupal-way.com/blog/nextjs-and-prismjs-integration + // const highlightedCode = Prism.highlight(codes, Lan[lang], lang); + // return
${highlightedCode}`}} + // /> +// } + +// let highlighter: Highlighter; + +// shiki render +export async function ShikiCodeRender({ block, defaultLanguage, className }: CodeBlockProps) { const { code, id, type } = block; if (type != "code" || !code) { return null; } let lang = (code.language || defaultLanguage).toLowerCase(); + + // https://shiki.style/languages if (lang == "c++") { lang = "cpp"; + } else if (lang == "assembly") { + lang = "asm"; + } + if (!(lang in bundledLanguages)) { + throw new Error('Unsupported language "' + lang + '"'); } - const caption = code.caption; - // Text cann't be nested in code element. So there are two ways to render code - // Solution 1: Replace code with div tag. - // Solution 2: Render all raw plain-text + const theme = 'github-dark'; let codes = ""; code.rich_text?.map((item: any) => { codes += item?.plain_text; }); - // TODO: 1. add copy to clipboard feature - // TODO: 2. support mermaid diagram. see https://stackblitz.com/edit/react-ts-mermaid?file=Mermaid.tsx - return ( -
-
-        
-          {codes}
-        
-      
+ const highlighter = await getHighlighter({ + langs: [lang], + themes: [theme], + }); + + const html = await highlighter.codeToHtml(codes, { + lang: lang, + theme: theme, + }); + // console.log(html); + + const caption = code.caption; + return ( +
+
${html}`, + }} + /> {caption && caption.length > 0 && (
@@ -81,10 +147,4 @@ export function CodeRender({ block, defaultLanguage, className }: CodeBlockProps )}
); - - // Solution 3: https://drupal-way.com/blog/nextjs-and-prismjs-integration - // const highlightedCode = Prism.highlight(codes, Lan[lang], lang); - // return
${highlightedCode}`}} - // /> } diff --git a/app/notion/_components/pdf.tsx b/app/notion/_components/pdf.tsx index ee37bf2..406d6f8 100644 --- a/app/notion/_components/pdf.tsx +++ b/app/notion/_components/pdf.tsx @@ -102,7 +102,7 @@ export function PDFRender({ block, className }: PDFBlockProps) { renderTextLayer={false} onLoadSuccess={onPageLoadSuccess} onRenderError={() => setLoading(false)} - // width={Math.max(pageWidth * 0.75, 320)} + width={Math.max(pageWidth * 0.75, 320)} />
diff --git a/app/notion/render.tsx b/app/notion/render.tsx index 0028328..238bbdb 100644 --- a/app/notion/render.tsx +++ b/app/notion/render.tsx @@ -6,7 +6,7 @@ import RichText from "./text"; import { bulletListStyle, numberListStyle } from "./tools"; import { cn } from "@/lib/utils"; import React from "react"; -import { CodeRender } from "./_components/code"; +import { ShikiCodeRender } from "./_components/code"; import { CalloutRender } from "./_components/callout"; import { ImageRender } from "./_components/image"; import { BookmarkPreviewRender } from "./_components/bookmark"; @@ -131,7 +131,7 @@ export function renderBlock(block: any, level: number = 1) { case "code": return ( Loading...
}> - + ); diff --git a/package-lock.json b/package-lock.json index 8c8095f..83236c4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -28,7 +28,6 @@ "lucide-react": "^0.312.0", "next": "^14.1.0", "next-themes": "^0.2.1", - "prismjs": "^1.29.0", "react": "^18", "react-dom": "^18", "react-pdf": "^7.7.0", @@ -41,7 +40,6 @@ "@next/eslint-plugin-next": "^14.1.0", "@types/ms": "^0.7.34", "@types/node": "^20", - "@types/prismjs": "^1.26.3", "@types/react": "^18", "@types/react-dom": "^18", "autoprefixer": "^10.0.1", @@ -50,6 +48,7 @@ "postcss": "^8", "prettier": "^3.2.5", "prettier-plugin-tailwindcss": "^0.5.11", + "shiki": "^1.1.7", "tailwindcss": "^3.3.0", "typescript": "^5" } @@ -1387,6 +1386,12 @@ "integrity": "sha512-UY+FGM/2jjMkzQLn8pxcHGMaVLh9aEitG3zY2CiY7XHdLiz3bZOwa6oDxNqEMv7zZkV+cj5DOdz0cQ1BP5Hjgw==", "dev": true }, + "node_modules/@shikijs/core": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/@shikijs/core/-/core-1.1.7.tgz", + "integrity": "sha512-gTYLUIuD1UbZp/11qozD3fWpUTuMqPSf3svDMMrL0UmlGU7D9dPw/V1FonwAorCUJBltaaESxq90jrSjQyGixg==", + "dev": true + }, "node_modules/@swc/helpers": { "version": "0.5.2", "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.2.tgz", @@ -1460,12 +1465,6 @@ "form-data": "^4.0.0" } }, - "node_modules/@types/prismjs": { - "version": "1.26.3", - "resolved": "https://registry.npmjs.org/@types/prismjs/-/prismjs-1.26.3.tgz", - "integrity": "sha512-A0D0aTXvjlqJ5ZILMz3rNfDBOx9hHxLZYv2by47Sm/pqW35zzjusrZTryatjN/Rf8Us2gZrJD+KeHbUSTux1Cw==", - "dev": true - }, "node_modules/@types/prop-types": { "version": "15.7.11", "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.11.tgz", @@ -5181,14 +5180,6 @@ } } }, - "node_modules/prismjs": { - "version": "1.29.0", - "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.29.0.tgz", - "integrity": "sha512-Kx/1w86q/epKcmte75LNrEoT+lX8pBpavuAbvJWRXar7Hz8jrtF+e3vY751p0R8H9HdArwaCTNDDzHg/ScJK1Q==", - "engines": { - "node": ">=6" - } - }, "node_modules/prop-types": { "version": "15.8.1", "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", @@ -5645,6 +5636,15 @@ "node": ">=8" } }, + "node_modules/shiki": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/shiki/-/shiki-1.1.7.tgz", + "integrity": "sha512-9kUTMjZtcPH3i7vHunA6EraTPpPOITYTdA5uMrvsJRexktqP0s7P3s9HVK80b4pP42FRVe03D7fT3NmJv2yYhw==", + "dev": true, + "dependencies": { + "@shikijs/core": "1.1.7" + } + }, "node_modules/side-channel": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", diff --git a/package.json b/package.json index aba0115..a756f73 100644 --- a/package.json +++ b/package.json @@ -32,7 +32,6 @@ "lucide-react": "^0.312.0", "next": "^14.1.0", "next-themes": "^0.2.1", - "prismjs": "^1.29.0", "react": "^18", "react-dom": "^18", "react-pdf": "^7.7.0", @@ -45,7 +44,6 @@ "@next/eslint-plugin-next": "^14.1.0", "@types/ms": "^0.7.34", "@types/node": "^20", - "@types/prismjs": "^1.26.3", "@types/react": "^18", "@types/react-dom": "^18", "autoprefixer": "^10.0.1", @@ -54,6 +52,7 @@ "postcss": "^8", "prettier": "^3.2.5", "prettier-plugin-tailwindcss": "^0.5.11", + "shiki": "^1.1.7", "tailwindcss": "^3.3.0", "typescript": "^5" }