mirror of
https://github.com/HKUDS/nanobot.git
synced 2026-05-24 10:32:45 +00:00
fix(webui): sync code block theme with dark mode toggle instantly
- Replace one-time DOM read with MutationObserver on <html> class - Remove hardcoded #0a0a0a background, let oneDark/oneLight own it - Add light-mode header/copy-button colors (bg-zinc-100 for light) - Bump font size from 13px to 14px, line-height from 1.55 to 1.6 - Add subtle border to distinguish code block edges
This commit is contained in:
parent
a7caee1186
commit
375b1f0328
@ -1,4 +1,4 @@
|
||||
import { useState } from "react";
|
||||
import { useCallback, useEffect, useState } from "react";
|
||||
import { Check, Copy } from "lucide-react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { Prism as SyntaxHighlighter } from "react-syntax-highlighter";
|
||||
@ -15,33 +15,67 @@ interface CodeBlockProps {
|
||||
className?: string;
|
||||
}
|
||||
|
||||
/** Read dark mode straight from the DOM — stays in sync with Tailwind's `dark:`. */
|
||||
function useIsDark() {
|
||||
const [isDark, setIsDark] = useState(() =>
|
||||
typeof document !== "undefined"
|
||||
? document.documentElement.classList.contains("dark")
|
||||
: true,
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
const el = document.documentElement;
|
||||
const observer = new MutationObserver(() => {
|
||||
setIsDark(el.classList.contains("dark"));
|
||||
});
|
||||
observer.observe(el, { attributeFilter: ["class"] });
|
||||
return () => observer.disconnect();
|
||||
}, []);
|
||||
|
||||
return isDark;
|
||||
}
|
||||
|
||||
export function CodeBlock({ language, code, className }: CodeBlockProps) {
|
||||
const { t } = useTranslation();
|
||||
const [copied, setCopied] = useState(false);
|
||||
const isDark = useIsDark();
|
||||
|
||||
const onCopy = () => {
|
||||
const onCopy = useCallback(() => {
|
||||
if (!navigator.clipboard) return;
|
||||
navigator.clipboard.writeText(code).then(() => {
|
||||
setCopied(true);
|
||||
setTimeout(() => setCopied(false), 1_500);
|
||||
});
|
||||
};
|
||||
|
||||
const isDark =
|
||||
typeof window !== "undefined"
|
||||
? document.documentElement.classList.contains("dark")
|
||||
: true;
|
||||
}, [code]);
|
||||
|
||||
return (
|
||||
<div className={cn("overflow-hidden rounded-lg", className)}>
|
||||
<div className="flex items-center justify-between bg-zinc-900 px-4 py-1.5 text-xs font-medium text-zinc-200">
|
||||
<span className="lowercase">
|
||||
<div
|
||||
className={cn(
|
||||
"overflow-hidden rounded-lg border",
|
||||
isDark ? "border-white/10" : "border-black/10",
|
||||
className,
|
||||
)}
|
||||
>
|
||||
<div
|
||||
className={cn(
|
||||
"flex items-center justify-between px-4 py-1.5 text-xs font-medium",
|
||||
isDark
|
||||
? "bg-zinc-800 text-zinc-300"
|
||||
: "bg-zinc-100 text-zinc-600",
|
||||
)}
|
||||
>
|
||||
<span className="lowercase font-mono">
|
||||
{language || t("code.fallbackLanguage")}
|
||||
</span>
|
||||
<button
|
||||
type="button"
|
||||
onClick={onCopy}
|
||||
className="inline-flex items-center gap-1 rounded px-1.5 py-0.5 text-zinc-300 transition-colors hover:bg-zinc-800 hover:text-zinc-100"
|
||||
className={cn(
|
||||
"inline-flex items-center gap-1 rounded px-1.5 py-0.5 font-mono transition-colors",
|
||||
isDark
|
||||
? "text-zinc-400 hover:bg-zinc-700 hover:text-zinc-200"
|
||||
: "text-zinc-500 hover:bg-zinc-200 hover:text-zinc-700",
|
||||
)}
|
||||
aria-label={t("code.copyAria")}
|
||||
>
|
||||
{copied ? (
|
||||
@ -58,9 +92,8 @@ export function CodeBlock({ language, code, className }: CodeBlockProps) {
|
||||
customStyle={{
|
||||
margin: 0,
|
||||
padding: "1rem",
|
||||
background: "var(--tw-prose-pre-bg, #0a0a0a)",
|
||||
fontSize: "0.8125rem",
|
||||
lineHeight: 1.55,
|
||||
fontSize: "0.875rem",
|
||||
lineHeight: 1.6,
|
||||
}}
|
||||
PreTag="pre"
|
||||
wrapLongLines
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user