Files
kandev/apps/web/components/editors/monaco/monaco-code-block.tsx
T
2026-02-16 09:55:04 +00:00

83 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 w-fit max-w-full min-w-[50%]">
<button
onClick={() => copy(code)}
className={cn(
'absolute top-2 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' },
overviewRulerLanes: 0,
hideCursorInOverviewRuler: true,
overviewRulerBorder: false,
automaticLayout: true,
domReadOnly: true,
contextmenu: false,
padding: { top: 4, bottom: 4 },
}}
className="rounded-md overflow-hidden text-xs"
loading={null}
/>
</div>
);
}