Skip to content

Commit

Permalink
feat: fix markdown theme rendering in codeblocks
Browse files Browse the repository at this point in the history
  • Loading branch information
drewstone committed Nov 3, 2024
1 parent a4d90d7 commit b3a6415
Show file tree
Hide file tree
Showing 4 changed files with 147 additions and 45 deletions.
98 changes: 64 additions & 34 deletions components/GithubFileReaderDisplay.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { useTheme } from "next-themes";
import React, { useEffect, useState } from "react";
import { FaGithub, FaLink, FaSpinner } from "react-icons/fa";
import { getHighlighter, type Highlighter, bundledLanguages } from 'shiki';
import { useTheme } from 'next-themes';

interface GithubFileReaderDisplayProps {
url: string;
Expand All @@ -9,6 +10,37 @@ interface GithubFileReaderDisplayProps {
title?: string;
}

// Create a singleton promise for the highlighter
let highlighterPromise: Promise<Highlighter> | null = null;

// Singleton function to get or create the highlighter
const getShikiHighlighter = () => {
if (!highlighterPromise) {
highlighterPromise = getHighlighter({
themes: ['github-dark', 'github-light'],
langs: Object.keys(bundledLanguages),
});
}
return highlighterPromise;
};

// Language detection utility
const getLanguage = (url: string) => {
const extension = url.split(".").pop()?.toLowerCase();
switch (extension) {
case "rs":
return "rust";
case "ts":
case "tsx":
return "typescript";
case "js":
case "jsx":
return "javascript";
default:
return "plaintext";
}
};

const GithubFileReaderDisplay: React.FC<GithubFileReaderDisplayProps> = ({
url,
fromLine = 1,
Expand All @@ -18,32 +50,20 @@ const GithubFileReaderDisplay: React.FC<GithubFileReaderDisplayProps> = ({
const [content, setContent] = useState<string>("");
const [loading, setLoading] = useState(true);
const [error, setError] = useState<string | null>(null);
const { theme } = useTheme();

const getLanguage = (url: string) => {
const extension = url.split(".").pop()?.toLowerCase();
switch (extension) {
case "rs":
return "rust";
case "ts":
case "tsx":
return "typescript";
case "js":
case "jsx":
return "javascript";
default:
return "text";
}
};
const { theme: currentTheme } = useTheme();

useEffect(() => {
const fetchGithubContent = async () => {
const fetchAndHighlightContent = async () => {
try {
const rawUrl = url
.replace("github.com", "raw.githubusercontent.com")
.replace("/blob/", "/");

const response = await fetch(rawUrl);
const [response, highlighter] = await Promise.all([
fetch(rawUrl),
getShikiHighlighter()
]);

if (!response.ok) {
throw new Error("Failed to fetch file content");
}
Expand All @@ -52,16 +72,33 @@ const GithubFileReaderDisplay: React.FC<GithubFileReaderDisplayProps> = ({
const lines = text.split("\n");
const selectedLines = lines.slice(fromLine - 1, toLine || lines.length);
const codeContent = selectedLines.join("\n");
setContent(codeContent);

// Set the theme based on current theme
const theme = currentTheme === 'dark' ? 'github-dark' : 'github-light';

// Highlight the code with the current theme and line numbers
const highlightedCode = highlighter.codeToHtml(codeContent, {
lang: getLanguage(url),
theme: theme,
lineOptions: Array.from({ length: selectedLines.length }, (_, i) => ({
line: i + 1,
classes: [`line-${i + fromLine}`]
})),
});

// Wrap the highlighted code with a div that sets the starting line number
const wrappedCode = `<div style="--start-line: ${fromLine}">${highlightedCode}</div>`;
setContent(wrappedCode);
setLoading(false);
} catch (err) {
console.error('Highlighting error:', err);
setError(err instanceof Error ? err.message : "An error occurred");
setLoading(false);
}
};

fetchGithubContent();
}, [url, fromLine, toLine]);
fetchAndHighlightContent();
}, [url, fromLine, toLine, currentTheme]);

if (loading) {
return (
Expand All @@ -79,8 +116,6 @@ const GithubFileReaderDisplay: React.FC<GithubFileReaderDisplayProps> = ({
);
}

const language = getLanguage(url);

return (
<div className="my-6 overflow-hidden border border-gray-200 dark:border-gray-800 rounded-lg">
<div className="flex justify-between items-center px-4 py-2 bg-gray-50 dark:bg-gray-800/50 border-b border-gray-200 dark:border-gray-800">
Expand All @@ -107,15 +142,10 @@ const GithubFileReaderDisplay: React.FC<GithubFileReaderDisplayProps> = ({
</div>

<div className="nextra-code-block nx-relative">
<pre className="nx-bg-primary-700/5 nx-overflow-x-auto nx-font-medium nx-subpixel-antialiased dark:nx-bg-primary-300/10 nx-text-[.9em]">
<code
className={`language-${getLanguage(url)} nx-cdx`}
data-language={getLanguage(url)}
data-theme={theme}
>
{content}
</code>
</pre>
<div
className="nx-bg-primary-700/5 nx-overflow-x-auto nx-font-medium nx-subpixel-antialiased dark:nx-bg-primary-300/10 nx-text-[.9em]"
dangerouslySetInnerHTML={{ __html: content }}
/>
</div>
</div>
);
Expand Down
29 changes: 29 additions & 0 deletions globals.css
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,35 @@
}
}

/* Add these styles for line numbers */
.shiki {
background-color: transparent !important;
padding: 1rem 0;
}

.shiki code {
counter-reset: step;
counter-increment: step calc(var(--start-line, 1) - 1);
}

.shiki .line::before {
content: counter(step);
counter-increment: step;
width: 3rem;
margin-right: 1.5rem;
display: inline-block;
text-align: right;
color: #666;
border-right: 1px solid #404040;
padding-right: 1rem;
}

/* Dark mode adjustments */
.dark .shiki .line::before {
color: #888;
border-right-color: #404040;
}

@supports (
(-webkit-backdrop-filter: blur(1px)) or (backdrop-filter: blur(1px))
) {
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@
"remark-math": "4.0.0",
"shadcn-ui": "^0.8.0",
"sharp": "^0.31.3",
"shiki": "^1.2.1",
"shiki": "^1.22.2",
"styled-components": "^6.1.8",
"swr": "1.3.0",
"tailwind-merge": "^2.3.0",
Expand Down
63 changes: 53 additions & 10 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -2818,10 +2818,17 @@
"@types/hast" "^3.0.4"
hast-util-to-html "^9.0.2"

"@shikijs/[email protected]":
version "1.2.1"
resolved "https://registry.yarnpkg.com/@shikijs/core/-/core-1.2.1.tgz#df4bdfd20e0492681716eaca51c086a018b2bdb9"
integrity sha512-KaIS0H4EQ3KI2d++TjYqRNgwp8E3M/68e9veR4QtInzA7kKFgcjeiJqb80fuXW+blDy5fmd11PN9g9soz/3ANQ==
"@shikijs/[email protected]":
version "1.22.2"
resolved "https://registry.yarnpkg.com/@shikijs/core/-/core-1.22.2.tgz#9c22bd4cc8a4d6c062461cfd35e1faa6c617ca25"
integrity sha512-bvIQcd8BEeR1yFvOYv6HDiyta2FFVePbzeowf5pPS1avczrPK+cjmaxxh0nx5QzbON7+Sv0sQfQVciO7bN72sg==
dependencies:
"@shikijs/engine-javascript" "1.22.2"
"@shikijs/engine-oniguruma" "1.22.2"
"@shikijs/types" "1.22.2"
"@shikijs/vscode-textmate" "^9.3.0"
"@types/hast" "^3.0.4"
hast-util-to-html "^9.0.3"

"@shikijs/[email protected]":
version "1.17.7"
Expand All @@ -2832,6 +2839,15 @@
"@shikijs/vscode-textmate" "^9.2.2"
oniguruma-to-js "0.4.3"

"@shikijs/[email protected]":
version "1.22.2"
resolved "https://registry.yarnpkg.com/@shikijs/engine-javascript/-/engine-javascript-1.22.2.tgz#62e90dbd2ed1d78b972ad7d0a1f8ffaaf5e43279"
integrity sha512-iOvql09ql6m+3d1vtvP8fLCVCK7BQD1pJFmHIECsujB0V32BJ0Ab6hxk1ewVSMFA58FI0pR2Had9BKZdyQrxTw==
dependencies:
"@shikijs/types" "1.22.2"
"@shikijs/vscode-textmate" "^9.3.0"
oniguruma-to-js "0.4.3"

"@shikijs/[email protected]":
version "1.17.7"
resolved "https://registry.yarnpkg.com/@shikijs/engine-oniguruma/-/engine-oniguruma-1.17.7.tgz#ed5a890a156cb29803368d3506e6cf239e7057bd"
Expand All @@ -2840,6 +2856,14 @@
"@shikijs/types" "1.17.7"
"@shikijs/vscode-textmate" "^9.2.2"

"@shikijs/[email protected]":
version "1.22.2"
resolved "https://registry.yarnpkg.com/@shikijs/engine-oniguruma/-/engine-oniguruma-1.22.2.tgz#b12a44e3faf486e19fbcf8952f4b56b9b9b8d9b8"
integrity sha512-GIZPAGzQOy56mGvWMoZRPggn0dTlBf1gutV5TdceLCZlFNqWmuc7u+CzD0Gd9vQUTgLbrt0KLzz6FNprqYAxlA==
dependencies:
"@shikijs/types" "1.22.2"
"@shikijs/vscode-textmate" "^9.3.0"

"@shikijs/rehype@^1.12.1":
version "1.17.7"
resolved "https://registry.yarnpkg.com/@shikijs/rehype/-/rehype-1.17.7.tgz#44045fa1b6366f2074eec598f1a7a04769ffb93b"
Expand Down Expand Up @@ -2868,11 +2892,24 @@
"@shikijs/vscode-textmate" "^9.2.2"
"@types/hast" "^3.0.4"

"@shikijs/[email protected]":
version "1.22.2"
resolved "https://registry.yarnpkg.com/@shikijs/types/-/types-1.22.2.tgz#695a283f19963fe0638fc2646862ba5cfc4623a8"
integrity sha512-NCWDa6LGZqTuzjsGfXOBWfjS/fDIbDdmVDug+7ykVe1IKT4c1gakrvlfFYp5NhAXH/lyqLM8wsAPo5wNy73Feg==
dependencies:
"@shikijs/vscode-textmate" "^9.3.0"
"@types/hast" "^3.0.4"

"@shikijs/vscode-textmate@^9.2.2":
version "9.2.2"
resolved "https://registry.yarnpkg.com/@shikijs/vscode-textmate/-/vscode-textmate-9.2.2.tgz#24571f50625c7cd075f9efe0def8b9d2c0930ada"
integrity sha512-TMp15K+GGYrWlZM8+Lnj9EaHEFmOen0WJBrfa17hF7taDOYthuPPV0GWzfd/9iMij0akS/8Yw2ikquH7uVi/fg==

"@shikijs/vscode-textmate@^9.3.0":
version "9.3.0"
resolved "https://registry.yarnpkg.com/@shikijs/vscode-textmate/-/vscode-textmate-9.3.0.tgz#b2f1776e488c1d6c2b6cd129bab62f71bbc9c7ab"
integrity sha512-jn7/7ky30idSkd/O5yDBfAnVt+JJpepofP/POZ1iMOxK59cOfqIgg/Dj0eFsjOTMw+4ycJN0uhZH/Eb0bs/EUA==

"@substrate/ss58-registry@^1.44.0":
version "1.47.0"
resolved "https://registry.yarnpkg.com/@substrate/ss58-registry/-/ss58-registry-1.47.0.tgz#99b11fd3c16657f5eae483b3df7c545ca756d1fc"
Expand Down Expand Up @@ -6206,7 +6243,7 @@ hast-util-to-estree@^2.0.0:
unist-util-position "^4.0.0"
zwitch "^2.0.0"

hast-util-to-html@^9.0.2:
hast-util-to-html@^9.0.2, hast-util-to-html@^9.0.3:
version "9.0.3"
resolved "https://registry.yarnpkg.com/hast-util-to-html/-/hast-util-to-html-9.0.3.tgz#a9999a0ba6b4919576a9105129fead85d37f302b"
integrity sha512-M17uBDzMJ9RPCqLMO92gNNUDuBSq10a25SDBI08iCCxmorf4Yy6sYHK57n9WAbRAAaU+DuR4W6GN9K4DFZesYg==
Expand Down Expand Up @@ -10251,12 +10288,17 @@ shiki@^0.14.3:
vscode-oniguruma "^1.7.0"
vscode-textmate "^8.0.0"

shiki@^1.2.1:
version "1.2.1"
resolved "https://registry.yarnpkg.com/shiki/-/shiki-1.2.1.tgz#d48bc631f4172afff62a36b2f395f29403b50e71"
integrity sha512-u+XW6o0vCkUNlneZb914dLO+AayEIwK5tI62WeS//R5HIXBFiYaj/Hc5xcq27Yh83Grr4JbNtUBV8W6zyK4hWg==
shiki@^1.22.2:
version "1.22.2"
resolved "https://registry.yarnpkg.com/shiki/-/shiki-1.22.2.tgz#ed109a3d0850504ad5a1edf8496470a2121c5b7b"
integrity sha512-3IZau0NdGKXhH2bBlUk4w1IHNxPh6A5B2sUpyY+8utLu2j/h1QpFkAaUA1bAMxOWWGtTWcAh531vnS4NJKS/lA==
dependencies:
"@shikijs/core" "1.2.1"
"@shikijs/core" "1.22.2"
"@shikijs/engine-javascript" "1.22.2"
"@shikijs/engine-oniguruma" "1.22.2"
"@shikijs/types" "1.22.2"
"@shikijs/vscode-textmate" "^9.3.0"
"@types/hast" "^3.0.4"

side-channel@^1.0.4, side-channel@^1.0.6:
version "1.0.6"
Expand Down Expand Up @@ -10448,6 +10490,7 @@ stream-length@^1.0.1, stream-length@^1.0.2:
bluebird "^2.6.2"

"string-width-cjs@npm:string-width@^4.2.0", string-width@^4.1.0:
name string-width-cjs
version "4.2.3"
resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
Expand Down

0 comments on commit b3a6415

Please sign in to comment.