mirror of
https://github.com/jnsahaj/tweakcn.git
synced 2026-08-29 07:14:31 +08:00
5fe4958db2
* Enhance Preview Panel and Support External Websites (#184) * feat: Add Block Viewer for Preview Panel * feat: support theme injection for external websites * refactor: Improve Naming and Composition * Support for Google Fonts in Preview script and Error Feedback (#202) * feat: Add support for loading Google Fonts in the embed script. * feat: Add error state and UI feedback * fix font loading by overriding classes * add reset button + update some ui * syntax highlighting plus copy changes * rename script * update ui for connection status * ui * zustand store for preview state * use nuqs for active preview tab * get shadows working * refactor * A * add tracking --------- Co-authored-by: Luis Llanes <137589205+llanesluis@users.noreply.github.com> Co-authored-by: Luis Llanes <luisllaboj@gmail.com>
125 lines
3.2 KiB
TypeScript
125 lines
3.2 KiB
TypeScript
"use client";
|
|
|
|
import { Button } from "@/components/ui/button";
|
|
import { cn } from "@/lib/utils";
|
|
import { CheckIcon, CopyIcon } from "lucide-react";
|
|
import type { ComponentProps, HTMLAttributes, ReactNode } from "react";
|
|
import { createContext, useContext, useState } from "react";
|
|
import { Prism as SyntaxHighlighter } from "react-syntax-highlighter";
|
|
import { oneDark, oneLight } from "react-syntax-highlighter/dist/esm/styles/prism";
|
|
import { useTheme } from "../theme-provider";
|
|
|
|
type CodeBlockContextType = {
|
|
code: string;
|
|
};
|
|
|
|
const CodeBlockContext = createContext<CodeBlockContextType>({
|
|
code: "",
|
|
});
|
|
|
|
export type CodeBlockProps = HTMLAttributes<HTMLDivElement> & {
|
|
code: string;
|
|
language: string;
|
|
showLineNumbers?: boolean;
|
|
children?: ReactNode;
|
|
};
|
|
|
|
export const CodeBlock = ({
|
|
code,
|
|
language,
|
|
showLineNumbers = false,
|
|
className,
|
|
children,
|
|
...props
|
|
}: CodeBlockProps) => {
|
|
const { theme } = useTheme();
|
|
return (
|
|
<CodeBlockContext.Provider value={{ code }}>
|
|
<div
|
|
className={cn(
|
|
"bg-background text-foreground relative w-full overflow-hidden rounded-md border",
|
|
className
|
|
)}
|
|
{...props}
|
|
>
|
|
<div className="relative">
|
|
<SyntaxHighlighter
|
|
className="overflow-hidden"
|
|
codeTagProps={{
|
|
className: "font-mono text-sm",
|
|
}}
|
|
customStyle={{
|
|
margin: 0,
|
|
padding: "1rem",
|
|
fontSize: "0.875rem",
|
|
background: "hsl(var(--background))",
|
|
color: "hsl(var(--foreground))",
|
|
}}
|
|
language={language}
|
|
lineNumberStyle={{
|
|
color: "hsl(var(--muted-foreground))",
|
|
paddingRight: "1rem",
|
|
minWidth: "2.5rem",
|
|
}}
|
|
showLineNumbers={showLineNumbers}
|
|
style={theme === "light" ? oneLight : oneDark}
|
|
>
|
|
{code}
|
|
</SyntaxHighlighter>
|
|
{children && (
|
|
<div className="absolute top-2 right-2 flex items-center gap-2">{children}</div>
|
|
)}
|
|
</div>
|
|
</div>
|
|
</CodeBlockContext.Provider>
|
|
);
|
|
};
|
|
|
|
export type CodeBlockCopyButtonProps = ComponentProps<typeof Button> & {
|
|
onCopy?: () => void;
|
|
onError?: (error: Error) => void;
|
|
timeout?: number;
|
|
};
|
|
|
|
export const CodeBlockCopyButton = ({
|
|
onCopy,
|
|
onError,
|
|
timeout = 2000,
|
|
children,
|
|
className,
|
|
...props
|
|
}: CodeBlockCopyButtonProps) => {
|
|
const [isCopied, setIsCopied] = useState(false);
|
|
const { code } = useContext(CodeBlockContext);
|
|
|
|
const copyToClipboard = async () => {
|
|
if (typeof window === "undefined" || !navigator.clipboard.writeText) {
|
|
onError?.(new Error("Clipboard API not available"));
|
|
return;
|
|
}
|
|
|
|
try {
|
|
await navigator.clipboard.writeText(code);
|
|
setIsCopied(true);
|
|
onCopy?.();
|
|
setTimeout(() => setIsCopied(false), timeout);
|
|
} catch (error) {
|
|
onError?.(error as Error);
|
|
}
|
|
};
|
|
|
|
const Icon = isCopied ? CheckIcon : CopyIcon;
|
|
|
|
return (
|
|
<Button
|
|
className={cn("shrink-0", className)}
|
|
onClick={copyToClipboard}
|
|
size="icon"
|
|
variant="ghost"
|
|
{...props}
|
|
>
|
|
{children ?? <Icon size={14} />}
|
|
</Button>
|
|
);
|
|
};
|