mirror of
https://github.com/ItzCrazyKns/Perplexica.git
synced 2025-12-20 10:38:15 +00:00
feat(renderers): add code block
This commit is contained in:
102
src/components/MessageRenderer/CodeBlock/CodeBlockDarkTheme.ts
Normal file
102
src/components/MessageRenderer/CodeBlock/CodeBlockDarkTheme.ts
Normal file
@@ -0,0 +1,102 @@
|
|||||||
|
import type { CSSProperties } from 'react';
|
||||||
|
|
||||||
|
const darkTheme = {
|
||||||
|
'hljs-comment': {
|
||||||
|
color: '#8b949e',
|
||||||
|
},
|
||||||
|
'hljs-quote': {
|
||||||
|
color: '#8b949e',
|
||||||
|
},
|
||||||
|
'hljs-variable': {
|
||||||
|
color: '#ff7b72',
|
||||||
|
},
|
||||||
|
'hljs-template-variable': {
|
||||||
|
color: '#ff7b72',
|
||||||
|
},
|
||||||
|
'hljs-tag': {
|
||||||
|
color: '#ff7b72',
|
||||||
|
},
|
||||||
|
'hljs-name': {
|
||||||
|
color: '#ff7b72',
|
||||||
|
},
|
||||||
|
'hljs-selector-id': {
|
||||||
|
color: '#ff7b72',
|
||||||
|
},
|
||||||
|
'hljs-selector-class': {
|
||||||
|
color: '#ff7b72',
|
||||||
|
},
|
||||||
|
'hljs-regexp': {
|
||||||
|
color: '#ff7b72',
|
||||||
|
},
|
||||||
|
'hljs-deletion': {
|
||||||
|
color: '#ff7b72',
|
||||||
|
},
|
||||||
|
'hljs-number': {
|
||||||
|
color: '#f2cc60',
|
||||||
|
},
|
||||||
|
'hljs-built_in': {
|
||||||
|
color: '#f2cc60',
|
||||||
|
},
|
||||||
|
'hljs-builtin-name': {
|
||||||
|
color: '#f2cc60',
|
||||||
|
},
|
||||||
|
'hljs-literal': {
|
||||||
|
color: '#f2cc60',
|
||||||
|
},
|
||||||
|
'hljs-type': {
|
||||||
|
color: '#f2cc60',
|
||||||
|
},
|
||||||
|
'hljs-params': {
|
||||||
|
color: '#f2cc60',
|
||||||
|
},
|
||||||
|
'hljs-meta': {
|
||||||
|
color: '#f2cc60',
|
||||||
|
},
|
||||||
|
'hljs-link': {
|
||||||
|
color: '#f2cc60',
|
||||||
|
},
|
||||||
|
'hljs-attribute': {
|
||||||
|
color: '#58a6ff',
|
||||||
|
},
|
||||||
|
'hljs-string': {
|
||||||
|
color: '#7ee787',
|
||||||
|
},
|
||||||
|
'hljs-symbol': {
|
||||||
|
color: '#7ee787',
|
||||||
|
},
|
||||||
|
'hljs-bullet': {
|
||||||
|
color: '#7ee787',
|
||||||
|
},
|
||||||
|
'hljs-addition': {
|
||||||
|
color: '#7ee787',
|
||||||
|
},
|
||||||
|
'hljs-title': {
|
||||||
|
color: '#79c0ff',
|
||||||
|
},
|
||||||
|
'hljs-section': {
|
||||||
|
color: '#79c0ff',
|
||||||
|
},
|
||||||
|
'hljs-keyword': {
|
||||||
|
color: '#c297ff',
|
||||||
|
},
|
||||||
|
'hljs-selector-tag': {
|
||||||
|
color: '#c297ff',
|
||||||
|
},
|
||||||
|
hljs: {
|
||||||
|
display: 'block',
|
||||||
|
overflowX: 'auto',
|
||||||
|
background: '#0d1117',
|
||||||
|
color: '#c9d1d9',
|
||||||
|
padding: '0.75em',
|
||||||
|
border: '1px solid #21262d',
|
||||||
|
borderRadius: '10px',
|
||||||
|
},
|
||||||
|
'hljs-emphasis': {
|
||||||
|
fontStyle: 'italic',
|
||||||
|
},
|
||||||
|
'hljs-strong': {
|
||||||
|
fontWeight: 'bold',
|
||||||
|
},
|
||||||
|
} satisfies Record<string, CSSProperties>;
|
||||||
|
|
||||||
|
export default darkTheme;
|
||||||
102
src/components/MessageRenderer/CodeBlock/CodeBlockLightTheme.ts
Normal file
102
src/components/MessageRenderer/CodeBlock/CodeBlockLightTheme.ts
Normal file
@@ -0,0 +1,102 @@
|
|||||||
|
import type { CSSProperties } from 'react';
|
||||||
|
|
||||||
|
const lightTheme = {
|
||||||
|
'hljs-comment': {
|
||||||
|
color: '#6e7781',
|
||||||
|
},
|
||||||
|
'hljs-quote': {
|
||||||
|
color: '#6e7781',
|
||||||
|
},
|
||||||
|
'hljs-variable': {
|
||||||
|
color: '#d73a49',
|
||||||
|
},
|
||||||
|
'hljs-template-variable': {
|
||||||
|
color: '#d73a49',
|
||||||
|
},
|
||||||
|
'hljs-tag': {
|
||||||
|
color: '#d73a49',
|
||||||
|
},
|
||||||
|
'hljs-name': {
|
||||||
|
color: '#d73a49',
|
||||||
|
},
|
||||||
|
'hljs-selector-id': {
|
||||||
|
color: '#d73a49',
|
||||||
|
},
|
||||||
|
'hljs-selector-class': {
|
||||||
|
color: '#d73a49',
|
||||||
|
},
|
||||||
|
'hljs-regexp': {
|
||||||
|
color: '#d73a49',
|
||||||
|
},
|
||||||
|
'hljs-deletion': {
|
||||||
|
color: '#d73a49',
|
||||||
|
},
|
||||||
|
'hljs-number': {
|
||||||
|
color: '#b08800',
|
||||||
|
},
|
||||||
|
'hljs-built_in': {
|
||||||
|
color: '#b08800',
|
||||||
|
},
|
||||||
|
'hljs-builtin-name': {
|
||||||
|
color: '#b08800',
|
||||||
|
},
|
||||||
|
'hljs-literal': {
|
||||||
|
color: '#b08800',
|
||||||
|
},
|
||||||
|
'hljs-type': {
|
||||||
|
color: '#b08800',
|
||||||
|
},
|
||||||
|
'hljs-params': {
|
||||||
|
color: '#b08800',
|
||||||
|
},
|
||||||
|
'hljs-meta': {
|
||||||
|
color: '#b08800',
|
||||||
|
},
|
||||||
|
'hljs-link': {
|
||||||
|
color: '#b08800',
|
||||||
|
},
|
||||||
|
'hljs-attribute': {
|
||||||
|
color: '#0a64ae',
|
||||||
|
},
|
||||||
|
'hljs-string': {
|
||||||
|
color: '#22863a',
|
||||||
|
},
|
||||||
|
'hljs-symbol': {
|
||||||
|
color: '#22863a',
|
||||||
|
},
|
||||||
|
'hljs-bullet': {
|
||||||
|
color: '#22863a',
|
||||||
|
},
|
||||||
|
'hljs-addition': {
|
||||||
|
color: '#22863a',
|
||||||
|
},
|
||||||
|
'hljs-title': {
|
||||||
|
color: '#005cc5',
|
||||||
|
},
|
||||||
|
'hljs-section': {
|
||||||
|
color: '#005cc5',
|
||||||
|
},
|
||||||
|
'hljs-keyword': {
|
||||||
|
color: '#6f42c1',
|
||||||
|
},
|
||||||
|
'hljs-selector-tag': {
|
||||||
|
color: '#6f42c1',
|
||||||
|
},
|
||||||
|
hljs: {
|
||||||
|
display: 'block',
|
||||||
|
overflowX: 'auto',
|
||||||
|
background: '#ffffff',
|
||||||
|
color: '#24292f',
|
||||||
|
padding: '0.75em',
|
||||||
|
border: '1px solid #e8edf1',
|
||||||
|
borderRadius: '10px',
|
||||||
|
},
|
||||||
|
'hljs-emphasis': {
|
||||||
|
fontStyle: 'italic',
|
||||||
|
},
|
||||||
|
'hljs-strong': {
|
||||||
|
fontWeight: 'bold',
|
||||||
|
},
|
||||||
|
} satisfies Record<string, CSSProperties>;
|
||||||
|
|
||||||
|
export default lightTheme;
|
||||||
64
src/components/MessageRenderer/CodeBlock/index.tsx
Normal file
64
src/components/MessageRenderer/CodeBlock/index.tsx
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
'use client';
|
||||||
|
|
||||||
|
import { CheckIcon, CopyIcon } from '@phosphor-icons/react';
|
||||||
|
import React, { useEffect, useMemo, useState } from 'react';
|
||||||
|
import { useTheme } from 'next-themes';
|
||||||
|
import SyntaxHighlighter from 'react-syntax-highlighter';
|
||||||
|
import darkTheme from './CodeBlockDarkTheme';
|
||||||
|
import lightTheme from './CodeBlockLightTheme';
|
||||||
|
|
||||||
|
const CodeBlock = ({
|
||||||
|
language,
|
||||||
|
children,
|
||||||
|
}: {
|
||||||
|
language: string;
|
||||||
|
children: React.ReactNode;
|
||||||
|
}) => {
|
||||||
|
const { resolvedTheme } = useTheme();
|
||||||
|
const [mounted, setMounted] = useState(false);
|
||||||
|
|
||||||
|
const [copied, setCopied] = useState(false);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setMounted(true);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const syntaxTheme = useMemo(() => {
|
||||||
|
if (!mounted) return lightTheme;
|
||||||
|
return resolvedTheme === 'dark' ? darkTheme : lightTheme;
|
||||||
|
}, [mounted, resolvedTheme]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="relative">
|
||||||
|
<button
|
||||||
|
className="absolute top-2 right-2 p-1"
|
||||||
|
onClick={() => {
|
||||||
|
navigator.clipboard.writeText(children as string);
|
||||||
|
setCopied(true);
|
||||||
|
setTimeout(() => setCopied(false), 2000);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{copied ? (
|
||||||
|
<CheckIcon
|
||||||
|
size={16}
|
||||||
|
className="absolute top-2 right-2 text-black/70 dark:text-white/70"
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<CopyIcon
|
||||||
|
size={16}
|
||||||
|
className="absolute top-2 right-2 transition duration-200 text-black/70 dark:text-white/70 hover:text-gray-800/70 hover:dark:text-gray-300/70"
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</button>
|
||||||
|
<SyntaxHighlighter
|
||||||
|
language={language}
|
||||||
|
style={syntaxTheme}
|
||||||
|
showInlineLineNumbers
|
||||||
|
>
|
||||||
|
{children as string}
|
||||||
|
</SyntaxHighlighter>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default CodeBlock;
|
||||||
Reference in New Issue
Block a user