diff --git a/examples/nextjs-ai-chatbot/app/globals.css b/examples/nextjs-ai-chatbot/app/globals.css index 902d4e1..a204c6c 100644 --- a/examples/nextjs-ai-chatbot/app/globals.css +++ b/examples/nextjs-ai-chatbot/app/globals.css @@ -1,3 +1,4 @@ +@import url('./rsh-dark.css') layer(syntax-highlighter); @import 'tailwindcss'; @plugin "@tailwindcss/typography"; @plugin "daisyui" { @@ -12,16 +13,6 @@ scrollbar-gutter: stable; } -pre { - display: block !important; -} - -pre pre { - border: none !important; - background-color: transparent !important; - box-shadow: none !important; -} - .ai-thinking { background: linear-gradient(90deg, #00ffe0, #0072ff, #00ffe0); background-size: 200% auto; diff --git a/examples/nextjs-ai-chatbot/app/rsh-dark.css b/examples/nextjs-ai-chatbot/app/rsh-dark.css new file mode 100644 index 0000000..61a6d9e --- /dev/null +++ b/examples/nextjs-ai-chatbot/app/rsh-dark.css @@ -0,0 +1,152 @@ +code[class*='language-'] { + color: white; + hyphens: none; + tab-size: 4; + font-size: 1em; + word-wrap: normal; + background: none; + text-align: left; + word-break: normal; + -ms-hyphens: none; + -o-tab-size: 4; + font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace; + line-height: 1.5; + text-shadow: 0 -0.1em 0.2em black; + white-space: pre; + -moz-hyphens: none; + word-spacing: normal; + -moz-tab-size: 4; + -webkit-hyphens: none; +} +pre[class*='language-'] { + color: white; + border: 0.3em solid hsl(30, 20%, 40%); + margin: 0.5em 0; + hyphens: none; + padding: 1em; + overflow: auto; + tab-size: 4; + font-size: 1em; + word-wrap: normal; + background: hsl(30, 20%, 25%); + box-shadow: 1px 1px 0.5em black inset; + text-align: left; + word-break: normal; + -ms-hyphens: none; + -o-tab-size: 4; + font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace; + line-height: 1.5; + text-shadow: 0 -0.1em 0.2em black; + white-space: pre; + -moz-hyphens: none; + word-spacing: normal; + -moz-tab-size: 4; + border-radius: 0.5em; + -webkit-hyphens: none; +} +:not(pre) > code[class*='language-'] { + border: 0.13em solid hsl(30, 20%, 40%); + padding: 0.15em 0.2em 0.05em; + background: hsl(30, 20%, 25%); + box-shadow: 1px 1px 0.3em -0.1em black inset; + white-space: normal; + border-radius: 0.3em; +} +.comment { + color: hsl(30, 20%, 50%); +} +.prolog { + color: hsl(30, 20%, 50%); +} +.doctype { + color: hsl(30, 20%, 50%); +} +.cdata { + color: hsl(30, 20%, 50%); +} +.punctuation { + -opacity: 0.7; +} +.namespace { + -opacity: 0.7; +} +.property { + color: hsl(350, 40%, 70%); +} +.tag { + color: hsl(350, 40%, 70%); +} +.boolean { + color: hsl(350, 40%, 70%); +} +.number { + color: hsl(350, 40%, 70%); +} +.constant { + color: hsl(350, 40%, 70%); +} +.symbol { + color: hsl(350, 40%, 70%); +} +.selector { + color: hsl(75, 70%, 60%); +} +.attr-name { + color: hsl(75, 70%, 60%); +} +.string { + color: hsl(75, 70%, 60%); +} +.char { + color: hsl(75, 70%, 60%); +} +.builtin { + color: hsl(75, 70%, 60%); +} +.inserted { + color: hsl(75, 70%, 60%); +} +.operator { + color: hsl(40, 90%, 60%); +} +.entity { + color: hsl(40, 90%, 60%); + cursor: help; +} +.url { + color: hsl(40, 90%, 60%); +} +.language-css .token.string { + color: hsl(40, 90%, 60%); +} +.style .token.string { + color: hsl(40, 90%, 60%); +} +.variable { + color: hsl(40, 90%, 60%); +} +.atrule { + color: hsl(350, 40%, 70%); +} +.attr-value { + color: hsl(350, 40%, 70%); +} +.keyword { + color: hsl(350, 40%, 70%); +} +.regex { + color: #e90; +} +.important { + color: #e90; + font-weight: bold; +} +.bold { + font-weight: bold; +} +.italic { + font-style: italic; +} +.deleted { + color: red; +} diff --git a/examples/nextjs-ai-chatbot/components/messages.tsx b/examples/nextjs-ai-chatbot/components/messages.tsx index 3e3b75c..0255dbb 100644 --- a/examples/nextjs-ai-chatbot/components/messages.tsx +++ b/examples/nextjs-ai-chatbot/components/messages.tsx @@ -8,6 +8,9 @@ import { useEffect, useRef } from 'react'; import { useParams } from 'next/navigation'; import Image from 'next/image'; import ToolsOutput from './tools'; +import { AIMarkdown } from '@stream-io/ai-components-react'; +import Weather from './tools/weather'; +import clsx from 'clsx'; export default function Messages() { const { messages, status, isLoadingMessages } = useApp(); @@ -81,11 +84,48 @@ export default function Messages() { : 'max-w-full overflow-scroll' }`} > - +
{children}
, + code: ({ children, ...rest }) => ( + + {children} + + ), + ol: ({ node, children, ...props }) => ( +
    + {children} +
+ ), + li: ({ node, children, ...props }) => { + return ( +
  • + {children} +
  • + ); + }, + ul: ({ node, children, ...props }) => { + return ( + + ); + }, + }} + > {m.parts .map((part: any) => (part.type === 'text' ? part.text : '')) .join('')} -
    + diff --git a/examples/nextjs-ai-chatbot/package.json b/examples/nextjs-ai-chatbot/package.json index 90fd816..5cd39bf 100644 --- a/examples/nextjs-ai-chatbot/package.json +++ b/examples/nextjs-ai-chatbot/package.json @@ -13,10 +13,12 @@ "@ai-sdk/openai": "^2.0.53", "@ai-sdk/react": "^2.0.76", "@ai-sdk/xai": "^2.0.27", + "@stream-io/ai-components-react": "workspace:^", "@stream-io/ai-sdk-storage": "workspace:^", "@tailwindcss/typography": "^0.5.19", "ai": "^5.0.76", "animate.css": "^4.1.1", + "clsx": "^2.1.1", "daisyui": "^5.3.7", "lucide-react": "^0.546.0", "next": "16.0.0", diff --git a/packages/react-sdk/package.json b/packages/react-sdk/package.json index 20901a4..f8ef9f7 100644 --- a/packages/react-sdk/package.json +++ b/packages/react-sdk/package.json @@ -9,7 +9,7 @@ ".": { "types": "./dist/types/index.d.ts", "browser": { - "import": "./dist/esm/index.mjs", + "import": "./dist/es/index.mjs", "require": "./dist/cjs/index.js" }, "default": "./dist/cjs/index.js" @@ -17,7 +17,7 @@ "./stream": { "types": "./dist/types/stream/index.d.ts", "browser": { - "import": "./dist/esm/stream/index.mjs", + "import": "./dist/es/stream/index.mjs", "require": "./dist/cjs/stream/index.js" }, "default": "./dist/cjs/stream/index.js" @@ -30,8 +30,9 @@ ], "scripts": { "build": "rimraf ./dist && concurrently 'vite build' 'tsc -p ./tsconfig.lib.json'", + "dev": "concurrently 'vite build --watch' 'tsc -p ./tsconfig.lib.json' --watch", "prepare": "pnpm run build", - "test": "vitest" + "test": "" }, "keywords": [ "ai", @@ -50,13 +51,19 @@ "react-dom": "^17 || ^18 || ^19" }, "devDependencies": { - "@types/react": "^17", - "@types/react-dom": "^17", "@types/node": "^24", + "@types/react": "^18.3.26", + "@types/react-dom": "^18.3.7", + "@types/react-syntax-highlighter": "^15.5.13", "concurrently": "catalog:", - "vite": "catalog:", "rimraf": "^6.0.1", "typescript": "catalog:", - "vitest": "catalog:" + "vite": "catalog:" + }, + "dependencies": { + "clsx": "^2.1.1", + "react-markdown": "^10.1.0", + "react-syntax-highlighter": "15.5.0", + "remark-gfm": "^4.0.1" } } diff --git a/packages/react-sdk/src/components/ai-markdown.tsx b/packages/react-sdk/src/components/ai-markdown.tsx new file mode 100644 index 0000000..d648e53 --- /dev/null +++ b/packages/react-sdk/src/components/ai-markdown.tsx @@ -0,0 +1,176 @@ +import React, { + Children, + type ComponentProps, + type ComponentType, + type ElementType, + isValidElement, + useContext, + useMemo, +} from 'react'; +import ReactMarkdown, { + type Components, + type ExtraProps, +} from 'react-markdown'; +import remarkGfm from 'remark-gfm'; +import { Prism, type SyntaxHighlighterProps } from 'react-syntax-highlighter'; +import clsx from 'clsx'; + +const SyntaxHighlighter = + Prism as unknown as ComponentType; + +const getToolOrLanguage = (className: string = '') => { + return className.match(/language-(?[\w-]+)/)?.groups?.['tool']; +}; + +type ToolComponents = Record>; +type MarkdownComponents = Components; + +const AIMarkdownContext = React.createContext<{ + toolComponents: ToolComponents; +}>({ toolComponents: {} }); + +type BaseDefaultPreProps = ComponentProps<'pre'> & ExtraProps; + +type DefaultPreProps = BaseDefaultPreProps & { + Pre?: ComponentType | ElementType; +}; + +const DefaultPre = (props: DefaultPreProps) => { + const { children, Pre = 'pre', ...restProps } = props; + + const { toolComponents } = useContext(AIMarkdownContext); + + const [codeElement] = Children.toArray(children); + + if ( + !isValidElement(codeElement) || + codeElement.props.node.tagName !== 'code' + ) { + return
    {children}
    ; + } + + const tool = getToolOrLanguage(codeElement.props.className); + + // grab from pre-registered component set and render + const Component = typeof tool === 'string' ? toolComponents[tool] : null; + if (Component) { + return ; + } + + // render just a fragment with the code content + // which gets replaced by SyntaxHighlighter (it itself renders pre too) + if (tool) { + return <>{children}; + } + + // treat as regular pre/code block if there's no tool/language + return
    {children}
    ; +}; + +const DefaultSyntaxHighlighter = ({ + children, + language, +}: BaseDefaultCodeProps) => { + return ( + + {children as string} + + ); +}; + +type BaseDefaultCodeProps = ComponentProps<'code'> & + ExtraProps & { language?: string; inline?: boolean }; + +type DefaultCodeProps = BaseDefaultCodeProps & { + Code?: ComponentType | ElementType; + SyntaxHighlighter?: ComponentType; +}; + +const DefaultCode = (props: DefaultCodeProps) => { + const { + node, + className, + children, + SyntaxHighlighter = DefaultSyntaxHighlighter, + Code = 'code', + ...restProps + } = props; + + const language = getToolOrLanguage(className); + const inline = !language; + + const Component = inline ? Code : SyntaxHighlighter; + + return ( + + {children} + + ); +}; + +const DefaultComponents = { + pre: DefaultPre, + code: DefaultCode, +} as const; + +interface AIMarkdown { + (props: { + children: string; + toolComponents?: ToolComponents; + markdownComponents?: MarkdownComponents; + }): JSX.Element; + default: typeof DefaultComponents; +} + +export const AIMarkdown: AIMarkdown = (props) => { + const mergedMarkdownComponents: MarkdownComponents = useMemo( + () => ({ + ...DefaultComponents, + ...props.markdownComponents, + }), + [props.markdownComponents], + ); + + const mergedToolComponents: ToolComponents = useMemo( + () => ({ + ...props.toolComponents, + // ...DefaultTools, + }), + [props.toolComponents], + ); + + return ( + + + {props.children} + + + ); +}; + +AIMarkdown.default = DefaultComponents; diff --git a/packages/react-sdk/src/components/tools/index.ts b/packages/react-sdk/src/components/tools/index.ts new file mode 100644 index 0000000..e69de29 diff --git a/packages/react-sdk/src/index.ts b/packages/react-sdk/src/index.ts index e69de29..e1f5e19 100644 --- a/packages/react-sdk/src/index.ts +++ b/packages/react-sdk/src/index.ts @@ -0,0 +1 @@ +export * from './components/ai-markdown'; diff --git a/packages/react-sdk/tsconfig.lib.json b/packages/react-sdk/tsconfig.lib.json index a3ae812..390b480 100644 --- a/packages/react-sdk/tsconfig.lib.json +++ b/packages/react-sdk/tsconfig.lib.json @@ -1,11 +1,12 @@ { "extends": "../../tsconfig.root.json", "compilerOptions": { + "jsx": "react-jsx", "rootDir": "./src", "outDir": "./dist/types", "emitDeclarationOnly": true, "declarationMap": true, - "verbatimModuleSyntax": false + "verbatimModuleSyntax": true }, "include": ["src"], "exclude": ["src/**/*.test.{ts,tsx}"] diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index c7d84e6..55b56e4 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -65,6 +65,9 @@ importers: '@ai-sdk/xai': specifier: ^2.0.27 version: 2.0.30(zod@4.1.12) + '@stream-io/ai-components-react': + specifier: workspace:^ + version: link:../../packages/react-sdk '@stream-io/ai-sdk-storage': specifier: workspace:^ version: link:../../packages/node-sdk @@ -73,10 +76,13 @@ importers: version: 0.5.19(tailwindcss@4.1.16) ai: specifier: ^5.0.76 - version: 5.0.81(zod@4.1.12) + version: 5.0.86(zod@4.1.12) animate.css: specifier: ^4.1.1 version: 4.1.1 + clsx: + specifier: ^2.1.1 + version: 2.1.1 daisyui: specifier: ^5.3.7 version: 5.4.2 @@ -189,7 +195,7 @@ importers: dependencies: ai: specifier: ^5.0.0 - version: 5.0.81(zod@4.1.12) + version: 5.0.86(zod@4.1.12) stream-chat: specifier: ^9.24.0 version: 9.25.0 @@ -258,22 +264,37 @@ importers: packages/react-sdk: dependencies: + clsx: + specifier: ^2.1.1 + version: 2.1.1 react: specifier: ^17 || ^18 || ^19 version: 19.2.0 react-dom: specifier: ^17 || ^18 || ^19 version: 19.2.0(react@19.2.0) + react-markdown: + specifier: ^10.1.0 + version: 10.1.0(@types/react@18.3.26)(react@19.2.0) + react-syntax-highlighter: + specifier: 15.5.0 + version: 15.5.0(react@19.2.0) + remark-gfm: + specifier: ^4.0.1 + version: 4.0.1 devDependencies: '@types/node': specifier: ^24 version: 24.9.1 '@types/react': - specifier: ^17 - version: 17.0.89 + specifier: ^18.3.26 + version: 18.3.26 '@types/react-dom': - specifier: ^17 - version: 17.0.26(@types/react@17.0.89) + specifier: ^18.3.7 + version: 18.3.7(@types/react@18.3.26) + '@types/react-syntax-highlighter': + specifier: ^15.5.13 + version: 15.5.13 concurrently: specifier: 'catalog:' version: 9.2.1 @@ -286,9 +307,6 @@ importers: vite: specifier: 'catalog:' version: 7.1.11(@types/node@24.9.1)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(yaml@2.8.1) - vitest: - specifier: 'catalog:' - version: 4.0.6(@types/debug@4.1.12)(@types/node@24.9.1)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(yaml@2.8.1) packages: @@ -300,12 +318,6 @@ packages: graphql: optional: true - '@ai-sdk/gateway@2.0.2': - resolution: {integrity: sha512-25F1qPqZxOw9IcV9OQCL29hV4HAFLw5bFWlzQLBi5aDhEZsTMT2rMi3umSqNaUxrrw+dLRtjOL7RbHC+WjbA/A==} - engines: {node: '>=18'} - peerDependencies: - zod: ^3.25.76 || ^4.1.8 - '@ai-sdk/gateway@2.0.5': resolution: {integrity: sha512-5TTDSl0USWY6YGnb4QmJGplFZhk+p9OT7hZevAaER6OGiZ17LB1GypsGYDpNo/MiVMklk8kX4gk6p1/R/EiJ8Q==} engines: {node: '>=18'} @@ -330,12 +342,6 @@ packages: peerDependencies: zod: ^3.25.76 || ^4.1.8 - '@ai-sdk/provider-utils@3.0.13': - resolution: {integrity: sha512-aXFLBLRPTUYA853MJliItefSXeJPl+mg0KSjbToP41kJ+banBmHO8ZPGLJhNqGlCU82o11TYN7G05EREKX8CkA==} - engines: {node: '>=18'} - peerDependencies: - zod: ^3.25.76 || ^4.1.8 - '@ai-sdk/provider-utils@3.0.15': resolution: {integrity: sha512-kOc6Pxb7CsRlNt+sLZKL7/VGQUd7ccl3/tIK+Bqf5/QhHR0Qm3qRBMz1IwU1RmjJEZA73x+KB5cUckbDl2WF7Q==} engines: {node: '>=18'} @@ -379,10 +385,6 @@ packages: resolution: {integrity: sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==} engines: {node: '>=6.9.0'} - '@babel/compat-data@7.28.4': - resolution: {integrity: sha512-YsmSKC29MJwf0gF8Rjjrg5LQCmyh+j/nD8/eP7f+BeoQTKYqs9RoWbjGOdy0+1Ekr68RJZMUOPVQaQisnIo4Rw==} - engines: {node: '>=6.9.0'} - '@babel/compat-data@7.28.5': resolution: {integrity: sha512-6uFXyCayocRbqhZOB+6XcuZbkMNimwfVGFji8CTZnCzOHVGvDqzvitu1re2AU5LROliz7eQPhB8CpAMvnx9EjA==} engines: {node: '>=6.9.0'} @@ -391,10 +393,6 @@ packages: resolution: {integrity: sha512-2BCOP7TN8M+gVDj7/ht3hsaO/B/n5oDbiAyyvnRlNOs+u1o+JWNYTQrmpuNp1/Wq2gcFrI01JAW+paEKDMx/CA==} engines: {node: '>=6.9.0'} - '@babel/generator@7.28.3': - resolution: {integrity: sha512-3lSpxGgvnmZznmBkCRnVREPUFJv2wrv9iAoFDvADJc0ypmdOxdUtcLeBgBJ6zE0PMeTKnxeQzyk0xTBq4Ep7zw==} - engines: {node: '>=6.9.0'} - '@babel/generator@7.28.5': resolution: {integrity: sha512-3EwLFhZ38J4VyIP6WNtt2kUdW9dokXA9Cr4IVIFHuCpZ3H8/YFOl5JjZHisrn1fATPBmKKqXzDFvh9fUwHz6CQ==} engines: {node: '>=6.9.0'} @@ -470,10 +468,6 @@ packages: resolution: {integrity: sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==} engines: {node: '>=6.9.0'} - '@babel/helper-validator-identifier@7.27.1': - resolution: {integrity: sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==} - engines: {node: '>=6.9.0'} - '@babel/helper-validator-identifier@7.28.5': resolution: {integrity: sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==} engines: {node: '>=6.9.0'} @@ -494,11 +488,6 @@ packages: resolution: {integrity: sha512-llL88JShoCsth8fF8R4SJnIn+WLvR6ccFxu1H3FlMhDontdcmZWf2HgIZ7AIqV3Xcck1idlohrN4EUBQz6klbw==} engines: {node: '>=6.9.0'} - '@babel/parser@7.28.4': - resolution: {integrity: sha512-yZbBqeM6TkpP9du/I2pUZnJsRMGGvOuIrhjzC1AwHwW+6he4mni6Bp/m8ijn0iOuZuPI2BfkCoSRunpyjnrQKg==} - engines: {node: '>=6.0.0'} - hasBin: true - '@babel/parser@7.28.5': resolution: {integrity: sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ==} engines: {node: '>=6.0.0'} @@ -1075,18 +1064,10 @@ packages: resolution: {integrity: sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==} engines: {node: '>=6.9.0'} - '@babel/traverse@7.28.4': - resolution: {integrity: sha512-YEzuboP2qvQavAcjgQNVgsvHIDv6ZpwXvcvjmyySP2DIMuByS/6ioU5G9pYrWHM6T2YDfc7xga9iNzYOs12CFQ==} - engines: {node: '>=6.9.0'} - '@babel/traverse@7.28.5': resolution: {integrity: sha512-TCCj4t55U90khlYkVV/0TfkJkAkUg3jZFA3Neb7unZT8CPok7iiRfaX0F+WnqWqt7OxhOn0uBKXCw4lbL8W0aQ==} engines: {node: '>=6.9.0'} - '@babel/types@7.28.4': - resolution: {integrity: sha512-bkFqkLhh3pMBUQQkpVgWDWq/lqzc2678eUyDlTBhRqhCHFguYYGM0Efga7tYk4TogG/3x0EEl66/OQ+WGbWB/Q==} - engines: {node: '>=6.9.0'} - '@babel/types@7.28.5': resolution: {integrity: sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA==} engines: {node: '>=6.9.0'} @@ -2193,10 +2174,10 @@ packages: '@types/prop-types@15.7.15': resolution: {integrity: sha512-F6bEyamV9jKGAFBEmlQnesRPGOQqS2+Uwi0Em15xenOxHaf2hv6L8YCVn3rPdPJOiJfPiCnLIRyvwVaqMY3MIw==} - '@types/react-dom@17.0.26': - resolution: {integrity: sha512-Z+2VcYXJwOqQ79HreLU/1fyQ88eXSSFh6I3JdrEHQIfYSI0kCQpTGvOrbE6jFGGYXKsHuwY9tBa/w5Uo6KzrEg==} + '@types/react-dom@18.3.7': + resolution: {integrity: sha512-MEe3UeoENYVFXzoXEWsvcpg6ZvlrFNlOQ7EOsvhI3CfAXwzPfO8Qwuxd40nepsYKqyyVQnTdEfv68q91yLcKrQ==} peerDependencies: - '@types/react': ^17.0.0 + '@types/react': ^18.0.0 '@types/react-dom@19.2.2': resolution: {integrity: sha512-9KQPoO6mZCi7jcIStSnlOWn2nEF3mNmyr3rIAsGnAbQKYbRLyqmeSc39EVgtxXVia+LMT8j3knZLAZAh+xLmrw==} @@ -2206,8 +2187,8 @@ packages: '@types/react-syntax-highlighter@15.5.13': resolution: {integrity: sha512-uLGJ87j6Sz8UaBAooU0T6lWJ0dBmjZgN1PZTrj05TNql2/XpC6+4HhMT5syIdFUUt+FASfCeLLv4kBygNU+8qA==} - '@types/react@17.0.89': - resolution: {integrity: sha512-I98SaDCar5lvEYl80ClRIUztH/hyWHR+I2f+5yTVp/MQ205HgYkA2b5mVdry/+nsEIrf8I65KA5V/PASx68MsQ==} + '@types/react@18.3.26': + resolution: {integrity: sha512-RFA/bURkcKzx/X9oumPG9Vp3D3JUgus/d0b67KB0t5S/raciymilkOa66olh78MUI92QLbEJevO7rvqU/kjwKA==} '@types/react@19.1.1': resolution: {integrity: sha512-ePapxDL7qrgqSF67s0h9m412d9DbXyC1n59O2st+9rjuuamWsZuD2w55rqY12CbzsZ7uVXb5Nw0gEp9Z8MMutQ==} @@ -2215,9 +2196,6 @@ packages: '@types/react@19.2.2': resolution: {integrity: sha512-6mDvHUFSjyT2B2yeNx2nUgMxh9LtOWvkhIU3uePn2I2oyNymUAX1NIsdgviM4CH+JSrp2D2hsMvJOkxY+0wNRA==} - '@types/scheduler@0.16.8': - resolution: {integrity: sha512-WZLiwShhwLRmeV6zH+GkbOFT6Z6VklCItrDioxUnv+u4Ll+8vKeFySoFyK/0ctcRpOmwAicELfmys1sDc/Rw+A==} - '@types/stack-utils@2.0.3': resolution: {integrity: sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==} @@ -2470,12 +2448,6 @@ packages: resolution: {integrity: sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==} engines: {node: '>=8'} - ai@5.0.81: - resolution: {integrity: sha512-SB7oMC9QSpIu1VLswFTZuhhpfQfrGtFBUbWLtHBkhjWZIQskjtcdEhB+N4yO9hscdc2wYtjw/tacgoxX93QWFw==} - engines: {node: '>=18'} - peerDependencies: - zod: ^3.25.76 || ^4.1.8 - ai@5.0.86: resolution: {integrity: sha512-ooHwNTkLdedFf98iQhtSc5btc/P4UuXuOpYneoifq0190vqosLunNdW8Hs6CiE0Am7YOGNplDK56JIPlHZIL4w==} engines: {node: '>=18'} @@ -2872,6 +2844,10 @@ packages: resolution: {integrity: sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==} engines: {node: '>=0.8'} + clsx@2.1.1: + resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==} + engines: {node: '>=6'} + color-convert@1.9.3: resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==} @@ -6097,13 +6073,6 @@ snapshots: '@0no-co/graphql.web@1.2.0': {} - '@ai-sdk/gateway@2.0.2(zod@4.1.12)': - dependencies: - '@ai-sdk/provider': 2.0.0 - '@ai-sdk/provider-utils': 3.0.13(zod@4.1.12) - '@vercel/oidc': 3.0.3 - zod: 4.1.12 - '@ai-sdk/gateway@2.0.5(zod@4.1.12)': dependencies: '@ai-sdk/provider': 2.0.0 @@ -6129,13 +6098,6 @@ snapshots: '@ai-sdk/provider-utils': 3.0.15(zod@4.1.12) zod: 4.1.12 - '@ai-sdk/provider-utils@3.0.13(zod@4.1.12)': - dependencies: - '@ai-sdk/provider': 2.0.0 - '@standard-schema/spec': 1.0.0 - eventsource-parser: 3.0.6 - zod: 4.1.12 - '@ai-sdk/provider-utils@3.0.15(zod@4.1.12)': dependencies: '@ai-sdk/provider': 2.0.0 @@ -6178,25 +6140,23 @@ snapshots: '@babel/code-frame@7.27.1': dependencies: - '@babel/helper-validator-identifier': 7.27.1 + '@babel/helper-validator-identifier': 7.28.5 js-tokens: 4.0.0 picocolors: 1.1.1 - '@babel/compat-data@7.28.4': {} - '@babel/compat-data@7.28.5': {} '@babel/core@7.28.4': dependencies: '@babel/code-frame': 7.27.1 - '@babel/generator': 7.28.3 + '@babel/generator': 7.28.5 '@babel/helper-compilation-targets': 7.27.2 '@babel/helper-module-transforms': 7.28.3(@babel/core@7.28.4) '@babel/helpers': 7.28.4 - '@babel/parser': 7.28.4 + '@babel/parser': 7.28.5 '@babel/template': 7.27.2 - '@babel/traverse': 7.28.4 - '@babel/types': 7.28.4 + '@babel/traverse': 7.28.5 + '@babel/types': 7.28.5 '@jridgewell/remapping': 2.3.5 convert-source-map: 2.0.0 debug: 4.4.3 @@ -6206,14 +6166,6 @@ snapshots: transitivePeerDependencies: - supports-color - '@babel/generator@7.28.3': - dependencies: - '@babel/parser': 7.28.4 - '@babel/types': 7.28.4 - '@jridgewell/gen-mapping': 0.3.13 - '@jridgewell/trace-mapping': 0.3.31 - jsesc: 3.1.0 - '@babel/generator@7.28.5': dependencies: '@babel/parser': 7.28.5 @@ -6224,11 +6176,11 @@ snapshots: '@babel/helper-annotate-as-pure@7.27.3': dependencies: - '@babel/types': 7.28.4 + '@babel/types': 7.28.5 '@babel/helper-compilation-targets@7.27.2': dependencies: - '@babel/compat-data': 7.28.4 + '@babel/compat-data': 7.28.5 '@babel/helper-validator-option': 7.27.1 browserslist: 4.27.0 lru-cache: 5.1.1 @@ -6276,8 +6228,8 @@ snapshots: '@babel/helper-module-imports@7.27.1': dependencies: - '@babel/traverse': 7.28.4 - '@babel/types': 7.28.4 + '@babel/traverse': 7.28.5 + '@babel/types': 7.28.5 transitivePeerDependencies: - supports-color @@ -6285,8 +6237,8 @@ snapshots: dependencies: '@babel/core': 7.28.4 '@babel/helper-module-imports': 7.27.1 - '@babel/helper-validator-identifier': 7.27.1 - '@babel/traverse': 7.28.4 + '@babel/helper-validator-identifier': 7.28.5 + '@babel/traverse': 7.28.5 transitivePeerDependencies: - supports-color @@ -6301,7 +6253,7 @@ snapshots: '@babel/core': 7.28.4 '@babel/helper-annotate-as-pure': 7.27.3 '@babel/helper-wrap-function': 7.28.3 - '@babel/traverse': 7.28.4 + '@babel/traverse': 7.28.5 transitivePeerDependencies: - supports-color @@ -6310,21 +6262,19 @@ snapshots: '@babel/core': 7.28.4 '@babel/helper-member-expression-to-functions': 7.28.5 '@babel/helper-optimise-call-expression': 7.27.1 - '@babel/traverse': 7.28.4 + '@babel/traverse': 7.28.5 transitivePeerDependencies: - supports-color '@babel/helper-skip-transparent-expression-wrappers@7.27.1': dependencies: - '@babel/traverse': 7.28.4 + '@babel/traverse': 7.28.5 '@babel/types': 7.28.5 transitivePeerDependencies: - supports-color '@babel/helper-string-parser@7.27.1': {} - '@babel/helper-validator-identifier@7.27.1': {} - '@babel/helper-validator-identifier@7.28.5': {} '@babel/helper-validator-option@7.27.1': {} @@ -6332,7 +6282,7 @@ snapshots: '@babel/helper-wrap-function@7.28.3': dependencies: '@babel/template': 7.27.2 - '@babel/traverse': 7.28.4 + '@babel/traverse': 7.28.5 '@babel/types': 7.28.5 transitivePeerDependencies: - supports-color @@ -6340,19 +6290,15 @@ snapshots: '@babel/helpers@7.28.4': dependencies: '@babel/template': 7.27.2 - '@babel/types': 7.28.4 + '@babel/types': 7.28.5 '@babel/highlight@7.25.9': dependencies: - '@babel/helper-validator-identifier': 7.27.1 + '@babel/helper-validator-identifier': 7.28.5 chalk: 2.4.2 js-tokens: 4.0.0 picocolors: 1.1.1 - '@babel/parser@7.28.4': - dependencies: - '@babel/types': 7.28.4 - '@babel/parser@7.28.5': dependencies: '@babel/types': 7.28.5 @@ -6536,7 +6482,7 @@ snapshots: '@babel/core': 7.28.4 '@babel/helper-plugin-utils': 7.27.1 '@babel/helper-remap-async-to-generator': 7.27.1(@babel/core@7.28.4) - '@babel/traverse': 7.28.4 + '@babel/traverse': 7.28.5 transitivePeerDependencies: - supports-color @@ -6583,7 +6529,7 @@ snapshots: '@babel/helper-globals': 7.28.0 '@babel/helper-plugin-utils': 7.27.1 '@babel/helper-replace-supers': 7.27.1(@babel/core@7.28.4) - '@babel/traverse': 7.28.4 + '@babel/traverse': 7.28.5 transitivePeerDependencies: - supports-color @@ -6660,7 +6606,7 @@ snapshots: '@babel/core': 7.28.4 '@babel/helper-compilation-targets': 7.27.2 '@babel/helper-plugin-utils': 7.27.1 - '@babel/traverse': 7.28.4 + '@babel/traverse': 7.28.5 transitivePeerDependencies: - supports-color @@ -6746,7 +6692,7 @@ snapshots: '@babel/helper-plugin-utils': 7.27.1 '@babel/plugin-transform-destructuring': 7.28.5(@babel/core@7.28.4) '@babel/plugin-transform-parameters': 7.27.7(@babel/core@7.28.4) - '@babel/traverse': 7.28.4 + '@babel/traverse': 7.28.5 transitivePeerDependencies: - supports-color @@ -6827,7 +6773,7 @@ snapshots: '@babel/helper-module-imports': 7.27.1 '@babel/helper-plugin-utils': 7.27.1 '@babel/plugin-syntax-jsx': 7.27.1(@babel/core@7.28.4) - '@babel/types': 7.28.4 + '@babel/types': 7.28.5 transitivePeerDependencies: - supports-color @@ -7043,20 +6989,8 @@ snapshots: '@babel/template@7.27.2': dependencies: '@babel/code-frame': 7.27.1 - '@babel/parser': 7.28.4 - '@babel/types': 7.28.4 - - '@babel/traverse@7.28.4': - dependencies: - '@babel/code-frame': 7.27.1 - '@babel/generator': 7.28.3 - '@babel/helper-globals': 7.28.0 - '@babel/parser': 7.28.4 - '@babel/template': 7.27.2 - '@babel/types': 7.28.4 - debug: 4.4.3 - transitivePeerDependencies: - - supports-color + '@babel/parser': 7.28.5 + '@babel/types': 7.28.5 '@babel/traverse@7.28.5': dependencies: @@ -7070,11 +7004,6 @@ snapshots: transitivePeerDependencies: - supports-color - '@babel/types@7.28.4': - dependencies: - '@babel/helper-string-parser': 7.27.1 - '@babel/helper-validator-identifier': 7.27.1 - '@babel/types@7.28.5': dependencies: '@babel/helper-string-parser': 7.27.1 @@ -7560,7 +7489,7 @@ snapshots: dependencies: '@babel/code-frame': 7.27.1 '@babel/core': 7.28.4 - '@babel/generator': 7.28.3 + '@babel/generator': 7.28.5 '@expo/config': 12.0.10 '@expo/env': 2.0.7 '@expo/json-file': 10.0.7 @@ -7965,7 +7894,7 @@ snapshots: '@react-native/babel-plugin-codegen@0.81.5(@babel/core@7.28.4)': dependencies: - '@babel/traverse': 7.28.4 + '@babel/traverse': 7.28.5 '@react-native/codegen': 0.81.5(@babel/core@7.28.4) transitivePeerDependencies: - '@babel/core' @@ -8034,7 +7963,7 @@ snapshots: '@react-native/codegen@0.82.1(@babel/core@7.28.4)': dependencies: '@babel/core': 7.28.4 - '@babel/parser': 7.28.4 + '@babel/parser': 7.28.5 glob: 7.2.3 hermes-parser: 0.32.0 invariant: 2.2.4 @@ -8318,24 +8247,24 @@ snapshots: '@types/babel__core@7.20.5': dependencies: - '@babel/parser': 7.28.4 - '@babel/types': 7.28.4 + '@babel/parser': 7.28.5 + '@babel/types': 7.28.5 '@types/babel__generator': 7.27.0 '@types/babel__template': 7.4.4 '@types/babel__traverse': 7.28.0 '@types/babel__generator@7.27.0': dependencies: - '@babel/types': 7.28.4 + '@babel/types': 7.28.5 '@types/babel__template@7.4.4': dependencies: - '@babel/parser': 7.28.4 - '@babel/types': 7.28.4 + '@babel/parser': 7.28.5 + '@babel/types': 7.28.5 '@types/babel__traverse@7.28.0': dependencies: - '@babel/types': 7.28.4 + '@babel/types': 7.28.5 '@types/chai@5.2.3': dependencies: @@ -8407,9 +8336,9 @@ snapshots: '@types/prop-types@15.7.15': {} - '@types/react-dom@17.0.26(@types/react@17.0.89)': + '@types/react-dom@18.3.7(@types/react@18.3.26)': dependencies: - '@types/react': 17.0.89 + '@types/react': 18.3.26 '@types/react-dom@19.2.2(@types/react@19.2.2)': dependencies: @@ -8419,10 +8348,9 @@ snapshots: dependencies: '@types/react': 19.2.2 - '@types/react@17.0.89': + '@types/react@18.3.26': dependencies: '@types/prop-types': 15.7.15 - '@types/scheduler': 0.16.8 csstype: 3.1.3 '@types/react@19.1.1': @@ -8433,8 +8361,6 @@ snapshots: dependencies: csstype: 3.1.3 - '@types/scheduler@0.16.8': {} - '@types/stack-utils@2.0.3': {} '@types/unist@2.0.11': {} @@ -8694,14 +8620,6 @@ snapshots: clean-stack: 2.2.0 indent-string: 4.0.0 - ai@5.0.81(zod@4.1.12): - dependencies: - '@ai-sdk/gateway': 2.0.2(zod@4.1.12) - '@ai-sdk/provider': 2.0.0 - '@ai-sdk/provider-utils': 3.0.13(zod@4.1.12) - '@opentelemetry/api': 1.9.0 - zod: 4.1.12 - ai@5.0.86(zod@4.1.12): dependencies: '@ai-sdk/gateway': 2.0.5(zod@4.1.12) @@ -8901,7 +8819,7 @@ snapshots: babel-plugin-polyfill-corejs2@0.4.14(@babel/core@7.28.4): dependencies: - '@babel/compat-data': 7.28.4 + '@babel/compat-data': 7.28.5 '@babel/core': 7.28.4 '@babel/helper-define-polyfill-provider': 0.6.5(@babel/core@7.28.4) semver: 6.3.1 @@ -8925,7 +8843,7 @@ snapshots: babel-plugin-react-compiler@1.0.0: dependencies: - '@babel/types': 7.28.4 + '@babel/types': 7.28.5 babel-plugin-react-native-web@0.21.2: {} @@ -9172,6 +9090,8 @@ snapshots: clone@1.0.4: {} + clsx@2.1.1: {} + color-convert@1.9.3: dependencies: color-name: 1.1.3 @@ -11171,9 +11091,9 @@ snapshots: metro-source-map@0.83.2: dependencies: - '@babel/traverse': 7.28.4 + '@babel/traverse': 7.28.5 '@babel/traverse--for-generate-function-map': '@babel/traverse@7.28.5' - '@babel/types': 7.28.4 + '@babel/types': 7.28.5 flow-enums-runtime: 0.0.6 invariant: 2.2.4 metro-symbolicate: 0.83.2 @@ -11186,9 +11106,9 @@ snapshots: metro-source-map@0.83.3: dependencies: - '@babel/traverse': 7.28.4 + '@babel/traverse': 7.28.5 '@babel/traverse--for-generate-function-map': '@babel/traverse@7.28.5' - '@babel/types': 7.28.4 + '@babel/types': 7.28.5 flow-enums-runtime: 0.0.6 invariant: 2.2.4 metro-symbolicate: 0.83.3 @@ -11224,9 +11144,9 @@ snapshots: metro-transform-plugins@0.83.2: dependencies: '@babel/core': 7.28.4 - '@babel/generator': 7.28.3 + '@babel/generator': 7.28.5 '@babel/template': 7.27.2 - '@babel/traverse': 7.28.4 + '@babel/traverse': 7.28.5 flow-enums-runtime: 0.0.6 nullthrows: 1.1.1 transitivePeerDependencies: @@ -11237,7 +11157,7 @@ snapshots: '@babel/core': 7.28.4 '@babel/generator': 7.28.5 '@babel/template': 7.27.2 - '@babel/traverse': 7.28.4 + '@babel/traverse': 7.28.5 flow-enums-runtime: 0.0.6 nullthrows: 1.1.1 transitivePeerDependencies: @@ -11246,9 +11166,9 @@ snapshots: metro-transform-worker@0.83.2: dependencies: '@babel/core': 7.28.4 - '@babel/generator': 7.28.3 - '@babel/parser': 7.28.4 - '@babel/types': 7.28.4 + '@babel/generator': 7.28.5 + '@babel/parser': 7.28.5 + '@babel/types': 7.28.5 flow-enums-runtime: 0.0.6 metro: 0.83.2 metro-babel-transformer: 0.83.2 @@ -11287,11 +11207,11 @@ snapshots: dependencies: '@babel/code-frame': 7.27.1 '@babel/core': 7.28.4 - '@babel/generator': 7.28.3 - '@babel/parser': 7.28.4 + '@babel/generator': 7.28.5 + '@babel/parser': 7.28.5 '@babel/template': 7.27.2 - '@babel/traverse': 7.28.4 - '@babel/types': 7.28.4 + '@babel/traverse': 7.28.5 + '@babel/types': 7.28.5 accepts: 1.3.8 chalk: 4.1.2 ci-info: 2.0.0 @@ -11334,11 +11254,11 @@ snapshots: dependencies: '@babel/code-frame': 7.27.1 '@babel/core': 7.28.4 - '@babel/generator': 7.28.3 - '@babel/parser': 7.28.4 + '@babel/generator': 7.28.5 + '@babel/parser': 7.28.5 '@babel/template': 7.27.2 - '@babel/traverse': 7.28.4 - '@babel/types': 7.28.4 + '@babel/traverse': 7.28.5 + '@babel/types': 7.28.5 accepts: 1.3.8 chalk: 4.1.2 ci-info: 2.0.0 @@ -12025,6 +11945,24 @@ snapshots: react-is@18.3.1: {} + react-markdown@10.1.0(@types/react@18.3.26)(react@19.2.0): + dependencies: + '@types/hast': 3.0.4 + '@types/mdast': 4.0.4 + '@types/react': 18.3.26 + devlop: 1.1.0 + hast-util-to-jsx-runtime: 2.3.6 + html-url-attributes: 3.0.1 + mdast-util-to-hast: 13.2.0 + react: 19.2.0 + remark-parse: 11.0.0 + remark-rehype: 11.1.2 + unified: 11.0.5 + unist-util-visit: 5.0.0 + vfile: 6.0.3 + transitivePeerDependencies: + - supports-color + react-markdown@10.1.0(@types/react@19.2.2)(react@19.2.0): dependencies: '@types/hast': 3.0.4