File size: 4,850 Bytes
94e1b2f | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 | // 代码预览组件
import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter';
import { oneLight, vscDarkPlus } from 'react-syntax-highlighter/dist/esm/styles/prism';
import { memo, useEffect, useState } from 'react';
import { useI18n } from '../i18n';
interface CodeViewProps {
code: string;
editable?: boolean;
onChange?: (value: string) => void;
disabled?: boolean;
}
export const CodeView = memo(function CodeView({ code, editable = false, onChange, disabled = false }: CodeViewProps) {
const { t } = useI18n();
const [copied, setCopied] = useState(false);
const [isEditing, setIsEditing] = useState(false);
const [isDark, setIsDark] = useState(
typeof document !== 'undefined' && document.documentElement.classList.contains('dark')
);
useEffect(() => {
if (typeof document === 'undefined') {
return;
}
const updateThemeState = () => {
setIsDark(document.documentElement.classList.contains('dark'));
};
updateThemeState();
const observer = new MutationObserver(updateThemeState);
observer.observe(document.documentElement, {
attributes: true,
attributeFilter: ['class']
});
return () => observer.disconnect();
}, []);
const handleCopy = async () => {
await navigator.clipboard.writeText(code);
setCopied(true);
setTimeout(() => setCopied(false), 2000);
};
const textareaClassName = [
'w-full h-full resize-none bg-transparent p-4 text-[0.75rem] leading-relaxed overflow-auto',
'font-mono text-text-primary/90 focus:outline-none',
disabled ? 'opacity-60 cursor-not-allowed' : ''
]
.filter(Boolean)
.join(' ');
return (
<div className="h-full flex flex-col bg-bg-secondary/30 rounded-2xl overflow-hidden">
{/* 顶部工具栏 */}
<div className="flex items-center justify-between px-4 py-2.5">
<h3 className="text-xs font-medium text-text-secondary/80 uppercase tracking-wide">{t('codeView.title')}</h3>
<div className="flex items-center gap-3">
{editable && (
<button
onClick={() => setIsEditing((prev) => !prev)}
disabled={disabled}
className="text-xs text-text-secondary/70 hover:text-accent transition-colors disabled:opacity-50 disabled:cursor-not-allowed"
>
{isEditing ? t('common.preview') : t('common.edit')}
</button>
)}
<button
onClick={handleCopy}
className="text-xs text-text-secondary/70 hover:text-accent transition-colors flex items-center gap-1.5"
>
{copied ? (
<>
<svg className="w-3.5 h-3.5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M5 13l4 4L19 7" />
</svg>
{t('common.copied')}
</>
) : (
<>
<svg className="w-3.5 h-3.5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z" />
</svg>
{t('common.copy')}
</>
)}
</button>
</div>
</div>
{/* 代码区域 */}
<div className="flex-1 overflow-hidden">
{editable && isEditing ? (
<textarea
value={code}
onChange={(event) => onChange?.(event.target.value)}
className={textareaClassName}
disabled={disabled}
spellCheck={false}
/>
) : (
<div className="h-full overflow-auto">
<SyntaxHighlighter
language="python"
style={isDark ? vscDarkPlus : oneLight}
customStyle={{
margin: 0,
padding: '1rem',
fontSize: '0.75rem',
lineHeight: '1.6',
minHeight: '100%',
width: '100%',
boxSizing: 'border-box',
fontFamily: 'Monaco, Cascadia Code, Roboto Mono, monospace',
background: 'transparent',
whiteSpace: 'pre',
wordBreak: 'normal',
overflow: 'visible'
}}
codeTagProps={{
style: {
fontFamily: 'Monaco, Cascadia Code, Roboto Mono, monospace',
whiteSpace: 'pre',
wordBreak: 'normal'
}
}}
showLineNumbers
>
{code}
</SyntaxHighlighter>
</div>
)}
</div>
</div>
);
});
|