diff --git a/apps/www/components/ui/assistant-ui/codeblock.ts b/apps/www/components/ui/assistant-ui/codeblock.ts new file mode 100644 index 000000000..dc4507052 --- /dev/null +++ b/apps/www/components/ui/assistant-ui/codeblock.ts @@ -0,0 +1 @@ +export * from "assistant-ui/registry/assistant-ui/experimental/codeblock"; diff --git a/apps/www/components/ui/assistant-ui/markdown-text.ts b/apps/www/components/ui/assistant-ui/markdown-text.ts new file mode 100644 index 000000000..39382aad7 --- /dev/null +++ b/apps/www/components/ui/assistant-ui/markdown-text.ts @@ -0,0 +1 @@ +export * from "assistant-ui/registry/assistant-ui/experimental/markdown-text"; diff --git a/apps/www/tailwind.config.ts b/apps/www/tailwind.config.ts index dcefbfb54..288ff9ef6 100644 --- a/apps/www/tailwind.config.ts +++ b/apps/www/tailwind.config.ts @@ -76,7 +76,7 @@ const config = { }, }, }, - plugins: [require("tailwindcss-animate")], + plugins: [require("@tailwindcss/typography"), require("tailwindcss-animate")], } satisfies Config; export default config; diff --git a/packages/cli/components/ui/assistant-ui/codeblock.tsx b/packages/cli/components/ui/assistant-ui/codeblock.tsx new file mode 100644 index 000000000..f46cf45cd --- /dev/null +++ b/packages/cli/components/ui/assistant-ui/codeblock.tsx @@ -0,0 +1 @@ +export * from "@/registry/assistant-ui/experimental/codeblock"; diff --git a/packages/cli/components/ui/assistant-ui/markdown-text.tsx b/packages/cli/components/ui/assistant-ui/markdown-text.tsx new file mode 100644 index 000000000..ee511f66a --- /dev/null +++ b/packages/cli/components/ui/assistant-ui/markdown-text.tsx @@ -0,0 +1 @@ +export * from "@/registry/assistant-ui/experimental/markdown-text"; diff --git a/packages/cli/package.json b/packages/cli/package.json index 29e6b2e94..212e6f85f 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -13,6 +13,7 @@ }, "devDependencies": { "@assistant-ui/react": "workspace:*", + "@assistant-ui/react-markdown": "workspace:*", "@assistant-ui/tsconfig": "workspace:*", "@radix-ui/react-avatar": "^1.0.4", "@radix-ui/react-popover": "^1.0.7", @@ -21,11 +22,15 @@ "@types/cross-spawn": "^6.0.6", "@types/node": "^20.14.2", "@types/react": "^18", + "@types/react-syntax-highlighter": "^15.5.13", "class-variance-authority": "^0.7.0", "clsx": "^2.1.1", "lucide-react": "^0.395.0", "react": "^18.3.1", "react-resizable-panels": "^2.0.19", + "react-syntax-highlighter": "^15.5.0", + "remark-gfm": "^4.0.0", + "remark-math": "^6.0.0", "rimraf": "^5.0.7", "shadcn-ui": "0.8.0", "tailwind-merge": "^2.3.0", diff --git a/packages/cli/registry/assistant-ui/experimental/codeblock.tsx b/packages/cli/registry/assistant-ui/experimental/codeblock.tsx new file mode 100644 index 000000000..aa647ca30 --- /dev/null +++ b/packages/cli/registry/assistant-ui/experimental/codeblock.tsx @@ -0,0 +1,181 @@ +// Inspired by Chatbot-UI and modified to fit the needs of this project +// @see https://github.com/mckaywrigley/chatbot-ui/blob/main/components/Markdown/CodeBlock.tsx + +"use client"; + +import { type FC, memo, useState } from "react"; +import { Prism as SyntaxHighlighter } from "react-syntax-highlighter"; +import { coldarkDark } from "react-syntax-highlighter/dist/cjs/styles/prism"; + +import { Button } from "@/components/ui/button"; +import { CheckIcon, CopyIcon, DownloadIcon } from "lucide-react"; + +interface Props { + language: string; + value: string; +} + +interface languageMap { + [key: string]: string | undefined; +} + +export const programmingLanguages: languageMap = { + javascript: ".js", + python: ".py", + java: ".java", + c: ".c", + cpp: ".cpp", + "c++": ".cpp", + "c#": ".cs", + ruby: ".rb", + php: ".php", + swift: ".swift", + "objective-c": ".m", + kotlin: ".kt", + typescript: ".ts", + go: ".go", + perl: ".pl", + rust: ".rs", + scala: ".scala", + haskell: ".hs", + lua: ".lua", + shell: ".sh", + sql: ".sql", + html: ".html", + css: ".css", + // add more file extensions here, make sure the key is same as language prop in CodeBlock.tsx component +}; + +export const generateRandomString = (length: number, lowercase = false) => { + const chars = "ABCDEFGHJKLMNPQRSTUVWXY3456789"; // excluding similar looking characters like Z, 2, I, 1, O, 0 + let result = ""; + for (let i = 0; i < length; i++) { + result += chars.charAt(Math.floor(Math.random() * chars.length)); + } + return lowercase ? result.toLowerCase() : result; +}; + +const CodeBlock: FC = memo(({ language, value }) => { + const { isCopied, copyToClipboard } = useCopyToClipboard({ timeout: 2000 }); + + const downloadAsFile = () => { + if (typeof window === "undefined") { + return; + } + const fileExtension = programmingLanguages[language] || ".file"; + const suggestedFileName = `file-${generateRandomString( + 3, + true, + )}${fileExtension}`; + const fileName = window.prompt("Enter file name" || "", suggestedFileName); + + if (!fileName) { + // User pressed cancel on prompt. + return; + } + + const blob = new Blob([value], { type: "text/plain" }); + const url = URL.createObjectURL(blob); + const link = document.createElement("a"); + link.download = fileName; + link.href = url; + link.style.display = "none"; + document.body.appendChild(link); + link.click(); + document.body.removeChild(link); + URL.revokeObjectURL(url); + }; + + const onCopy = () => { + if (isCopied) return; + copyToClipboard(value); + }; + + return ( +
+
+ {language} +
+ + +
+
+ + {value} + +
+ ); +}); +CodeBlock.displayName = "CodeBlock"; + +export { CodeBlock }; + +export interface useCopyToClipboardProps { + timeout?: number; +} + +export function useCopyToClipboard({ + timeout = 2000, +}: useCopyToClipboardProps) { + const [isCopied, setIsCopied] = useState(false); + + const copyToClipboard = (value: string) => { + if (typeof window === "undefined" || !navigator.clipboard?.writeText) { + return; + } + + if (!value) { + return; + } + + navigator.clipboard.writeText(value).then(() => { + setIsCopied(true); + + setTimeout(() => { + setIsCopied(false); + }, timeout); + }); + }; + + return { isCopied, copyToClipboard }; +} diff --git a/packages/cli/registry/assistant-ui/experimental/markdown-text.tsx b/packages/cli/registry/assistant-ui/experimental/markdown-text.tsx new file mode 100644 index 000000000..c3e84352f --- /dev/null +++ b/packages/cli/registry/assistant-ui/experimental/markdown-text.tsx @@ -0,0 +1,49 @@ +"use client"; + +import { unstable_MarkdownTextPrimitive as MarkdownTextPrimitive } from "@assistant-ui/react-markdown"; +import remarkGfm from "remark-gfm"; +import remarkMath from "remark-math"; + +export const MarkdownText = () => { + return ( + {children}

; + }, + // code({ node, className, children, ...props }) { + // if (children.length) { + // if (children[0] == "▍") { + // return ( + // + // ); + // } + + // children[0] = (children[0] as string).replace("`▍`", "▍"); + // } + + // const match = /language-(\w+)/.exec(className || ""); + + // if (inline) { + // return ( + // + // {children} + // + // ); + // } + + // return ( + // + // ); + // }, + }} + /> + ); +}; diff --git a/packages/cli/registry/assistant-ui/experimental/thread.tsx b/packages/cli/registry/assistant-ui/experimental/thread.tsx new file mode 100644 index 000000000..15116af8b --- /dev/null +++ b/packages/cli/registry/assistant-ui/experimental/thread.tsx @@ -0,0 +1,260 @@ +"use client"; + +import { + ActionBarPrimitive, + BranchPickerPrimitive, + ComposerPrimitive, + MessagePrimitive, + ThreadPrimitive, +} from "@assistant-ui/react"; +import type { FC } from "react"; + +import { MarkdownText } from "@/components/ui/assistant-ui/markdown-text"; +import { Avatar, AvatarFallback } from "@/components/ui/avatar"; +import { Button, type ButtonProps } from "@/components/ui/button"; +import { + Tooltip, + TooltipContent, + TooltipTrigger, +} from "@/components/ui/tooltip"; +import { cn } from "@/lib/utils"; +import { TooltipProvider } from "@radix-ui/react-tooltip"; +import { + ArrowDownIcon, + CheckIcon, + ChevronLeftIcon, + ChevronRightIcon, + CopyIcon, + PencilIcon, + RefreshCwIcon, + SendHorizonalIcon, +} from "lucide-react"; + +export const Thread: FC = () => { + return ( + + + + + + + + + + + + + + + + ); +}; + +const ThreadEmpty: FC = () => { + return ( +
+ + C + +

How can I help you today?

+
+ ); +}; + +const ThreadScrollToBottom: FC = () => { + return ( +
+ + + + + +
+ ); +}; + +const Composer: FC = () => { + return ( + + + + + + + + + +
+ + + + ); +}; + +const UserMessage: FC = () => { + return ( + + + Y + + +
+

You

+ +

+ +

+ +
+ + + + + + + + + +
+
+
+ ); +}; + +const EditComposer: FC = () => { + return ( + + + Y + + +
+

You

+ + + + +
+ + + + + + +
+
+
+
+ ); +}; + +const AssistantMessage: FC = () => { + return ( + + + A + + +
+

Assistant

+ + +

+ +

+ +
+ + + + + + + + + + + + + + + + + + + +
+
+
+ ); +}; + +const BranchPicker: FC = () => { + return ( + + + + + + + / + + + + + + + ); +}; + +type IconButton = ButtonProps & { tooltip: string }; + +const IconButton: FC = ({ + children, + tooltip, + className, + ...rest +}) => { + return ( + + + + + {tooltip} + + ); +}; diff --git a/packages/cli/registry/registry.ts b/packages/cli/registry/registry.ts index b152fab76..54f366666 100644 --- a/packages/cli/registry/registry.ts +++ b/packages/cli/registry/registry.ts @@ -22,6 +22,41 @@ export const registry: RegistryIndex = [ registryDependencies: ["avatar"], dependencies: ["@assistant-ui/react", "lucide-react"], }, + { + name: "unstable-codeblock", + type: "components:ui", + files: ["assistant-ui/experimental/codeblock.tsx"], + registryDependencies: ["button"], + dependencies: [ + "react-syntax-highlighter", + "@types/react-syntax-highlighter", + "lucide-react", + ], + }, + { + name: "unstable-markdown-text", + type: "components:ui", + files: ["assistant-ui/experimental/markdown-text.tsx"], + // registryDependencies: ["unstable-codeblock"], + dependencies: [ + "@assistant-ui/react", + "@assistant-ui/react-markdown", + "remark-gfm", + "remark-math", + ], + }, + { + name: "unstable-thread-full", + type: "components:ui", + files: ["assistant-ui/experimental/thread.tsx"], + registryDependencies: [ + "unstable-markdown-text", + "button", + "avatar", + "tooltip", + ], + dependencies: ["@assistant-ui/react", "lucide-react"], + }, { name: "thread-full", type: "components:ui", diff --git a/packages/react-markdown/README.md b/packages/react-markdown/README.md new file mode 100644 index 000000000..28aef743b --- /dev/null +++ b/packages/react-markdown/README.md @@ -0,0 +1,5 @@ +# `@assistant-ui/react-hook-form` + +React Hook Form integration for `@assistant-ui/react`. + +Simply replace `useForm` with `useAssistantForm` to give the chatbot the ability to interact with your form. \ No newline at end of file diff --git a/packages/react-markdown/package.json b/packages/react-markdown/package.json new file mode 100644 index 000000000..a185b29d5 --- /dev/null +++ b/packages/react-markdown/package.json @@ -0,0 +1,58 @@ +{ + "name": "@assistant-ui/react-markdown", + "version": "0.0.1", + "private": true, + "license": "MIT", + "exports": { + ".": { + "import": { + "types": "./dist/index.d.mts", + "default": "./dist/index.mjs" + }, + "require": { + "types": "./dist/index.d.ts", + "default": "./dist/index.js" + } + } + }, + "source": "./src/index.ts", + "main": "./dist/index.js", + "module": "./dist/index.mjs", + "types": "./dist/index.d.ts", + "files": ["dist", "README.md"], + "sideEffects": false, + "scripts": { + "build": "tsup src/index.ts --format cjs,esm --dts --sourcemap" + }, + "dependencies": { + "react-markdown": "^9.0.1", + "zod": "^3.23.8" + }, + "peerDependencies": { + "@assistant-ui/react": "^0.1.5", + "@types/react": "*", + "react": "^18" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + }, + "devDependencies": { + "@assistant-ui/react": "workspace:*", + "@assistant-ui/tsconfig": "workspace:*", + "tsup": "^8.1.0" + }, + "publishConfig": { + "access": "public", + "provenance": true + }, + "homepage": "https://assistant-ui.com/", + "repository": { + "type": "git", + "url": "git+https://github.com/Yonom/assistant-ui.git" + }, + "bugs": { + "url": "https://github.com/Yonom/assistant-ui/issues" + } +} diff --git a/packages/react-markdown/src/MarkdownText.tsx b/packages/react-markdown/src/MarkdownText.tsx new file mode 100644 index 000000000..0de7bf6c1 --- /dev/null +++ b/packages/react-markdown/src/MarkdownText.tsx @@ -0,0 +1,22 @@ +import { ContentPartPrimitive } from "@assistant-ui/react"; +import { useContentPartContext } from "@assistant-ui/react/experimental"; +import type { FC } from "react"; +import ReactMarkdown, { type Options } from "react-markdown"; + +export const MarkdownText: FC = (options) => { + const { useContentPart } = useContentPartContext(); + const text = useContentPart((c) => { + if (c.part.type !== "text") + throw new Error( + "This component can only be used inside text content parts.", + ); + + return c.part.text; + }); + return ( + + {text} + + + ); +}; diff --git a/packages/react-markdown/src/codeblock.tsx b/packages/react-markdown/src/codeblock.tsx new file mode 100644 index 000000000..1bde5acb5 --- /dev/null +++ b/packages/react-markdown/src/codeblock.tsx @@ -0,0 +1,148 @@ +// Inspired by Chatbot-UI and modified to fit the needs of this project +// @see https://github.com/mckaywrigley/chatbot-ui/blob/main/components/Markdown/CodeBlock.tsx + +'use client' + +import { FC, memo } from 'react' +import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter' +import { coldarkDark } from 'react-syntax-highlighter/dist/cjs/styles/prism' + +import { useCopyToClipboard } from '@/lib/hooks/use-copy-to-clipboard' +import { IconCheck, IconCopy, IconDownload } from '@/components/ui/icons' +import { Button } from '@/components/ui/button' + +interface Props { + language: string + value: string +} + +interface languageMap { + [key: string]: string | undefined +} + +export const programmingLanguages: languageMap = { + javascript: '.js', + python: '.py', + java: '.java', + c: '.c', + cpp: '.cpp', + 'c++': '.cpp', + 'c#': '.cs', + ruby: '.rb', + php: '.php', + swift: '.swift', + 'objective-c': '.m', + kotlin: '.kt', + typescript: '.ts', + go: '.go', + perl: '.pl', + rust: '.rs', + scala: '.scala', + haskell: '.hs', + lua: '.lua', + shell: '.sh', + sql: '.sql', + html: '.html', + css: '.css' + // add more file extensions here, make sure the key is same as language prop in CodeBlock.tsx component +} + +export const generateRandomString = (length: number, lowercase = false) => { + const chars = 'ABCDEFGHJKLMNPQRSTUVWXY3456789' // excluding similar looking characters like Z, 2, I, 1, O, 0 + let result = '' + for (let i = 0; i < length; i++) { + result += chars.charAt(Math.floor(Math.random() * chars.length)) + } + return lowercase ? result.toLowerCase() : result +} + +const CodeBlock: FC = memo(({ language, value }) => { + const { isCopied, copyToClipboard } = useCopyToClipboard({ timeout: 2000 }) + + const downloadAsFile = () => { + if (typeof window === 'undefined') { + return + } + const fileExtension = programmingLanguages[language] || '.file' + const suggestedFileName = `file-${generateRandomString( + 3, + true + )}${fileExtension}` + const fileName = window.prompt('Enter file name' || '', suggestedFileName) + + if (!fileName) { + // User pressed cancel on prompt. + return + } + + const blob = new Blob([value], { type: 'text/plain' }) + const url = URL.createObjectURL(blob) + const link = document.createElement('a') + link.download = fileName + link.href = url + link.style.display = 'none' + document.body.appendChild(link) + link.click() + document.body.removeChild(link) + URL.revokeObjectURL(url) + } + + const onCopy = () => { + if (isCopied) return + copyToClipboard(value) + } + + return ( +
+
+ {language} +
+ + +
+
+ + {value} + +
+ ) +}) +CodeBlock.displayName = 'CodeBlock' + +export { CodeBlock } diff --git a/packages/react-markdown/src/index.ts b/packages/react-markdown/src/index.ts new file mode 100644 index 000000000..98152cbf4 --- /dev/null +++ b/packages/react-markdown/src/index.ts @@ -0,0 +1 @@ +export { MarkdownText as unstable_MarkdownTextPrimitive } from "./MarkdownText"; diff --git a/packages/react-markdown/tsconfig.json b/packages/react-markdown/tsconfig.json new file mode 100644 index 000000000..09a74b1dc --- /dev/null +++ b/packages/react-markdown/tsconfig.json @@ -0,0 +1,11 @@ +{ + "extends": "@assistant-ui/tsconfig/base.json", + "compilerOptions": { + "paths": { + "@assistant-ui/*": ["../../packages/*/src"], + "@assistant-ui/react/*": ["../../packages/react/src/*"] + } + }, + "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"], + "exclude": ["node_modules"] +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 8b0ba7b8a..0dbfd9517 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -481,6 +481,9 @@ importers: '@assistant-ui/react': specifier: workspace:* version: link:../react + '@assistant-ui/react-markdown': + specifier: workspace:* + version: link:../react-markdown '@assistant-ui/tsconfig': specifier: workspace:* version: link:../tsconfig @@ -505,6 +508,9 @@ importers: '@types/react': specifier: ^18 version: 18.3.3 + '@types/react-syntax-highlighter': + specifier: ^15.5.13 + version: 15.5.13 class-variance-authority: specifier: ^0.7.0 version: 0.7.0 @@ -520,6 +526,15 @@ importers: react-resizable-panels: specifier: ^2.0.19 version: 2.0.19(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react-syntax-highlighter: + specifier: ^15.5.0 + version: 15.5.0(react@18.3.1) + remark-gfm: + specifier: ^4.0.0 + version: 4.0.0 + remark-math: + specifier: ^6.0.0 + version: 6.0.0 rimraf: specifier: ^5.0.7 version: 5.0.7 @@ -650,6 +665,31 @@ importers: specifier: ^8.1.0 version: 8.1.0(postcss@8.4.38)(ts-node@10.9.2(typescript@5.4.5))(typescript@5.4.5) + packages/react-markdown: + dependencies: + '@types/react': + specifier: '*' + version: 18.3.3 + react: + specifier: ^18 + version: 18.3.1 + react-markdown: + specifier: ^9.0.1 + version: 9.0.1(@types/react@18.3.3)(react@18.3.1) + zod: + specifier: ^3.23.8 + version: 3.23.8 + devDependencies: + '@assistant-ui/react': + specifier: workspace:* + version: link:../react + '@assistant-ui/tsconfig': + specifier: workspace:* + version: link:../tsconfig + tsup: + specifier: ^8.1.0 + version: 8.1.0(postcss@8.4.38)(ts-node@10.9.2(typescript@5.4.5))(typescript@5.4.5) + packages/tsconfig: dependencies: '@tsconfig/strictest': @@ -2390,6 +2430,9 @@ packages: '@types/express@4.17.21': resolution: {integrity: sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ==} + '@types/hast@2.3.10': + resolution: {integrity: sha512-McWspRw8xx8J9HurkVBfYj0xKoE25tOFlHGdx4MJ5xORQrMGZNqJhVQWaIbm6Oyla5kYOXtDiopzKRJzEOkwJw==} + '@types/hast@3.0.4': resolution: {integrity: sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==} @@ -2459,6 +2502,9 @@ packages: '@types/react-dom@18.3.0': resolution: {integrity: sha512-EhwApuTmMBmXuFOikhQLIBUn6uFg81SwLMOAUgodJF14SOBOCMdU04gDoYi0WOJJHD144TL32z4yDqCW3dnkQg==} + '@types/react-syntax-highlighter@15.5.13': + resolution: {integrity: sha512-uLGJ87j6Sz8UaBAooU0T6lWJ0dBmjZgN1PZTrj05TNql2/XpC6+4HhMT5syIdFUUt+FASfCeLLv4kBygNU+8qA==} + '@types/react@18.3.3': resolution: {integrity: sha512-hti/R0pS0q1/xx+TsI73XIqk26eBsISZ2R0wUijXIngRK9R/e7Xw/cXVxQK7R5JjW+SV4zGcn5hXjudkN/pLIw==} @@ -3040,12 +3086,21 @@ packages: character-entities-html4@2.1.0: resolution: {integrity: sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==} + character-entities-legacy@1.1.4: + resolution: {integrity: sha512-3Xnr+7ZFS1uxeiUDvV02wQ+QDbc55o97tIV5zHScSPJpcLm/r0DFPcoY3tYRp+VZukxuMeKgXYmsXQHO05zQeA==} + character-entities-legacy@3.0.0: resolution: {integrity: sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==} + character-entities@1.2.4: + resolution: {integrity: sha512-iBMyeEHxfVnIakwOuDXpVkc54HijNgCyQB2w0VfGQThle6NXn50zU6V/u+LDhxHcDUPojn6Kpga3PTAD8W1bQw==} + character-entities@2.0.2: resolution: {integrity: sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==} + character-reference-invalid@1.1.4: + resolution: {integrity: sha512-mKKUkUbhPpQlCOfIuZkvSEgktjPFIsZKRRbC6KWVEMvlzblj3i3asQv5ODsrwt0N3pHAEvjP8KTQPHkp0+6jOg==} + character-reference-invalid@2.0.1: resolution: {integrity: sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw==} @@ -3144,6 +3199,9 @@ packages: resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} engines: {node: '>= 0.8'} + comma-separated-tokens@1.0.8: + resolution: {integrity: sha512-GHuDRO12Sypu2cV70d1dkA2EUmXHgntrzbpvOB+Qy+49ypNfGgFQIC2fhhXbnyrJRynDCAARsT7Ou0M6hirpfw==} + comma-separated-tokens@2.0.3: resolution: {integrity: sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==} @@ -4043,6 +4101,9 @@ packages: fastq@1.17.1: resolution: {integrity: sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==} + fault@1.0.4: + resolution: {integrity: sha512-CJ0HCB5tL5fYTEA7ToAq5+kTwd++Borf1/bifxd9iT70QcXr4MRrO3Llf8Ifs70q+SJcGHFtnIE/Nw6giCtECA==} + fault@2.0.1: resolution: {integrity: sha512-WtySTkS4OKev5JtpHXnib4Gxiurzh5NCGvWrFaZ34m6JehfTUhKZvn9njTfw48t6JumVQOmrKqpmGcdwxnhqBQ==} @@ -4321,6 +4382,9 @@ packages: hast-util-is-element@3.0.0: resolution: {integrity: sha512-Val9mnv2IWpLbNPqc/pUem+a7Ipj2aHacCwgNfTiK0vJKl0LF+4Ba4+v1oPHFpf3bLYmreq0/l3Gud9S5OH42g==} + hast-util-parse-selector@2.2.5: + resolution: {integrity: sha512-7j6mrk/qqkSehsM92wQjdIgWM2/BW61u/53G6xmC8i1OmEdKLHbk419QKQUjz6LglWsfqoiHmyMRkP1BGjecNQ==} + hast-util-parse-selector@4.0.0: resolution: {integrity: sha512-wkQCkSYoOGCRKERFWcxMVMOcYE2K1AaNLU8DXS9arxnLOUEWbOXKXiJUNzEpqZ3JOKpnha3jkFrumEjVliDe7A==} @@ -4345,6 +4409,9 @@ packages: hast-util-whitespace@3.0.0: resolution: {integrity: sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==} + hastscript@6.0.0: + resolution: {integrity: sha512-nDM6bvd7lIqDUiYEiu5Sl/+6ReP0BMk/2f4U/Rooccxkj0P5nm+acM5PrGJ/t5I8qPGiqZSE6hVAwZEdZIvP4w==} + hastscript@8.0.0: resolution: {integrity: sha512-dMOtzCEd3ABUeSIISmrETiKuyydk1w0pa+gE/uormcTpSYuaNJPbX1NU3JLyscSLjwAQM8bWMhhIlnCqnRvDTw==} @@ -4352,6 +4419,9 @@ packages: resolution: {integrity: sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==} hasBin: true + highlight.js@10.7.3: + resolution: {integrity: sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==} + hosted-git-info@2.8.9: resolution: {integrity: sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==} @@ -4359,6 +4429,9 @@ packages: resolution: {integrity: sha512-oWv4T4yJ52iKrufjnyZPkrN0CH3QnrUqdB6In1g5Fe1mia8GmF36gnfNySxoZtxD5+NmYw1EElVXiBk93UeskA==} engines: {node: '>=12'} + html-url-attributes@3.0.0: + resolution: {integrity: sha512-/sXbVCWayk6GDVg3ctOX6nxaVj7So40FcFAnWlWGNAB1LpYKcV5Cd10APjPjW80O7zYW2MsjBV4zZ7IZO5fVow==} + html-void-elements@3.0.0: resolution: {integrity: sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==} @@ -4458,9 +4531,15 @@ packages: resolution: {integrity: sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==} engines: {node: '>= 0.10'} + is-alphabetical@1.0.4: + resolution: {integrity: sha512-DwzsA04LQ10FHTZuL0/grVDk4rFoVH1pjAToYwBrHSxcrBIGQuXrQMtD5U1b0U2XVgKZCTLLP8u2Qxqhy3l2Vg==} + is-alphabetical@2.0.1: resolution: {integrity: sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ==} + is-alphanumerical@1.0.4: + resolution: {integrity: sha512-UzoZUr+XfVz3t3v4KyGEniVL9BDRoQtY7tOyrRybkVNjDFWyo1yhXNGrrBTQxp3ib9BLAWs7k2YKBQsFRkZG9A==} + is-alphanumerical@2.0.1: resolution: {integrity: sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw==} @@ -4505,6 +4584,9 @@ packages: resolution: {integrity: sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==} engines: {node: '>= 0.4'} + is-decimal@1.0.4: + resolution: {integrity: sha512-RGdriMmQQvZ2aqaQq3awNA6dCGtKpiDFcOzrTWrDAT2MiWrKQVPmxLGHl7Y2nNu6led0kEyoX0enY0qXYsv9zw==} + is-decimal@2.0.1: resolution: {integrity: sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A==} @@ -4531,6 +4613,9 @@ packages: resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} engines: {node: '>=0.10.0'} + is-hexadecimal@1.0.4: + resolution: {integrity: sha512-gyPJuv83bHMpocVYoqof5VDiZveEoGoFL8m3BXNb2VW8Xs+rz9kqO8LOQ5DH6EsuvilT1ApazU0pyl+ytbPtlw==} + is-hexadecimal@2.0.1: resolution: {integrity: sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg==} @@ -4837,6 +4922,9 @@ packages: resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} hasBin: true + lowlight@1.20.0: + resolution: {integrity: sha512-8Ktj+prEb1RoCPkEOrPMYUN/nCggB7qAWe3a7OpMjWQkh3l2RD5wKRQ+o8Q8YuI9RG/xs95waaI/E6ym/7NsTw==} + lru-cache@10.2.2: resolution: {integrity: sha512-9hp3Vp2/hFQUiIwKo8XCeFVnrg8Pk3TYNPIR7tJADKi5YfcF7vEaK7avFHTlSy3kOKYaJQaalfEo6YuXdceBOQ==} engines: {node: 14 || >=16.14} @@ -5535,6 +5623,9 @@ packages: resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} engines: {node: '>=6'} + parse-entities@2.0.0: + resolution: {integrity: sha512-kkywGpCcRYhqQIchaWqZ875wzpS/bMKhz5HnN3p7wveJTkTtyAB/AlnS0f8DFSqYW1T82t6yEAkEcB+A1I3MbQ==} + parse-entities@4.0.1: resolution: {integrity: sha512-SWzvYcSJh4d/SGLIOQfZ/CoNv6BTlI6YEQ7Nj82oDVnRpwe/Z/F1EMx42x3JAOwGBlCjeCH0BRJQbQ/opHL17w==} @@ -5702,6 +5793,14 @@ packages: resolution: {integrity: sha512-973driJZvxiGOQ5ONsFhOF/DtzPMOMtgC11kCpUrPGMTgqp2q/1gwzCquocrN33is0VZ5GFHXZYMM9l6h67v2Q==} engines: {node: '>=10'} + prismjs@1.27.0: + resolution: {integrity: sha512-t13BGPUlFDR7wRB5kQDG4jjl7XeuH6jbJGt11JHPL96qwsEHNX2+68tFXqc1/k+/jALsbSWJKUOT/hcYAZ5LkA==} + engines: {node: '>=6'} + + prismjs@1.29.0: + resolution: {integrity: sha512-Kx/1w86q/epKcmte75LNrEoT+lX8pBpavuAbvJWRXar7Hz8jrtF+e3vY751p0R8H9HdArwaCTNDDzHg/ScJK1Q==} + engines: {node: '>=6'} + prompts@2.4.2: resolution: {integrity: sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==} engines: {node: '>= 6'} @@ -5709,6 +5808,9 @@ packages: prop-types@15.8.1: resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==} + property-information@5.6.0: + resolution: {integrity: sha512-YUHSPk+A30YPv+0Qf8i9Mbfe/C0hdPXk1s1jPVToV8pk8BQtpw10ct89Eo7OWkutrwqvT0eicAxlOg3dOAu8JA==} + property-information@6.5.0: resolution: {integrity: sha512-PgTgs/BlvHxOu8QuEN7wi5A0OmXaBcHpmCSTehcs6Uuu9IkDIEo13Hy7n898RHfrQ49vKCoGeWZSaAK01nwVig==} @@ -5763,6 +5865,12 @@ packages: react-is@16.13.1: resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==} + react-markdown@9.0.1: + resolution: {integrity: sha512-186Gw/vF1uRkydbsOIkcGXw7aHq0sZOCRFFjGrr7b9+nVZg4UfA4enXCaxm4fUzecU38sWfrNDitGhshuU7rdg==} + peerDependencies: + '@types/react': '>=18' + react: '>=18' + react-remove-scroll-bar@2.3.4: resolution: {integrity: sha512-63C4YQBUt0m6ALadE9XV56hV8BgJWDmmTPY758iIJjfQKt2nYwoUrPk0LXRXcB/yIj82T1/Ixfdpdk68LwIB0A==} engines: {node: '>=10'} @@ -5809,6 +5917,11 @@ packages: '@types/react': optional: true + react-syntax-highlighter@15.5.0: + resolution: {integrity: sha512-+zq2myprEnQmH5yw6Gqc8lD55QHnpKaU8TOcFeC/Lg/MQSs8UknEA0JC4nTZGFAXC2J2Hyj/ijJ7NlabyPi2gg==} + peerDependencies: + react: '>= 0.14.0' + react-textarea-autosize@8.5.3: resolution: {integrity: sha512-XT1024o2pqCuZSuBt9FwHlaDeNtVrtCXu0Rnz88t1jUGheCLa3PhjE1GH8Ctm2axEtvdCl5SUHYschyQ0L5QHQ==} engines: {node: '>=10'} @@ -5857,6 +5970,9 @@ packages: resolution: {integrity: sha512-fmfw4XgoDke3kdI6h4xcUz1dG8uaiv5q9gcEwLS4Pnth2kxT+GZ7YehS1JTMGBQmtV7Y4GFGbs2re2NqhdozUg==} engines: {node: '>= 0.4'} + refractor@3.6.0: + resolution: {integrity: sha512-MY9W41IOWxxk31o+YvFCNyNzdkc9M20NoZK5vq6jkv4I/uh2zkWcfudj0Q1fovjUQJrNewS9NMzeTtqPf+n5EA==} + regenerator-runtime@0.14.1: resolution: {integrity: sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==} @@ -6163,6 +6279,9 @@ packages: resolution: {integrity: sha512-2ymg6oRBpebeZi9UUNsgQ89bhx01TcTkmNTGnNO88imTmbSgy4nfujrgVEFKWpMTEGA11EDkTt7mqObTPdigIA==} engines: {node: '>= 8'} + space-separated-tokens@1.1.5: + resolution: {integrity: sha512-q/JSVd1Lptzhf5bkYm4ob4iWPjx0KiRe3sRFBNrVqbJkFaBm5vbbowy1mymoPNLRa52+oadOhJ+K49wsSeSjTA==} + space-separated-tokens@2.0.2: resolution: {integrity: sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==} @@ -6950,6 +7069,10 @@ packages: resolution: {integrity: sha512-f9s+fUkX04BxQf+7mMWAp5zk61pciie+fFLC9hX9UVvCeJQfNHRHXpeo5MPcR0EUf57PYLdt+ZO4f3Ipk2oZUw==} engines: {node: '>=0.1'} + xtend@4.0.2: + resolution: {integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==} + engines: {node: '>=0.4'} + y18n@4.0.3: resolution: {integrity: sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==} @@ -8964,6 +9087,10 @@ snapshots: '@types/qs': 6.9.15 '@types/serve-static': 1.15.7 + '@types/hast@2.3.10': + dependencies: + '@types/unist': 2.0.10 + '@types/hast@3.0.4': dependencies: '@types/unist': 3.0.2 @@ -9029,6 +9156,10 @@ snapshots: dependencies: '@types/react': 18.3.3 + '@types/react-syntax-highlighter@15.5.13': + dependencies: + '@types/react': 18.3.3 + '@types/react@18.3.3': dependencies: '@types/prop-types': 15.7.12 @@ -9763,10 +9894,16 @@ snapshots: character-entities-html4@2.1.0: {} + character-entities-legacy@1.1.4: {} + character-entities-legacy@3.0.0: {} + character-entities@1.2.4: {} + character-entities@2.0.2: {} + character-reference-invalid@1.1.4: {} + character-reference-invalid@2.0.1: {} chardet@0.7.0: {} @@ -9860,6 +9997,8 @@ snapshots: dependencies: delayed-stream: 1.0.0 + comma-separated-tokens@1.0.8: {} + comma-separated-tokens@2.0.3: {} commander@10.0.1: {} @@ -10930,6 +11069,10 @@ snapshots: dependencies: reusify: 1.0.4 + fault@1.0.4: + dependencies: + format: 0.2.2 + fault@2.0.1: dependencies: format: 0.2.2 @@ -11241,6 +11384,8 @@ snapshots: dependencies: '@types/hast': 3.0.4 + hast-util-parse-selector@2.2.5: {} + hast-util-parse-selector@4.0.0: dependencies: '@types/hast': 3.0.4 @@ -11327,6 +11472,14 @@ snapshots: dependencies: '@types/hast': 3.0.4 + hastscript@6.0.0: + dependencies: + '@types/hast': 2.3.10 + comma-separated-tokens: 1.0.8 + hast-util-parse-selector: 2.2.5 + property-information: 5.6.0 + space-separated-tokens: 1.1.5 + hastscript@8.0.0: dependencies: '@types/hast': 3.0.4 @@ -11337,12 +11490,16 @@ snapshots: he@1.2.0: {} + highlight.js@10.7.3: {} + hosted-git-info@2.8.9: {} html-encoding-sniffer@3.0.0: dependencies: whatwg-encoding: 2.0.0 + html-url-attributes@3.0.0: {} + html-void-elements@3.0.0: {} http-errors@2.0.0: @@ -11454,8 +11611,15 @@ snapshots: ipaddr.js@1.9.1: {} + is-alphabetical@1.0.4: {} + is-alphabetical@2.0.1: {} + is-alphanumerical@1.0.4: + dependencies: + is-alphabetical: 1.0.4 + is-decimal: 1.0.4 + is-alphanumerical@2.0.1: dependencies: is-alphabetical: 2.0.1 @@ -11501,6 +11665,8 @@ snapshots: dependencies: has-tostringtag: 1.0.2 + is-decimal@1.0.4: {} + is-decimal@2.0.1: {} is-extendable@0.1.1: {} @@ -11521,6 +11687,8 @@ snapshots: dependencies: is-extglob: 2.1.1 + is-hexadecimal@1.0.4: {} + is-hexadecimal@2.0.1: {} is-interactive@2.0.0: {} @@ -11782,6 +11950,11 @@ snapshots: dependencies: js-tokens: 4.0.0 + lowlight@1.20.0: + dependencies: + fault: 1.0.4 + highlight.js: 10.7.3 + lru-cache@10.2.2: {} lru-cache@4.1.5: @@ -12913,6 +13086,15 @@ snapshots: dependencies: callsites: 3.1.0 + parse-entities@2.0.0: + dependencies: + character-entities: 1.2.4 + character-entities-legacy: 1.1.4 + character-reference-invalid: 1.1.4 + is-alphanumerical: 1.0.4 + is-decimal: 1.0.4 + is-hexadecimal: 1.0.4 + parse-entities@4.0.1: dependencies: '@types/unist': 2.0.10 @@ -13084,6 +13266,10 @@ snapshots: dependencies: parse-ms: 2.1.0 + prismjs@1.27.0: {} + + prismjs@1.29.0: {} + prompts@2.4.2: dependencies: kleur: 3.0.3 @@ -13095,6 +13281,10 @@ snapshots: object-assign: 4.1.1 react-is: 16.13.1 + property-information@5.6.0: + dependencies: + xtend: 4.0.2 + property-information@6.5.0: {} proxy-addr@2.0.7: @@ -13143,6 +13333,23 @@ snapshots: react-is@16.13.1: {} + react-markdown@9.0.1(@types/react@18.3.3)(react@18.3.1): + dependencies: + '@types/hast': 3.0.4 + '@types/react': 18.3.3 + devlop: 1.1.0 + hast-util-to-jsx-runtime: 2.3.0 + html-url-attributes: 3.0.0 + mdast-util-to-hast: 13.2.0 + react: 18.3.1 + remark-parse: 11.0.0 + remark-rehype: 11.1.0 + unified: 11.0.4 + unist-util-visit: 5.0.0 + vfile: 6.0.1 + transitivePeerDependencies: + - supports-color + react-remove-scroll-bar@2.3.4(@types/react@18.3.3)(react@18.3.1): dependencies: react: 18.3.1 @@ -13184,6 +13391,15 @@ snapshots: optionalDependencies: '@types/react': 18.3.3 + react-syntax-highlighter@15.5.0(react@18.3.1): + dependencies: + '@babel/runtime': 7.24.7 + highlight.js: 10.7.3 + lowlight: 1.20.0 + prismjs: 1.29.0 + react: 18.3.1 + refractor: 3.6.0 + react-textarea-autosize@8.5.3(@types/react@18.3.3)(react@18.3.1): dependencies: '@babel/runtime': 7.24.7 @@ -13256,6 +13472,12 @@ snapshots: globalthis: 1.0.4 which-builtin-type: 1.1.3 + refractor@3.6.0: + dependencies: + hastscript: 6.0.0 + parse-entities: 2.0.0 + prismjs: 1.27.0 + regenerator-runtime@0.14.1: {} regexp.prototype.flags@1.5.2: @@ -13681,6 +13903,8 @@ snapshots: dependencies: whatwg-url: 7.1.0 + space-separated-tokens@1.1.5: {} + space-separated-tokens@2.0.2: {} spawndamnit@2.0.0: @@ -14695,6 +14919,8 @@ snapshots: xmldom-sre@0.1.31: {} + xtend@4.0.2: {} + y18n@4.0.3: {} y18n@5.0.8: {}