feat: Add HTML preview to code blocks
Adds an HTML preview feature to the code block component, allowing users to view the rendered output of their HTML code snippets. This improves the user experience by providing a more interactive and informative way to understand the code.
This commit is contained in:
parent
fd5339d90a
commit
7be993c057
@ -1,5 +1,5 @@
|
|||||||
import { Tooltip } from "antd"
|
import { Tooltip, Modal } from "antd"
|
||||||
import { CheckIcon, ClipboardIcon } from "lucide-react"
|
import { CheckIcon, ClipboardIcon, EyeIcon, Maximize2Icon } from "lucide-react"
|
||||||
import { FC, useState } from "react"
|
import { FC, useState } from "react"
|
||||||
import { useTranslation } from "react-i18next"
|
import { useTranslation } from "react-i18next"
|
||||||
import { Prism as SyntaxHighlighter } from "react-syntax-highlighter"
|
import { Prism as SyntaxHighlighter } from "react-syntax-highlighter"
|
||||||
@ -10,9 +10,27 @@ interface Props {
|
|||||||
value: string
|
value: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export const CodeBlock: FC<Props> =({ language, value }) => {
|
export const CodeBlock: FC<Props> = ({ language, value }) => {
|
||||||
const [isBtnPressed, setIsBtnPressed] = useState(false)
|
const [isBtnPressed, setIsBtnPressed] = useState(false)
|
||||||
|
const [previewVisible, setPreviewVisible] = useState(false)
|
||||||
const { t } = useTranslation("common")
|
const { t } = useTranslation("common")
|
||||||
|
|
||||||
|
const handleCopy = () => {
|
||||||
|
navigator.clipboard.writeText(value)
|
||||||
|
setIsBtnPressed(true)
|
||||||
|
setTimeout(() => {
|
||||||
|
setIsBtnPressed(false)
|
||||||
|
}, 4000)
|
||||||
|
}
|
||||||
|
|
||||||
|
const handlePreview = () => {
|
||||||
|
setPreviewVisible(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
const handlePreviewClose = () => {
|
||||||
|
setPreviewVisible(false)
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className="code relative text-base font-sans codeblock bg-zinc-950 rounded-md overflow-hidden">
|
<div className="code relative text-base font-sans codeblock bg-zinc-950 rounded-md overflow-hidden">
|
||||||
@ -20,16 +38,19 @@ export const CodeBlock: FC<Props> =({ language, value }) => {
|
|||||||
<span className="text-xs lowercase text-gray-200">{language}</span>
|
<span className="text-xs lowercase text-gray-200">{language}</span>
|
||||||
|
|
||||||
<div className="flex items-center">
|
<div className="flex items-center">
|
||||||
|
{language.toLowerCase() === "html" && (
|
||||||
|
<Tooltip title={t("preview")}>
|
||||||
|
<button
|
||||||
|
onClick={handlePreview}
|
||||||
|
className="flex gap-1.5 items-center rounded bg-none p-1 text-xs text-gray-200 hover:bg-gray-700 hover:text-gray-100 focus:outline-none">
|
||||||
|
<EyeIcon className="h-4 w-4" />
|
||||||
|
</button>
|
||||||
|
</Tooltip>
|
||||||
|
)}
|
||||||
<Tooltip title={t("copyToClipboard")}>
|
<Tooltip title={t("copyToClipboard")}>
|
||||||
<button
|
<button
|
||||||
onClick={() => {
|
onClick={handleCopy}
|
||||||
navigator.clipboard.writeText(value)
|
className="flex gap-1.5 items-center rounded bg-none p-1 text-xs text-gray-200 hover:bg-gray-700 hover:text-gray-100 focus:outline-none">
|
||||||
setIsBtnPressed(true)
|
|
||||||
setTimeout(() => {
|
|
||||||
setIsBtnPressed(false)
|
|
||||||
}, 4000)
|
|
||||||
}}
|
|
||||||
className="flex gap-1.5 items-center rounded bg-none p-1 text-xs text-gray-200 hover:bg-gray-700 hover:text-gray-100 focus:outline-none focus:ring-2 focus:ring-gray-500 focus:ring-offset-2 focus:ring-offset-gray-100">
|
|
||||||
{!isBtnPressed ? (
|
{!isBtnPressed ? (
|
||||||
<ClipboardIcon className="h-4 w-4" />
|
<ClipboardIcon className="h-4 w-4" />
|
||||||
) : (
|
) : (
|
||||||
@ -61,6 +82,24 @@ export const CodeBlock: FC<Props> =({ language, value }) => {
|
|||||||
{value}
|
{value}
|
||||||
</SyntaxHighlighter>
|
</SyntaxHighlighter>
|
||||||
</div>
|
</div>
|
||||||
|
{previewVisible && (
|
||||||
|
<Modal
|
||||||
|
open={previewVisible}
|
||||||
|
onCancel={handlePreviewClose}
|
||||||
|
footer={null}
|
||||||
|
width="80%"
|
||||||
|
zIndex={999999}
|
||||||
|
centered
|
||||||
|
bodyStyle={{ padding: 0 }}>
|
||||||
|
<div className="relative w-full h-[80vh]">
|
||||||
|
<iframe
|
||||||
|
srcDoc={value}
|
||||||
|
title="HTML Preview"
|
||||||
|
className="w-full h-full"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</Modal>
|
||||||
|
)}
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -61,7 +61,7 @@ export const PlaygroundChat = () => {
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
{!isAtBottom && (
|
{!isAtBottom && (
|
||||||
<div className="fixed md:bottom-44 bottom-36 z-[9999999] left-0 right-0 flex justify-center">
|
<div className="fixed md:bottom-44 bottom-36 z-[99999] left-0 right-0 flex justify-center">
|
||||||
<button
|
<button
|
||||||
onClick={scrollToBottom}
|
onClick={scrollToBottom}
|
||||||
className="bg-gray-100 dark:bg-gray-800 p-1 rounded-full shadow-md hover:bg-gray-200 dark:hover:bg-gray-700 transition-colors duration-200">
|
className="bg-gray-100 dark:bg-gray-800 p-1 rounded-full shadow-md hover:bg-gray-200 dark:hover:bg-gray-700 transition-colors duration-200">
|
||||||
|
Loading…
x
Reference in New Issue
Block a user