84 lines
2.6 KiB
TypeScript
84 lines
2.6 KiB
TypeScript
"use client";
|
|
|
|
import { useTheme } from "next-themes";
|
|
import Editor from "@monaco-editor/react";
|
|
import { IconCheck, IconCopy } from "@tabler/icons-react";
|
|
import { cn } from "@/lib/utils";
|
|
import { getMonacoLanguageFromName } from "@/lib/editor/language-map";
|
|
import { EDITOR_FONT_FAMILY } from "@/lib/theme/editor-theme";
|
|
import { useCopyToClipboard } from "@/hooks/use-copy-to-clipboard";
|
|
import { initMonacoThemes } from "./monaco-init";
|
|
|
|
initMonacoThemes();
|
|
|
|
type MonacoCodeBlockProps = {
|
|
children: React.ReactNode;
|
|
className?: string;
|
|
};
|
|
|
|
export function MonacoCodeBlock({ children, className }: MonacoCodeBlockProps) {
|
|
const { copied, copy } = useCopyToClipboard();
|
|
const { resolvedTheme } = useTheme();
|
|
|
|
const code = String(children).replace(/\n$/, "");
|
|
const lang = className?.replace("language-", "").toLowerCase() ?? "";
|
|
const language = getMonacoLanguageFromName(lang);
|
|
const lineCount = code.split("\n").length;
|
|
// Compact height: ~16px per line + 8px padding
|
|
const height = Math.min(Math.max(lineCount * 16 + 8, 32), 500);
|
|
|
|
return (
|
|
<div className="relative group/code-block my-4 max-w-full">
|
|
<button
|
|
onClick={() => copy(code)}
|
|
className={cn(
|
|
"absolute top-1 right-2 z-10",
|
|
"p-1.5 rounded-md",
|
|
"bg-white/10 hover:bg-white/20",
|
|
"transition-all duration-200",
|
|
"opacity-0 group-hover/code-block:opacity-100",
|
|
"cursor-pointer",
|
|
)}
|
|
title="Copy code"
|
|
>
|
|
{copied ? (
|
|
<IconCheck className="h-3 w-3 text-green-400" />
|
|
) : (
|
|
<IconCopy className="h-3 w-3 text-gray-400" />
|
|
)}
|
|
</button>
|
|
|
|
<Editor
|
|
height={height}
|
|
language={language}
|
|
value={code}
|
|
theme={resolvedTheme === "dark" ? "kandev-dark" : "kandev-light"}
|
|
options={{
|
|
readOnly: true,
|
|
fontSize: 12,
|
|
fontFamily: EDITOR_FONT_FAMILY,
|
|
lineHeight: 16,
|
|
minimap: { enabled: false },
|
|
wordWrap: "on",
|
|
scrollBeyondLastLine: false,
|
|
lineNumbers: "off",
|
|
glyphMargin: false,
|
|
folding: false,
|
|
renderLineHighlight: "none",
|
|
scrollbar: { vertical: "hidden", horizontal: "hidden", alwaysConsumeMouseWheel: false },
|
|
overviewRulerLanes: 0,
|
|
hideCursorInOverviewRuler: true,
|
|
overviewRulerBorder: false,
|
|
automaticLayout: true,
|
|
domReadOnly: true,
|
|
contextmenu: false,
|
|
mouseWheelZoom: false,
|
|
padding: { top: 4, bottom: 4 },
|
|
}}
|
|
className="rounded-md overflow-hidden text-xs"
|
|
loading={null}
|
|
/>
|
|
</div>
|
|
);
|
|
}
|