import { LightAsync as SyntaxHighlighter } from "react-syntax-highlighter"; import { atomOneDark as style } from "react-syntax-highlighter/dist/esm/styles/hljs"; import ReactMarkdown from "react-markdown"; import remarkGfm from "remark-gfm"; import remarkMath from "remark-math"; import remarkBreaks from "remark-breaks"; import rehypeKatex from "rehype-katex"; import rehypeRaw from "rehype-raw"; import { parseFile } from "./plugins/file.tsx"; import "@/assets/markdown/all.less"; import { useEffect, useMemo } from "react"; import { useDispatch, useSelector } from "react-redux"; import { openDialog as openQuotaDialog } from "@/store/quota.ts"; import { openDialog as openSubscriptionDialog } from "@/store/subscription.ts"; import { AppDispatch } from "@/store"; import { CalendarPlus, Cloud, CloudCog, Cloudy, Codepen, Codesandbox, Copy, Github, Maximize, Package, Plus, RefreshCcwDot, Twitter, Wand2, Youtube, } from "lucide-react"; import { copyClipboard } from "@/utils/dom.ts"; import { useToast } from "./ui/use-toast.ts"; import { useTranslation } from "react-i18next"; import { parseProgressbar } from "@/components/plugins/progress.tsx"; import { cn } from "@/components/ui/lib/utils.ts"; import { Button } from "@/components/ui/button.tsx"; import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, DialogTrigger, } from "@/components/ui/dialog.tsx"; import { DialogClose } from "@radix-ui/react-dialog"; import { appLogo } from "@/conf/env.ts"; import { subscriptionDataSelector } from "@/store/globals.ts"; import { useMessageActions } from "@/store/chat.ts"; type MarkdownProps = { children: string; className?: string; acceptHtml?: boolean; codeStyle?: string; }; function doAction(dispatch: AppDispatch, url: string): boolean { if (url === "/subscribe") { dispatch(openSubscriptionDialog()); return true; } else if (url === "/buy") { dispatch(openQuotaDialog()); return true; } return false; } const LanguageMap: Record = { html: "htmlbars", js: "javascript", ts: "typescript", jsx: "javascript", tsx: "typescript", rs: "rust", }; function getSocialIcon(url: string) { try { const { hostname } = new URL(url); if (hostname.includes("github.com")) return ; if (hostname.includes("twitter.com")) return ; if (hostname.includes("youtube.com")) return ; if (hostname.includes("codepen.io")) return ; if (hostname.includes("codesandbox.io")) return ; } catch (e) { return; } } function getVirtualIcon(command: string) { switch (command) { case "/VARIATION": return ; case "/UPSCALE": return ; case "/REROLL": return ; } } function MarkdownContent({ children, className, acceptHtml, codeStyle, }: MarkdownProps) { const dispatch = useDispatch(); const { t } = useTranslation(); const { toast } = useToast(); const { send: sendAction } = useMessageActions(); const subscription = useSelector(subscriptionDataSelector); useEffect(() => { document.querySelectorAll(".file-instance").forEach((el) => { const parent = el.parentElement as HTMLElement; if (!parent.classList.contains("file-block")) parent.classList.add("file-block"); }); }, [children]); const rehypePlugins = useMemo(() => { const plugins = [rehypeKatex]; return acceptHtml ? [...plugins, rehypeRaw] : plugins; }, [acceptHtml]); return ( p.items.map((i) => i.models.includes(model))) .includes(true); return (
{""}
{t("model")}

{model}

{t("your-quota")}

{quota}

{t("min-quota")}

{minimum}

{plan && ( )}
); } return

{children}

; }, a({ href, children }) { const url: string = href?.toString() || ""; if (url.startsWith("https://chatnio.virtual")) { const message = url.slice(23).replace(/-/g, " "); const prefix = message.split(" ")[0]; return ( {t("chat.send-message")} {t("chat.send-message-desc")}

{message}

await sendAction(message)} asChild >
); } return ( { if (doAction(dispatch, url)) e.preventDefault(); }} > {getSocialIcon(url)} {children} ); }, code({ inline, className, children, ...props }) { const match = /language-(\w+)/.exec(className || ""); const language = match ? match[1].toLowerCase() : ""; if (language === "file") return parseFile(children.toString()); if (language === "progress") return parseProgressbar(children.toString()); return !inline && match ? (
{ await copyClipboard(children.toString()); toast({ title: t("share.copied"), }); }} />

{language}

) : ( {children} ); }, }} /> ); } function Markdown({ children, acceptHtml, codeStyle, className, }: MarkdownProps) { // memoize the component return useMemo( () => ( ), [children, acceptHtml, codeStyle, className], ); } type CodeMarkdownProps = MarkdownProps & { filename: string; }; export function CodeMarkdown({ filename, ...props }: CodeMarkdownProps) { const suffix = filename.includes(".") ? filename.split(".").pop() : ""; const children = useMemo(() => { const content = props.children.toString(); return `\`\`\`${suffix}\n${content}\n\`\`\``; }, [props.children]); return {children}; } export default Markdown;