feat: render annotation inline (#101)

* feat: render annotation inline

* clean up

* demo inline

* update comment

* wait longer for annotaton

* add doc

* refactor: add annotationsRenderer

* fix: format

* change contract

* fix build

* defaultAnnotationRenderers

* use zod

* separate advanced example

* have isInline props for easily custom renderer

* fix: lint

* fix fomat

* introduce: readonly for artifact

* add editable example

* treat inline artifact as normal artifact

* still need outside artifact

* fix: duplicate artifact

* fix annotations reset

* extract artifacts from markdown

* update inline artifacts

* remove readonly

* update example page

* fix import

* update example

* update comment

* update advanced example

* update wiki inline example

* able to custom show inline or not

* Create grumpy-geckos-drive.md

* fix: format

* fix format

* fix lint

* fix build

* chore: clear inline parser

* remove useless config

* simplify example

* remove zod

* remove artifact in message annotations

* use tryParse to safe extract inline annotations

* update demo canvas

* remove unused props

* update changese

* fix: comment

---------

Co-authored-by: Marcus Schiesser <mail@marcusschiesser.de>
This commit is contained in:
Thuc Pham
2025-06-03 11:09:05 +07:00
committed by GitHub
parent 93e73725cf
commit bdae046268
25 changed files with 897 additions and 220 deletions
+5
View File
@@ -0,0 +1,5 @@
---
'@llamaindex/chat-ui': minor
---
feat: support inline rendering of annotations and use it for artifacts
+28 -26
View File
@@ -46,7 +46,20 @@ const initialMessages: Message[] = [
id: 'Yqc9VoIR3ANyzTj3',
role: 'assistant',
content:
'The artifact is a Python implementation of the binary search algorithm, which efficiently searches for a target value in a sorted list.',
'The artifact is a Python implementation of the binary search algorithm, which efficiently searches for a target value in a sorted list.' +
`\n\`\`\`annotation\n${JSON.stringify({
type: 'artifact',
data: {
type: 'code',
data: {
file_name: 'binary_search.py',
code: 'def binary_search(arr, target):\n left, right = 0, len(arr) - 1\n while left <= right:\n mid = left + (right - left) // 2\n if arr[mid] == target:\n return mid\n elif arr[mid] < target:\n left = mid + 1\n else:\n right = mid - 1\n return -1\n\n# Example usage:\n# sorted_list = [1, 2, 3, 4, 5]\n# target = 3\n# result = binary_search(sorted_list, target)\n# print(result) # Output: 2',
language: 'python',
},
created_at: 1745480281756,
},
})}
\n\`\`\`\n`,
annotations: [
{
userInput: 'Write binary search algorithm in python',
@@ -198,18 +211,6 @@ const initialMessages: Message[] = [
},
],
},
{
type: 'artifact',
data: {
type: 'code',
data: {
file_name: 'binary_search.py',
code: 'def binary_search(arr, target):\n left, right = 0, len(arr) - 1\n while left <= right:\n mid = left + (right - left) // 2\n if arr[mid] == target:\n return mid\n elif arr[mid] < target:\n left = mid + 1\n else:\n right = mid - 1\n return -1\n\n# Example usage:\n# sorted_list = [1, 2, 3, 4, 5]\n# target = 3\n# result = binary_search(sorted_list, target)\n# print(result) # Output: 2',
language: 'python',
},
created_at: 1745480281756,
},
},
{
input: [
{
@@ -275,7 +276,20 @@ const initialMessages: Message[] = [
id: 'yWQHYXgn6WT0oE7J',
role: 'assistant',
content:
'The artifact is a Python implementation of the binary search algorithm that includes logging statements to track the values of the left, right, and mid indices during each step of the search process.',
'The artifact is a Python implementation of the binary search algorithm that includes logging statements to track the values of the left, right, and mid indices during each step of the search process.' +
`\n\`\`\`annotation\n${JSON.stringify({
type: 'artifact',
data: {
type: 'code',
data: {
file_name: 'binary_search.py',
code: "import logging\n\nlogging.basicConfig(level=logging.INFO)\n\ndef binary_search(arr, target):\n left, right = 0, len(arr) - 1\n while left <= right:\n mid = left + (right - left) // 2\n logging.info(f'Left: {left}, Right: {right}, Mid: {mid}, Mid Value: {arr[mid]}')\n if arr[mid] == target:\n return mid\n elif arr[mid] < target:\n left = mid + 1\n else:\n right = mid - 1\n return -1\n\n# Example usage\nif __name__ == '__main__':\n arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]\n target = 7\n result = binary_search(arr, target)\n if result != -1:\n logging.info(f'Target found at index: {result}')\n else:\n logging.info('Target not found')",
language: 'python',
},
created_at: 1745480319651,
},
})}
\n\`\`\`\n`,
annotations: [
{
userInput: 'Add some logs to the code',
@@ -455,18 +469,6 @@ const initialMessages: Message[] = [
},
],
},
{
type: 'artifact',
data: {
type: 'code',
data: {
file_name: 'binary_search.py',
code: "import logging\n\nlogging.basicConfig(level=logging.INFO)\n\ndef binary_search(arr, target):\n left, right = 0, len(arr) - 1\n while left <= right:\n mid = left + (right - left) // 2\n logging.info(f'Left: {left}, Right: {right}, Mid: {mid}, Mid Value: {arr[mid]}')\n if arr[mid] == target:\n return mid\n elif arr[mid] < target:\n left = mid + 1\n else:\n right = mid - 1\n return -1\n\n# Example usage\nif __name__ == '__main__':\n arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]\n target = 7\n result = binary_search(arr, target)\n if result != -1:\n logging.info(f'Target found at index: {result}')\n else:\n logging.info('Target not found')",
language: 'python',
},
created_at: 1745480319651,
},
},
{
input: [
{
+5 -3
View File
@@ -9,7 +9,6 @@ Artifacts are interactive, editable content that appears in the chat canvas. The
The artifact system consists of:
- **Artifact Annotations** - Data structure for artifact content
- **ChatCanvas** - Side panel for displaying artifacts
- **Artifact Viewers** - Components for rendering different artifact types
- **Version Management** - Track changes and revisions
@@ -119,7 +118,11 @@ export async function POST(request: Request) {
},
}
controller.enqueue(encoder.encode(`8:${JSON.stringify([artifact])}\\n`))
// wrap the annotation in a code block with the language key is 'annotation'
const codeBlock = `\n\`\`\`annotation\n${JSON.stringify(codeArtifact)}\n\`\`\`\n`
// send the artifact with the 0: prefix to make it inline
controller.enqueue(encoder.encode(`0:${JSON.stringify(codeBlock)}\\n`))
// Send follow-up text
controller.enqueue(
@@ -392,7 +395,6 @@ The canvas automatically appears when artifacts are present:
<ChatMessage message={messageWithArtifact}>
<ChatMessage.Content>
<ChatMessage.Content.Markdown />
<ChatMessage.Content.Artifact /> {/* Triggers canvas display */}
</ChatMessage.Content>
</ChatMessage>
```
-2
View File
@@ -148,7 +148,6 @@ function CustomMessage({ message, isLast }) {
</ChatMessage.Avatar>
<ChatMessage.Content>
<ChatMessage.Content.Markdown />
<ChatMessage.Content.Artifact />
</ChatMessage.Content>
<ChatMessage.Actions />
</ChatMessage>
@@ -189,7 +188,6 @@ Main content area with annotation support:
<ChatMessage.Content isLoading={isLoading} append={append}>
<ChatMessage.Content.Markdown />
<ChatMessage.Content.Image />
<ChatMessage.Content.Artifact />
<ChatMessage.Content.Source />
<ChatMessage.Content.Event />
<ChatMessage.Content.AgentEvent />
-1
View File
@@ -87,7 +87,6 @@ function CustomChatMessages() {
</ChatMessage.Avatar>
<ChatMessage.Content isLoading={isLoading} append={append}>
<ChatMessage.Content.Markdown />
<ChatMessage.Content.Artifact />
<ChatMessage.Content.Source />
<CustomWeatherAnnotation />
</ChatMessage.Content>
@@ -0,0 +1,234 @@
/**
* Advanced Chat API Route Example
*
* This example demonstrates advanced streaming features:
* - Text streaming with token-by-token delivery
* - Both standard annotations (sent after text) and inline annotations (embedded in text)
* - Inline annotations are embedded as special code blocks within the markdown stream
* - Multiple annotation types: sources, artifacts, and custom components (wiki)
*
* Use this example to understand how to mix regular content with interactive
* components that appear at specific positions in the chat stream.
*/
import { NextResponse, type NextRequest } from 'next/server'
const TOKEN_DELAY = 30 // 30ms delay between tokens
const TEXT_PREFIX = '0:' // vercel ai text prefix
const ANNOTATION_PREFIX = '8:' // vercel ai annotation prefix
const INLINE_ANNOTATION_KEY = 'annotation' // the language key to detect inline annotation code in markdown
const ANNOTATION_DELAY = 1000 // 1 second delay between annotations
export async function POST(request: NextRequest) {
try {
const { messages } = await request.json()
const lastMessage = messages[messages.length - 1]
const stream = fakeChatStream(`User query: "${lastMessage.content}".\n`)
return new Response(stream, {
headers: {
'Content-Type': 'text/plain; charset=utf-8',
'X-Vercel-AI-Data-Stream': 'v1',
Connection: 'keep-alive',
},
})
} catch (error) {
const detail = (error as Error).message
return NextResponse.json({ detail }, { status: 500 })
}
}
const SAMPLE_TEXT = [
`
Welcome to the demo of @llamaindex/chat-ui. Let me show you the different types of components that can be triggered from the server.
### Markdown with code block
\`\`\`js
const a = 1
const b = 2
const c = a + b
console.log(c)
\`\`\`
`,
'\n ### Demo inline annotations \n',
'Here are some steps to create a simple wiki app: \n',
'1. Create package.json file:',
{
type: 'artifact',
data: {
type: 'code',
created_at: 1717334400000,
inline: true, // this artifact will be only displayed inline in the message
data: {
file_name: 'package.json',
language: 'json',
code: `{
"name": "wiki-app",
"version": "1.0.0",
"description": "Wiki application",
"main": "wiki.js",
"dependencies": {
"axios": "^1.0.0",
"wiki-api": "^2.1.0"
}
}`,
},
},
},
'2. Check the wiki fetching script:',
{
type: 'artifact',
data: {
created_at: 1717334500000,
inline: true, // this artifact will be only displayed inline in the message
type: 'code',
data: {
file_name: 'wiki.js',
language: 'javascript',
code: `async function getWiki(search) {
const response = await fetch("/api/wiki?search=" + search);
const data = await response.json();
return data;
}
getWiki("What is LlamaIndex?");`,
},
},
},
'3. Check the current wiki:',
{
type: 'wiki',
data: {
title: 'LlamaIndex',
summary: 'LlamaIndex is a framework for building AI applications.',
url: 'https://www.llamaindex.ai',
category: 'AI',
lastUpdated: '2025-06-02',
},
},
'#### 🎯 Demo generating a document artifact',
{
type: 'artifact',
data: {
inline: true, // this artifact will be only displayed inline in the message
type: 'document',
data: {
title: 'Sample document',
content: `# Getting Started Guide
## Introduction
This comprehensive guide will walk you through everything you need to know to get started with our platform. Whether you're a beginner or an experienced user, you'll find valuable information here.
## Key Features
- **Easy Setup**: Get running in minutes
- **Powerful Tools**: Access advanced capabilities
- **Great Documentation**: Find answers quickly
- **Active Community**: Get help when needed
## Setup Process
1. Install Dependencies
First, ensure you have all required dependencies installed on your system.
2. Configuration
Update your configuration files with the necessary settings:
- API keys
- Environment variables
- User preferences
3. First Steps
Begin with basic operations to familiarize yourself with the platform.
## Best Practices
- Always backup your data
- Follow security guidelines
- Keep your dependencies updated
- Document your changes
## Troubleshooting
If you encounter issues, try these steps:
1. Check logs for errors
2. Verify configurations
3. Update to latest version
4. Contact support if needed
## Additional Resources
- [Documentation](https://docs.example.com)
- [API Reference](https://api.example.com)
- [Community Forums](https://community.example.com)
Feel free to explore and reach out if you need assistance!`,
type: 'markdown',
},
},
},
'\n\n Please feel free to open the document in the canvas and edit it. The document will be saved as a new version',
]
const SAMPLE_SOURCES = [
{
type: 'sources',
data: {
nodes: [
{ id: '1', url: '/sample.pdf' },
{ id: '2', url: '/sample.pdf' },
],
},
},
]
const fakeChatStream = (query: string): ReadableStream => {
return new ReadableStream({
async start(controller) {
const encoder = new TextEncoder()
controller.enqueue(
encoder.encode(`${TEXT_PREFIX}${JSON.stringify(query)}\n`)
)
// insert inline annotations
for (const item of SAMPLE_TEXT) {
if (typeof item === 'string') {
for (const token of item.split(' ')) {
await new Promise(resolve => setTimeout(resolve, TOKEN_DELAY))
controller.enqueue(
encoder.encode(`${TEXT_PREFIX}${JSON.stringify(`${token} `)}\n`)
)
}
} else {
await new Promise(resolve => setTimeout(resolve, ANNOTATION_DELAY))
// append inline annotation with 0: prefix
const annotationCode = toInlineAnnotationCode(item)
controller.enqueue(
encoder.encode(`${TEXT_PREFIX}${JSON.stringify(annotationCode)}\n`)
)
}
}
// insert sources in fixed positions
for (const item of SAMPLE_SOURCES) {
controller.enqueue(
encoder.encode(`${ANNOTATION_PREFIX}${JSON.stringify([item])}\n`)
)
}
controller.close()
},
})
}
/**
* To append inline annotations to the stream, we need to wrap the annotation in a code block with the language key.
* The language key is `annotation` and the code block is wrapped in backticks.
* The prefix `0:` ensures it will be treated as inline markdown. Example:
*
* 0:\`\`\`annotation
* \{
* "type": "artifact",
* "data": \{...\}
* \}
* \`\`\`
*/
function toInlineAnnotationCode(item: any) {
return `\n\`\`\`${INLINE_ANNOTATION_KEY}\n${JSON.stringify(item)}\n\`\`\`\n`
}
+21 -20
View File
@@ -1,3 +1,15 @@
/**
* Basic Chat API Route Example
*
* This is a simple example demonstrating:
* - Text streaming with token-by-token delivery
* - Basic markdown content with code blocks
* - Custom annotations (weather) sent after text completion
* - Standard annotations (sources) sent after text completion
*
* Use this example as a starting point for implementing basic chat functionality
* with \@llamaindex/chat-ui components.
*/
import { NextResponse, type NextRequest } from 'next/server'
const TOKEN_DELAY = 30 // 30ms delay between tokens
@@ -40,26 +52,6 @@ console.log(c)
`
const SAMPLE_ANNOTATIONS = [
{
type: 'sources',
data: {
nodes: [
{ id: '1', url: '/sample.pdf' },
{ id: '2', url: '/sample.pdf' },
],
},
},
{
type: 'artifact',
data: {
type: 'code',
data: {
file_name: 'sample.ts',
language: 'typescript',
code: 'console.log("Hello, world!");',
},
},
},
{
type: 'weather',
data: {
@@ -70,6 +62,15 @@ const SAMPLE_ANNOTATIONS = [
windSpeed: 12,
},
},
{
type: 'sources',
data: {
nodes: [
{ id: '1', url: '/sample.pdf' },
{ id: '2', url: '/sample.pdf' },
],
},
},
]
const fakeChatStream = (query: string): ReadableStream => {
+15 -5
View File
@@ -9,7 +9,8 @@ import {
useChatUI,
} from '@llamaindex/chat-ui'
import { Message, useChat } from 'ai/react'
import { CustomWeatherAnnotation } from '../components/custom-weather-annotation'
import { WeatherAnnotation } from '../components/custom-weather'
import { WikiCard } from '@/components/custom-wiki'
const initialMessages: Message[] = [
{
@@ -39,7 +40,9 @@ export default function Page(): JSX.Element {
function ChatExample() {
const handler = useChat({
api: '/api/chat',
// api: '/api/chat',
// uncomment this to try advanced example in app/api/chat/advanced/route.ts
api: '/api/chat/advanced',
initialMessages,
})
@@ -86,10 +89,17 @@ function CustomChatMessages() {
</div>
</ChatMessage.Avatar>
<ChatMessage.Content isLoading={isLoading} append={append}>
<ChatMessage.Content.Markdown />
<ChatMessage.Content.Artifact />
<ChatMessage.Content.Markdown
annotationRenderers={{
// these annotations are rendered inline with the Markdown text
artifact: ChatCanvas.Artifact,
wiki: WikiCard,
}}
/>
{/* annotation components under the Markdown text */}
<WeatherAnnotation />
<ChatMessage.Content.Source />
<CustomWeatherAnnotation />
</ChatMessage.Content>
<ChatMessage.Actions />
</ChatMessage>
@@ -10,20 +10,32 @@ interface WeatherData {
windSpeed: number
}
export function CustomWeatherAnnotation() {
// A custom annotation component that is used to display weather information in a chat message
// The weather data is extracted from annotations in the message that has type 'weather'
export function WeatherAnnotation() {
const { message } = useChatMessage()
const weatherData = getAnnotationData<WeatherData>(message, 'weather')
if (weatherData.length === 0) return null
return <WeatherCard data={weatherData[0]} />
}
const data = weatherData[0]
function WeatherCard({ data }: { data: WeatherData }) {
const iconMap: Record<string, string> = {
sunny: '☀️',
cloudy: '☁️',
rainy: '🌧️',
snowy: '❄️',
stormy: '⛈️',
}
return (
<div className="my-4 rounded-lg border border-blue-200 bg-blue-50 p-4">
<div className="rounded-lg border border-blue-200 bg-blue-50 p-4">
<div className="flex items-center gap-3">
<div className="flex h-12 w-12 items-center justify-center rounded-full bg-blue-100">
<WeatherIcon condition={data.condition} />
<span className="text-2xl">
{iconMap[data.condition.toLowerCase()] || '🌤️'}
</span>
</div>
<div className="flex-1">
<h3 className="font-semibold text-blue-900">{data.location}</h3>
@@ -46,17 +58,3 @@ export function CustomWeatherAnnotation() {
</div>
)
}
function WeatherIcon({ condition }: { condition: string }) {
const iconMap: Record<string, string> = {
sunny: '☀️',
cloudy: '☁️',
rainy: '🌧️',
snowy: '❄️',
stormy: '⛈️',
}
return (
<span className="text-2xl">{iconMap[condition.toLowerCase()] || '🌤️'}</span>
)
}
@@ -0,0 +1,62 @@
'use client'
interface WikiData {
title: string
summary: string
url: string
category: string
lastUpdated: string
}
// A UI widget that displays wiki information, it can be used inline with markdown text
export function WikiCard({ data }: { data: WikiData }) {
const iconMap: Record<string, string> = {
science: '🧪',
history: '📜',
technology: '💻',
biology: '🧬',
geography: '🌍',
literature: '📚',
art: '🎨',
music: '🎵',
}
return (
<div className="my-4 rounded-lg border border-green-200 bg-green-50 p-4">
<div className="flex items-center gap-3">
<div className="flex h-12 w-12 items-center justify-center rounded-full bg-green-100">
<span className="text-2xl">
{iconMap[data.category.toLowerCase()] || '📖'}
</span>
</div>
<div className="flex-1">
<h3 className="my-0! font-semibold text-green-900">{data.title}</h3>
<div className="text-sm text-green-700">
<p className="mt-1 whitespace-pre-wrap">{data.summary}</p>
</div>
</div>
</div>
<div className="mt-3 grid grid-cols-2 gap-4 text-sm text-green-600">
<div className="flex items-center gap-2">
<span>📂 Category:</span>
<span className="font-medium">{data.category}</span>
</div>
<div className="flex items-center gap-2">
<span>📅 Updated:</span>
<span className="font-medium">{data.lastUpdated}</span>
</div>
</div>
<div className="mt-3">
<a
href={data.url}
target="_blank"
rel="noopener noreferrer"
className="inline-flex items-center gap-1 text-sm text-green-700 underline hover:text-green-900"
>
📖 Read full article
<span></span>
</a>
</div>
</div>
)
}
+3 -1
View File
@@ -81,10 +81,12 @@
"prosemirror-view": "^1.39.2",
"react-markdown": "^8.0.7",
"rehype-katex": "^7.0.0",
"remark": "^14.0.3",
"remark": "^15.0.1",
"remark-code-import": "^1.2.0",
"remark-gfm": "^3.0.1",
"remark-math": "^5.1.1",
"remark-parse": "^11.0.0",
"unist-util-visit": "^5.0.0",
"tailwind-merge": "^2.1.0",
"vaul": "^0.9.1"
},
@@ -1,37 +1,28 @@
import { Message } from '../chat.interface'
// TODO: add different methods to retrieve annotations from a message - e.g. by parsing its content for inline annotations
export type MessageAnnotation<T = unknown> = {
type: string
data: T
}
import { getInlineAnnotations } from './inline'
import { isMessageAnnotation, MessageAnnotation } from './types'
import { getVercelAnnotations } from './vercel'
/**
* Gets annotation data directly from a message by type
* Type for annotation parser functions
*/
type AnnotationParser = (message: Message) => unknown[]
/**
* Gets all annotation data from a message by type, combining results from multiple parsers
* @param message - The message to extract annotations from
* @param type - The annotation type to filter by (can be standard or custom)
* @returns Array of data from annotations of the specified type, or null if none found
* @param parsers - Array of parser functions to use (defaults to Vercel and inline parsers)
* @returns Array of data from annotations of the specified type from all parsers
*/
export function getAnnotationData<T = unknown>(
message: Message,
type: string
type: string,
parsers: AnnotationParser[] = [getVercelAnnotations, getInlineAnnotations]
): T[] {
const annotations = message.annotations
if (!annotations) return []
const allAnnotations = parsers
.flatMap(parser => parser(message))
.filter(a => isMessageAnnotation(a)) as MessageAnnotation<T>[]
const matchingAnnotations = annotations
.filter(
a =>
a &&
typeof a === 'object' &&
a !== null &&
'type' in a &&
a.type === type &&
'data' in a
)
.map(a => (a as { data: T }).data)
return matchingAnnotations
return allAnnotations.filter(a => a.type === type).map(a => a.data) as T[]
}
@@ -1,9 +0,0 @@
export enum MessageAnnotationType {
IMAGE = 'image',
DOCUMENT_FILE = 'document_file',
SOURCES = 'sources',
EVENTS = 'events',
SUGGESTED_QUESTIONS = 'suggested_questions',
AGENT_EVENTS = 'agent',
ARTIFACT = 'artifact',
}
@@ -1,2 +1,3 @@
export * from './annotations'
export * from './data'
export * from './types'
export * from './inline'
@@ -0,0 +1,84 @@
import { remark } from 'remark'
import remarkParse from 'remark-parse'
import { visit } from 'unist-util-visit'
import { Message } from '../chat.interface'
import { isMessageAnnotation, MessageAnnotation } from './types'
const INLINE_ANNOTATION_KEY = 'annotation'
// parse Markdown and extract code blocks
function parseMarkdownCodeBlocks(markdown: string) {
const markdownCodeBlocks: {
language: string | null
code: string
}[] = []
// Parse Markdown to AST using remark
const processor = remark().use(remarkParse)
const ast = processor.parse(markdown)
// Visit all code nodes in the AST
visit(ast, 'code', (node: any) => {
markdownCodeBlocks.push({
language: node.lang || null, // Language is stored in node.lang
code: node.value, // Code content is stored in node.value
})
})
return markdownCodeBlocks
}
// extract all inline annotations from markdown
export function getInlineAnnotations(message: Message): unknown[] {
const codeBlocks = parseMarkdownCodeBlocks(message.content)
return codeBlocks
.filter(block => block.language === INLINE_ANNOTATION_KEY)
.map(block => tryParse(block.code))
.filter(Boolean) // filter out null values
}
// convert annotation to inline markdown
export function toInlineAnnotation(annotation: MessageAnnotation) {
return `\`\`\`${INLINE_ANNOTATION_KEY}\n${JSON.stringify(annotation)}\n\`\`\``
}
/**
* Parses and validates an inline annotation from a code block
* @param language - The language identifier from the markdown code block
* @param codeValue - The raw code content from a markdown code block
* @returns The parsed annotation if valid, null if not an annotation or invalid
*/
export function parseInlineAnnotation(
language: string,
codeValue: string
): MessageAnnotation | null {
// Check if this is an inline annotation code block
if (language !== INLINE_ANNOTATION_KEY) {
return null
}
try {
const annotation = tryParse(codeValue)
if (annotation === null || !isMessageAnnotation(annotation)) {
console.warn(
`Invalid inline annotation: ${codeValue}, expected an object`
)
return null
}
return annotation
} catch (error) {
console.warn(`Failed to parse inline annotation: ${codeValue}`, error)
return null
}
}
// try to parse the code value as a JSON object and return null if it fails
function tryParse(codeValue: string) {
try {
return JSON.parse(codeValue)
} catch (error) {
return null
}
}
@@ -0,0 +1,26 @@
export enum MessageAnnotationType {
IMAGE = 'image',
DOCUMENT_FILE = 'document_file',
SOURCES = 'sources',
EVENTS = 'events',
SUGGESTED_QUESTIONS = 'suggested_questions',
AGENT_EVENTS = 'agent',
ARTIFACT = 'artifact',
}
export type MessageAnnotation<T = unknown> = {
type: string
data: T
}
export function isMessageAnnotation(
annotation: unknown
): annotation is MessageAnnotation {
return (
annotation !== null &&
typeof annotation === 'object' &&
'type' in annotation &&
'data' in annotation &&
typeof (annotation as any).type === 'string'
)
}
@@ -0,0 +1,11 @@
import { Message } from '../chat.interface'
/**
* Gets annotation data directly from a message by type
* @param message - The message to extract annotations from
* @param type - The annotation type to filter by (can be standard or custom)
* @returns Array of data from annotations of the specified type, or null if none found
*/
export function getVercelAnnotations(message: Message): unknown[] {
return message.annotations ?? []
}
@@ -1,6 +1,6 @@
import { Message } from '../chat.interface'
import { MessageAnnotationType } from '../annotations/data'
import { getAnnotationData } from '../annotations/annotations'
import { MessageAnnotationType, getAnnotationData } from '../annotations'
import { getInlineAnnotations } from '../annotations/inline'
// check if two artifacts are equal by comparing their type and created time
export function isEqualArtifact(a: Artifact, b: Artifact) {
@@ -15,7 +15,9 @@ export function extractArtifactsFromAllMessages(messages: Message[]) {
}
export function extractArtifactsFromMessage(message: Message): Artifact[] {
return getAnnotationData<Artifact>(message, MessageAnnotationType.ARTIFACT)
return getAnnotationData<Artifact>(message, MessageAnnotationType.ARTIFACT, [
getInlineAnnotations, // only extract artifacts from inline annotations
])
}
export type CodeArtifactError = {
+3 -9
View File
@@ -19,6 +19,7 @@ import {
} from './artifacts'
import { Message } from '../chat.interface'
import { useChatUI } from '../chat.context'
import { toInlineAnnotation } from '../annotations'
interface ChatCanvasContextType {
allArtifacts: Artifact[]
@@ -110,13 +111,7 @@ export function ChatCanvasProvider({ children }: { children: ReactNode }) {
{
id: `restore-success-${Date.now()}`,
role: 'assistant',
content: `Successfully restored to ${artifact.type} version ${getArtifactVersion(artifact).versionNumber}`,
annotations: [
{
type: 'artifact',
data: newArtifact,
},
],
content: `Successfully restored to ${artifact.type} version ${getArtifactVersion(artifact).versionNumber}\n${toInlineAnnotation({ type: 'artifact', data: newArtifact })}`,
},
] as (Message & { id: string })[]
@@ -164,8 +159,7 @@ export function ChatCanvasProvider({ children }: { children: ReactNode }) {
},
{
role: 'assistant',
content: `Updated content for ${artifact.type} artifact version ${getArtifactVersion(artifact).versionNumber}`,
annotations: [{ type: 'artifact', data: newArtifact }],
content: `Updated content for ${artifact.type} artifact version ${getArtifactVersion(artifact).versionNumber}\n${toInlineAnnotation({ type: 'artifact', data: newArtifact })}`,
},
] as (Message & { id: string })[]
@@ -5,6 +5,7 @@ import { ChatCanvasActions } from './actions'
import { CodeArtifactViewer } from './artifacts/code'
import { useChatCanvas } from './context'
import { DocumentArtifactViewer } from './artifacts/document'
import { ArtifactCard } from './artifact-card'
interface ChatCanvasProps {
children?: React.ReactNode
@@ -40,6 +41,7 @@ function ChatCanvas({ children, className }: ChatCanvasProps) {
ChatCanvas.CodeArtifact = CodeArtifactViewer
ChatCanvas.DocumentArtifact = DocumentArtifactViewer
ChatCanvas.Artifact = ArtifactCard
ChatCanvas.Actions = ChatCanvasActions
export default ChatCanvas
+8 -19
View File
@@ -1,4 +1,4 @@
import { useMemo } from 'react'
import { ComponentType } from 'react'
import {
ChatAgentEvents,
ChatEvents,
@@ -14,13 +14,12 @@ import {
SourceData,
SourceNode,
} from '../widgets/index.js' // this import needs the file extension as it's importing the widget bundle
import { MessageAnnotationType } from './annotations/data.js'
import { MessageAnnotationType } from './annotations/types.js'
import { getAnnotationData } from './annotations/annotations.js'
import { extractArtifactsFromMessage } from './canvas/artifacts.js'
import { useChatMessage } from './chat-message.context.js'
import { useChatUI } from './chat.context.js'
import { ArtifactCard } from './canvas/artifact-card.js'
import { Message } from './chat.interface.js'
import ChatCanvas from './canvas/index.js'
export function EventAnnotations() {
const { message, isLast, isLoading } = useChatMessage()
@@ -122,19 +121,9 @@ export function SuggestedQuestionsAnnotations() {
)
}
export function ArtifactAnnotations() {
const { message } = useChatMessage()
const artifacts = useMemo(
() => extractArtifactsFromMessage(message),
[message]
)
if (!artifacts?.length) return null
return (
<div className="flex items-center gap-2">
{artifacts.map((artifact, index) => (
<ArtifactCard key={index} data={artifact} />
))}
</div>
)
export const defaultAnnotationRenderers: Record<
string,
ComponentType<{ data: any }>
> = {
artifact: ChatCanvas.Artifact,
}
+5 -4
View File
@@ -10,7 +10,7 @@ import {
} from '../widgets/index.js'
import {
AgentEventAnnotations,
ArtifactAnnotations,
defaultAnnotationRenderers,
DocumentFileAnnotations,
EventAnnotations,
ImageAnnotations,
@@ -69,6 +69,7 @@ interface ChatMarkdownProps extends React.PropsWithChildren {
citationComponent?: ComponentType<CitationComponentProps>
className?: string
languageRenderers?: Record<string, ComponentType<LanguageRendererProps>>
annotationRenderers?: Record<string, ComponentType<{ data: any }>>
}
function ChatMessage(props: ChatMessageProps) {
@@ -125,7 +126,6 @@ function ChatMessageContent(props: ChatMessageContentProps) {
<DocumentFileAnnotations />
<SourceAnnotations />
<SuggestedQuestionsAnnotations />
<ArtifactAnnotations />
</>
)
@@ -147,6 +147,9 @@ function ChatMarkdown(props: ChatMarkdownProps) {
sources={{ nodes }}
citationComponent={props.citationComponent}
languageRenderers={props.languageRenderers}
annotationRenderers={
props.annotationRenderers ?? defaultAnnotationRenderers
}
className={cn(
{
'bg-primary text-primary-foreground ml-auto w-fit max-w-[80%] rounded-xl px-3 py-2':
@@ -211,7 +214,6 @@ type ComposibleChatMessageContent = typeof ChatMessageContent & {
DocumentFile: typeof DocumentFileAnnotations
Source: typeof SourceAnnotations
SuggestedQuestions: typeof SuggestedQuestionsAnnotations
Artifact: typeof ArtifactAnnotations
}
type ComposibleChatMessage = typeof ChatMessage & {
@@ -238,7 +240,6 @@ PrimiviteChatMessage.Content.Markdown = ChatMarkdown
PrimiviteChatMessage.Content.DocumentFile = DocumentFileAnnotations
PrimiviteChatMessage.Content.Source = SourceAnnotations
PrimiviteChatMessage.Content.SuggestedQuestions = SuggestedQuestionsAnnotations
PrimiviteChatMessage.Content.Artifact = ArtifactAnnotations
PrimiviteChatMessage.Avatar = ChatMessageAvatar
PrimiviteChatMessage.Actions = ChatMessageActions
+1
View File
@@ -7,6 +7,7 @@ export { default as ChatCanvas } from './chat/canvas'
export { default as ChatInput } from './chat/chat-input'
export { default as ChatMessages } from './chat/chat-messages'
export { default as ChatMessage, ContentPosition } from './chat/chat-message'
export { defaultAnnotationRenderers } from './chat/chat-annotations'
// Context Provider Hooks
export { useChatUI } from './chat/chat.context'
+27
View File
@@ -12,6 +12,7 @@ import {
import { SourceData } from './chat-sources'
import { Citation, CitationComponentProps } from './citation'
import { cn } from '../lib/utils'
import { parseInlineAnnotation } from '../chat/annotations/inline'
const MemoizedReactMarkdown: FC<Options> = memo(
ReactMarkdown,
@@ -85,6 +86,7 @@ export function Markdown({
citationComponent: CitationComponent,
className: customClassName,
languageRenderers,
annotationRenderers,
}: {
content: string
sources?: SourceData
@@ -92,6 +94,7 @@ export function Markdown({
citationComponent?: ComponentType<CitationComponentProps>
className?: string
languageRenderers?: Record<string, ComponentType<LanguageRendererProps>>
annotationRenderers?: Record<string, ComponentType<{ data: any }>>
}) {
const processedContent = preprocessContent(content)
@@ -123,6 +126,30 @@ export function Markdown({
const language = (match && match[1]) || ''
const codeValue = String(children).replace(/\n$/, '')
const annotation = parseInlineAnnotation(language, codeValue)
if (annotation) {
// Check if we have a specific renderer for it
if (annotationRenderers?.[annotation.type]) {
const CustomRenderer = annotationRenderers[annotation.type]
return (
<div className="my-4">
<CustomRenderer data={annotation.data} />
</div>
)
}
// If no custom renderer found, render an error message
return (
<div className="mb-2 max-w-full overflow-hidden rounded-md border border-red-200 bg-red-50 p-3 dark:border-red-800 dark:bg-red-900/20">
<div className="overflow-wrap-anywhere whitespace-pre-wrap break-words text-sm text-red-800 dark:text-red-200">
<strong>Annotation Render Error:</strong> No renderer found
for annotation type &ldquo;{annotation.type}&rdquo;.
</div>
</div>
)
}
if (inline) {
return (
<code className={className} {...props}>
+316 -72
View File
@@ -43,7 +43,7 @@ importers:
version: 1.1.11(@types/react-dom@18.3.7)(@types/react@18.3.21)(react-dom@18.3.1)(react@18.3.1)
ai:
specifier: 4.0.0
version: 4.0.0(react@18.3.1)(zod@3.25.32)
version: 4.0.0(react@18.3.1)(zod@3.25.42)
class-variance-authority:
specifier: ^0.7.0
version: 0.7.1
@@ -58,7 +58,7 @@ importers:
version: 11.11.1
llamaindex:
specifier: 0.9.3
version: 0.9.3(tree-sitter@0.22.4)(web-tree-sitter@0.24.7)(zod@3.25.32)
version: 0.9.3(tree-sitter@0.22.4)(web-tree-sitter@0.24.7)(zod@3.25.42)
lucide-react:
specifier: ^0.453.0
version: 0.453.0(react@18.3.1)
@@ -121,7 +121,7 @@ importers:
version: link:../../../packages/chat-ui
ai:
specifier: ^4.3.16
version: 4.3.16(react@19.1.0)(zod@3.25.32)
version: 4.3.16(react@19.1.0)(zod@3.25.42)
next:
specifier: ^15.3.2
version: 15.3.2(react-dom@19.1.0)(react@19.1.0)
@@ -170,7 +170,7 @@ importers:
version: link:../../packages/chat-ui
ai:
specifier: ^4.3.16
version: 4.3.16(react@19.1.0)(zod@3.25.32)
version: 4.3.16(react@19.1.0)(zod@3.25.42)
next:
specifier: ^15.3.2
version: 15.3.2(react-dom@19.1.0)(react@19.1.0)
@@ -311,8 +311,8 @@ importers:
specifier: ^7.0.0
version: 7.0.1
remark:
specifier: ^14.0.3
version: 14.0.3
specifier: ^15.0.1
version: 15.0.1
remark-code-import:
specifier: ^1.2.0
version: 1.2.0
@@ -322,9 +322,15 @@ importers:
remark-math:
specifier: ^5.1.1
version: 5.1.1
remark-parse:
specifier: ^11.0.0
version: 11.0.0
tailwind-merge:
specifier: ^2.1.0
version: 2.6.0
unist-util-visit:
specifier: ^5.0.0
version: 5.0.0
vaul:
specifier: ^0.9.1
version: 0.9.9(@types/react@18.3.21)(react-dom@18.3.1)(react@18.3.1)
@@ -370,7 +376,7 @@ importers:
packages:
/@ai-sdk/provider-utils@2.0.0(zod@3.25.32):
/@ai-sdk/provider-utils@2.0.0(zod@3.25.42):
resolution: {integrity: sha512-uITgVJByhtzuQU2ZW+2CidWRmQqTUTp6KADevy+4aRnmILZxY2LCt+UZ/ZtjJqq0MffwkuQPPY21ExmFAQ6kKA==}
engines: {node: '>=18'}
peerDependencies:
@@ -383,10 +389,10 @@ packages:
eventsource-parser: 3.0.1
nanoid: 5.1.5
secure-json-parse: 2.7.0
zod: 3.25.32
zod: 3.25.42
dev: false
/@ai-sdk/provider-utils@2.2.8(zod@3.25.32):
/@ai-sdk/provider-utils@2.2.8(zod@3.25.42):
resolution: {integrity: sha512-fqhG+4sCVv8x7nFzYnFo19ryhAa3w096Kmc3hWxMQfW/TubPOmt3A6tYZhl4mUfQWWQMsuSkLrtjlWuXBVSGQA==}
engines: {node: '>=18'}
peerDependencies:
@@ -395,7 +401,7 @@ packages:
'@ai-sdk/provider': 1.1.3
nanoid: 3.3.11
secure-json-parse: 2.7.0
zod: 3.25.32
zod: 3.25.42
dev: false
/@ai-sdk/provider@1.0.0:
@@ -412,7 +418,7 @@ packages:
json-schema: 0.4.0
dev: false
/@ai-sdk/react@1.0.0(react@18.3.1)(zod@3.25.32):
/@ai-sdk/react@1.0.0(react@18.3.1)(zod@3.25.42):
resolution: {integrity: sha512-BDrZqQA07Btg64JCuhFvBgYV+tt2B8cXINzEqWknGoxqcwgdE8wSLG2gkXoLzyC2Rnj7oj0HHpOhLUxDCmoKZg==}
engines: {node: '>=18'}
peerDependencies:
@@ -424,15 +430,15 @@ packages:
zod:
optional: true
dependencies:
'@ai-sdk/provider-utils': 2.0.0(zod@3.25.32)
'@ai-sdk/ui-utils': 1.0.0(zod@3.25.32)
'@ai-sdk/provider-utils': 2.0.0(zod@3.25.42)
'@ai-sdk/ui-utils': 1.0.0(zod@3.25.42)
react: 18.3.1
swr: 2.3.3(react@18.3.1)
throttleit: 2.1.0
zod: 3.25.32
zod: 3.25.42
dev: false
/@ai-sdk/react@1.2.12(react@19.1.0)(zod@3.25.32):
/@ai-sdk/react@1.2.12(react@19.1.0)(zod@3.25.42):
resolution: {integrity: sha512-jK1IZZ22evPZoQW3vlkZ7wvjYGYF+tRBKXtrcolduIkQ/m/sOAVcVeVDUDvh1T91xCnWCdUGCPZg2avZ90mv3g==}
engines: {node: '>=18'}
peerDependencies:
@@ -442,15 +448,15 @@ packages:
zod:
optional: true
dependencies:
'@ai-sdk/provider-utils': 2.2.8(zod@3.25.32)
'@ai-sdk/ui-utils': 1.2.11(zod@3.25.32)
'@ai-sdk/provider-utils': 2.2.8(zod@3.25.42)
'@ai-sdk/ui-utils': 1.2.11(zod@3.25.42)
react: 19.1.0
swr: 2.3.3(react@19.1.0)
throttleit: 2.1.0
zod: 3.25.32
zod: 3.25.42
dev: false
/@ai-sdk/ui-utils@1.0.0(zod@3.25.32):
/@ai-sdk/ui-utils@1.0.0(zod@3.25.42):
resolution: {integrity: sha512-oXBDIM/0niWeTWyw77RVl505dNxBUDLLple7bTsqo2d3i1UKwGlzBUX8XqZsh7GbY7I6V05nlG0Y8iGlWxv1Aw==}
engines: {node: '>=18'}
peerDependencies:
@@ -460,21 +466,21 @@ packages:
optional: true
dependencies:
'@ai-sdk/provider': 1.0.0
'@ai-sdk/provider-utils': 2.0.0(zod@3.25.32)
zod: 3.25.32
zod-to-json-schema: 3.24.5(zod@3.25.32)
'@ai-sdk/provider-utils': 2.0.0(zod@3.25.42)
zod: 3.25.42
zod-to-json-schema: 3.24.5(zod@3.25.42)
dev: false
/@ai-sdk/ui-utils@1.2.11(zod@3.25.32):
/@ai-sdk/ui-utils@1.2.11(zod@3.25.42):
resolution: {integrity: sha512-3zcwCc8ezzFlwp3ZD15wAPjf2Au4s3vAbKsXQVyhxODHcmu0iyPO2Eua6D/vicq/AUm/BAo60r97O6HU+EI0+w==}
engines: {node: '>=18'}
peerDependencies:
zod: ^3.23.8
dependencies:
'@ai-sdk/provider': 1.1.3
'@ai-sdk/provider-utils': 2.2.8(zod@3.25.32)
zod: 3.25.32
zod-to-json-schema: 3.24.5(zod@3.25.32)
'@ai-sdk/provider-utils': 2.2.8(zod@3.25.42)
zod: 3.25.42
zod-to-json-schema: 3.24.5(zod@3.25.42)
dev: false
/@alloc/quick-lru@5.2.0:
@@ -1616,8 +1622,8 @@ packages:
'@llamaindex/env': 0.1.28(gpt-tokenizer@2.9.0)
'@types/node': 22.15.23
magic-bytes.js: 1.12.1
zod: 3.24.4
zod-to-json-schema: 3.24.5(zod@3.24.4)
zod: 3.25.42
zod-to-json-schema: 3.24.5(zod@3.25.42)
transitivePeerDependencies:
- '@aws-crypto/sha256-js'
- '@huggingface/transformers'
@@ -1666,12 +1672,12 @@ packages:
web-tree-sitter: 0.24.7
dev: false
/@llamaindex/openai@0.1.54(gpt-tokenizer@2.9.0)(zod@3.25.32):
/@llamaindex/openai@0.1.54(gpt-tokenizer@2.9.0)(zod@3.25.42):
resolution: {integrity: sha512-C1r5PleKcIihiDQ9mCCVVBwmub/hT4o8Yi97U9KNR2u0oSOVXfuYWuxx9ViYiiYG4GnnwO5DqmEGzlC32TR5oA==}
dependencies:
'@llamaindex/core': 0.5.2(gpt-tokenizer@2.9.0)
'@llamaindex/env': 0.1.28(gpt-tokenizer@2.9.0)
openai: 4.98.0(zod@3.25.32)
openai: 4.98.0(zod@3.25.42)
transitivePeerDependencies:
- '@aws-crypto/sha256-js'
- '@huggingface/transformers'
@@ -3411,6 +3417,12 @@ packages:
'@types/unist': 2.0.11
dev: false
/@types/mdast@4.0.4:
resolution: {integrity: sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==}
dependencies:
'@types/unist': 3.0.3
dev: false
/@types/mdurl@2.0.0:
resolution: {integrity: sha512-RGdgjQUZba5p6QEFAVx2OGb8rQDL/cPRG7GiedRzMcJ1tYnUANBncjbSB1NRGwbvjcPeikRABz2nshyPk1bhWg==}
dev: false
@@ -3983,7 +3995,7 @@ packages:
humanize-ms: 1.2.1
dev: false
/ai@4.0.0(react@18.3.1)(zod@3.25.32):
/ai@4.0.0(react@18.3.1)(zod@3.25.42):
resolution: {integrity: sha512-cqf2GCaXnOPhUU+Ccq6i+5I0jDjnFkzfq7t6mc0SUSibSa1wDPn5J4p8+Joh2fDGDYZOJ44rpTW9hSs40rXNAw==}
engines: {node: '>=18'}
peerDependencies:
@@ -3996,17 +4008,17 @@ packages:
optional: true
dependencies:
'@ai-sdk/provider': 1.0.0
'@ai-sdk/provider-utils': 2.0.0(zod@3.25.32)
'@ai-sdk/react': 1.0.0(react@18.3.1)(zod@3.25.32)
'@ai-sdk/ui-utils': 1.0.0(zod@3.25.32)
'@ai-sdk/provider-utils': 2.0.0(zod@3.25.42)
'@ai-sdk/react': 1.0.0(react@18.3.1)(zod@3.25.42)
'@ai-sdk/ui-utils': 1.0.0(zod@3.25.42)
'@opentelemetry/api': 1.9.0
jsondiffpatch: 0.6.0
react: 18.3.1
zod: 3.25.32
zod-to-json-schema: 3.24.5(zod@3.25.32)
zod: 3.25.42
zod-to-json-schema: 3.24.5(zod@3.25.42)
dev: false
/ai@4.3.16(react@19.1.0)(zod@3.25.32):
/ai@4.3.16(react@19.1.0)(zod@3.25.42):
resolution: {integrity: sha512-KUDwlThJ5tr2Vw0A1ZkbDKNME3wzWhuVfAOwIvFUzl1TPVDFAXDFTXio3p+jaKneB+dKNCvFFlolYmmgHttG1g==}
engines: {node: '>=18'}
peerDependencies:
@@ -4017,13 +4029,13 @@ packages:
optional: true
dependencies:
'@ai-sdk/provider': 1.1.3
'@ai-sdk/provider-utils': 2.2.8(zod@3.25.32)
'@ai-sdk/react': 1.2.12(react@19.1.0)(zod@3.25.32)
'@ai-sdk/ui-utils': 1.2.11(zod@3.25.32)
'@ai-sdk/provider-utils': 2.2.8(zod@3.25.42)
'@ai-sdk/react': 1.2.12(react@19.1.0)(zod@3.25.42)
'@ai-sdk/ui-utils': 1.2.11(zod@3.25.42)
'@opentelemetry/api': 1.9.0
jsondiffpatch: 0.6.0
react: 19.1.0
zod: 3.25.32
zod: 3.25.42
dev: false
/ajv@6.12.6:
@@ -7087,7 +7099,7 @@ packages:
wrap-ansi: 9.0.0
dev: true
/llamaindex@0.9.3(tree-sitter@0.22.4)(web-tree-sitter@0.24.7)(zod@3.25.32):
/llamaindex@0.9.3(tree-sitter@0.22.4)(web-tree-sitter@0.24.7)(zod@3.25.42):
resolution: {integrity: sha512-NgMD2eddBxwvrhGsHsma7XxfEda015u4IMzWzUCq94ZnlVfXaRNMe/hWUOkief+huvUV3EkTTSWpSrXhZyiriQ==}
engines: {node: '>=18.0.0'}
dependencies:
@@ -7095,7 +7107,7 @@ packages:
'@llamaindex/core': 0.5.2(gpt-tokenizer@2.9.0)
'@llamaindex/env': 0.1.28(gpt-tokenizer@2.9.0)
'@llamaindex/node-parser': 1.0.2(@llamaindex/core@0.5.2)(@llamaindex/env@0.1.28)(tree-sitter@0.22.4)(web-tree-sitter@0.24.7)
'@llamaindex/openai': 0.1.54(gpt-tokenizer@2.9.0)(zod@3.25.32)
'@llamaindex/openai': 0.1.54(gpt-tokenizer@2.9.0)(zod@3.25.42)
'@types/lodash': 4.17.16
'@types/node': 22.15.17
ajv: 8.17.1
@@ -7284,6 +7296,25 @@ packages:
- supports-color
dev: false
/mdast-util-from-markdown@2.0.2:
resolution: {integrity: sha512-uZhTV/8NBuw0WHkPTrCqDOl0zVe1BIng5ZtHoDk49ME1qqcjYmmLmOf0gELgcRMxN4w2iuIeVso5/6QymSrgmA==}
dependencies:
'@types/mdast': 4.0.4
'@types/unist': 3.0.3
decode-named-character-reference: 1.1.0
devlop: 1.1.0
mdast-util-to-string: 4.0.0
micromark: 4.0.2
micromark-util-decode-numeric-character-reference: 2.0.2
micromark-util-decode-string: 2.0.1
micromark-util-normalize-identifier: 2.0.1
micromark-util-symbol: 2.0.1
micromark-util-types: 2.0.2
unist-util-stringify-position: 4.0.0
transitivePeerDependencies:
- supports-color
dev: false
/mdast-util-gfm-autolink-literal@1.0.3:
resolution: {integrity: sha512-My8KJ57FYEy2W2LyNom4n3E7hKTuQk/0SES0u16tjA9Z3oFkF4RrC/hPAPgjlSpezsOvI8ObcXcElo92wn5IGA==}
dependencies:
@@ -7355,6 +7386,13 @@ packages:
unist-util-is: 5.2.1
dev: false
/mdast-util-phrasing@4.1.0:
resolution: {integrity: sha512-TqICwyvJJpBwvGAMZjj4J2n0X8QWp21b9l0o7eXyVJ25YNWYbJDVIyD1bZXE6WtV6RmKJVYmQAKWa0zWOABz2w==}
dependencies:
'@types/mdast': 4.0.4
unist-util-is: 6.0.0
dev: false
/mdast-util-to-hast@12.3.0:
resolution: {integrity: sha512-pits93r8PhnIoU4Vy9bjW39M2jJ6/tdHyja9rrot9uujkN7UTU9SDnE6WNJz/IGyQk3XHX6yNNtrBH6cQzm8Hw==}
dependencies:
@@ -7381,12 +7419,32 @@ packages:
zwitch: 2.0.4
dev: false
/mdast-util-to-markdown@2.1.2:
resolution: {integrity: sha512-xj68wMTvGXVOKonmog6LwyJKrYXZPvlwabaryTjLh9LuvovB/KAH+kvi8Gjj+7rJjsFi23nkUxRQv1KqSroMqA==}
dependencies:
'@types/mdast': 4.0.4
'@types/unist': 3.0.3
longest-streak: 3.1.0
mdast-util-phrasing: 4.1.0
mdast-util-to-string: 4.0.0
micromark-util-classify-character: 2.0.1
micromark-util-decode-string: 2.0.1
unist-util-visit: 5.0.0
zwitch: 2.0.4
dev: false
/mdast-util-to-string@3.2.0:
resolution: {integrity: sha512-V4Zn/ncyN1QNSqSBxTrMOLpjr+IKdHl2v3KVLoWmDPscP4r9GcCi71gjgvUV1SFSKh92AjAG4peFuBl2/YgCJg==}
dependencies:
'@types/mdast': 3.0.15
dev: false
/mdast-util-to-string@4.0.0:
resolution: {integrity: sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==}
dependencies:
'@types/mdast': 4.0.4
dev: false
/mdurl@2.0.0:
resolution: {integrity: sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==}
dev: false
@@ -7459,6 +7517,27 @@ packages:
uvu: 0.5.6
dev: false
/micromark-core-commonmark@2.0.3:
resolution: {integrity: sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg==}
dependencies:
decode-named-character-reference: 1.1.0
devlop: 1.1.0
micromark-factory-destination: 2.0.1
micromark-factory-label: 2.0.1
micromark-factory-space: 2.0.1
micromark-factory-title: 2.0.1
micromark-factory-whitespace: 2.0.1
micromark-util-character: 2.1.1
micromark-util-chunked: 2.0.1
micromark-util-classify-character: 2.0.1
micromark-util-html-tag-name: 2.0.1
micromark-util-normalize-identifier: 2.0.1
micromark-util-resolve-all: 2.0.1
micromark-util-subtokenize: 2.1.0
micromark-util-symbol: 2.0.1
micromark-util-types: 2.0.2
dev: false
/micromark-extension-gfm-autolink-literal@1.0.5:
resolution: {integrity: sha512-z3wJSLrDf8kRDOh2qBtoTRD53vJ+CWIyo7uyZuxf/JAbNJjiHsOpG1y5wxk8drtv3ETAHutCu6N3thkOOgueWg==}
dependencies:
@@ -7551,6 +7630,14 @@ packages:
micromark-util-types: 1.1.0
dev: false
/micromark-factory-destination@2.0.1:
resolution: {integrity: sha512-Xe6rDdJlkmbFRExpTOmRj9N3MaWmbAgdpSrBQvCFqhezUn4AHqJHbaEnfbVYYiexVSs//tqOdY/DxhjdCiJnIA==}
dependencies:
micromark-util-character: 2.1.1
micromark-util-symbol: 2.0.1
micromark-util-types: 2.0.2
dev: false
/micromark-factory-label@1.1.0:
resolution: {integrity: sha512-OLtyez4vZo/1NjxGhcpDSbHQ+m0IIGnT8BoPamh+7jVlzLJBH98zzuCoUeMxvM6WsNeh8wx8cKvqLiPHEACn0w==}
dependencies:
@@ -7560,6 +7647,15 @@ packages:
uvu: 0.5.6
dev: false
/micromark-factory-label@2.0.1:
resolution: {integrity: sha512-VFMekyQExqIW7xIChcXn4ok29YE3rnuyveW3wZQWWqF4Nv9Wk5rgJ99KzPvHjkmPXF93FXIbBp6YdW3t71/7Vg==}
dependencies:
devlop: 1.1.0
micromark-util-character: 2.1.1
micromark-util-symbol: 2.0.1
micromark-util-types: 2.0.2
dev: false
/micromark-factory-space@1.1.0:
resolution: {integrity: sha512-cRzEj7c0OL4Mw2v6nwzttyOZe8XY/Z8G0rzmWQZTBi/jjwyw/U4uqKtUORXQrR5bAZZnbTI/feRV/R7hc4jQYQ==}
dependencies:
@@ -7567,6 +7663,13 @@ packages:
micromark-util-types: 1.1.0
dev: false
/micromark-factory-space@2.0.1:
resolution: {integrity: sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg==}
dependencies:
micromark-util-character: 2.1.1
micromark-util-types: 2.0.2
dev: false
/micromark-factory-title@1.1.0:
resolution: {integrity: sha512-J7n9R3vMmgjDOCY8NPw55jiyaQnH5kBdV2/UXCtZIpnHH3P6nHUKaH7XXEYuWwx/xUJcawa8plLBEjMPU24HzQ==}
dependencies:
@@ -7576,6 +7679,15 @@ packages:
micromark-util-types: 1.1.0
dev: false
/micromark-factory-title@2.0.1:
resolution: {integrity: sha512-5bZ+3CjhAd9eChYTHsjy6TGxpOFSKgKKJPJxr293jTbfry2KDoWkhBb6TcPVB4NmzaPhMs1Frm9AZH7OD4Cjzw==}
dependencies:
micromark-factory-space: 2.0.1
micromark-util-character: 2.1.1
micromark-util-symbol: 2.0.1
micromark-util-types: 2.0.2
dev: false
/micromark-factory-whitespace@1.1.0:
resolution: {integrity: sha512-v2WlmiymVSp5oMg+1Q0N1Lxmt6pMhIHD457whWM7/GUlEks1hI9xj5w3zbc4uuMKXGisksZk8DzP2UyGbGqNsQ==}
dependencies:
@@ -7585,6 +7697,15 @@ packages:
micromark-util-types: 1.1.0
dev: false
/micromark-factory-whitespace@2.0.1:
resolution: {integrity: sha512-Ob0nuZ3PKt/n0hORHyvoD9uZhr+Za8sFoP+OnMcnWK5lngSzALgQYKMr9RJVOWLqQYuyn6ulqGWSXdwf6F80lQ==}
dependencies:
micromark-factory-space: 2.0.1
micromark-util-character: 2.1.1
micromark-util-symbol: 2.0.1
micromark-util-types: 2.0.2
dev: false
/micromark-util-character@1.2.0:
resolution: {integrity: sha512-lXraTwcX3yH/vMDaFWCQJP1uIszLVebzUa3ZHdrgxr7KEU/9mL4mVgCpGbyhvNLNlauROiNUq7WN5u7ndbY6xg==}
dependencies:
@@ -7592,12 +7713,25 @@ packages:
micromark-util-types: 1.1.0
dev: false
/micromark-util-character@2.1.1:
resolution: {integrity: sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==}
dependencies:
micromark-util-symbol: 2.0.1
micromark-util-types: 2.0.2
dev: false
/micromark-util-chunked@1.1.0:
resolution: {integrity: sha512-Ye01HXpkZPNcV6FiyoW2fGZDUw4Yc7vT0E9Sad83+bEDiCJ1uXu0S3mr8WLpsz3HaG3x2q0HM6CTuPdcZcluFQ==}
dependencies:
micromark-util-symbol: 1.1.0
dev: false
/micromark-util-chunked@2.0.1:
resolution: {integrity: sha512-QUNFEOPELfmvv+4xiNg2sRYeS/P84pTW0TCgP5zc9FpXetHY0ab7SxKyAQCNCc1eK0459uoLI1y5oO5Vc1dbhA==}
dependencies:
micromark-util-symbol: 2.0.1
dev: false
/micromark-util-classify-character@1.1.0:
resolution: {integrity: sha512-SL0wLxtKSnklKSUplok1WQFoGhUdWYKggKUiqhX+Swala+BtptGCu5iPRc+xvzJ4PXE/hwM3FNXsfEVgoZsWbw==}
dependencies:
@@ -7606,6 +7740,14 @@ packages:
micromark-util-types: 1.1.0
dev: false
/micromark-util-classify-character@2.0.1:
resolution: {integrity: sha512-K0kHzM6afW/MbeWYWLjoHQv1sgg2Q9EccHEDzSkxiP/EaagNzCm7T/WMKZ3rjMbvIpvBiZgwR3dKMygtA4mG1Q==}
dependencies:
micromark-util-character: 2.1.1
micromark-util-symbol: 2.0.1
micromark-util-types: 2.0.2
dev: false
/micromark-util-combine-extensions@1.1.0:
resolution: {integrity: sha512-Q20sp4mfNf9yEqDL50WwuWZHUrCO4fEyeDCnMGmG5Pr0Cz15Uo7KBs6jq+dq0EgX4DPwwrh9m0X+zPV1ypFvUA==}
dependencies:
@@ -7613,12 +7755,25 @@ packages:
micromark-util-types: 1.1.0
dev: false
/micromark-util-combine-extensions@2.0.1:
resolution: {integrity: sha512-OnAnH8Ujmy59JcyZw8JSbK9cGpdVY44NKgSM7E9Eh7DiLS2E9RNQf0dONaGDzEG9yjEl5hcqeIsj4hfRkLH/Bg==}
dependencies:
micromark-util-chunked: 2.0.1
micromark-util-types: 2.0.2
dev: false
/micromark-util-decode-numeric-character-reference@1.1.0:
resolution: {integrity: sha512-m9V0ExGv0jB1OT21mrWcuf4QhP46pH1KkfWy9ZEezqHKAxkj4mPCy3nIH1rkbdMlChLHX531eOrymlwyZIf2iw==}
dependencies:
micromark-util-symbol: 1.1.0
dev: false
/micromark-util-decode-numeric-character-reference@2.0.2:
resolution: {integrity: sha512-ccUbYk6CwVdkmCQMyr64dXz42EfHGkPQlBj5p7YVGzq8I7CtjXZJrubAYezf7Rp+bjPseiROqe7G6foFd+lEuw==}
dependencies:
micromark-util-symbol: 2.0.1
dev: false
/micromark-util-decode-string@1.1.0:
resolution: {integrity: sha512-YphLGCK8gM1tG1bd54azwyrQRjCFcmgj2S2GoJDNnh4vYtnL38JS8M4gpxzOPNyHdNEpheyWXCTnnTDY3N+NVQ==}
dependencies:
@@ -7628,26 +7783,55 @@ packages:
micromark-util-symbol: 1.1.0
dev: false
/micromark-util-decode-string@2.0.1:
resolution: {integrity: sha512-nDV/77Fj6eH1ynwscYTOsbK7rR//Uj0bZXBwJZRfaLEJ1iGBR6kIfNmlNqaqJf649EP0F3NWNdeJi03elllNUQ==}
dependencies:
decode-named-character-reference: 1.1.0
micromark-util-character: 2.1.1
micromark-util-decode-numeric-character-reference: 2.0.2
micromark-util-symbol: 2.0.1
dev: false
/micromark-util-encode@1.1.0:
resolution: {integrity: sha512-EuEzTWSTAj9PA5GOAs992GzNh2dGQO52UvAbtSOMvXTxv3Criqb6IOzJUBCmEqrrXSblJIJBbFFv6zPxpreiJw==}
dev: false
/micromark-util-encode@2.0.1:
resolution: {integrity: sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==}
dev: false
/micromark-util-html-tag-name@1.2.0:
resolution: {integrity: sha512-VTQzcuQgFUD7yYztuQFKXT49KghjtETQ+Wv/zUjGSGBioZnkA4P1XXZPT1FHeJA6RwRXSF47yvJ1tsJdoxwO+Q==}
dev: false
/micromark-util-html-tag-name@2.0.1:
resolution: {integrity: sha512-2cNEiYDhCWKI+Gs9T0Tiysk136SnR13hhO8yW6BGNyhOC4qYFnwF1nKfD3HFAIXA5c45RrIG1ub11GiXeYd1xA==}
dev: false
/micromark-util-normalize-identifier@1.1.0:
resolution: {integrity: sha512-N+w5vhqrBihhjdpM8+5Xsxy71QWqGn7HYNUvch71iV2PM7+E3uWGox1Qp90loa1ephtCxG2ftRV/Conitc6P2Q==}
dependencies:
micromark-util-symbol: 1.1.0
dev: false
/micromark-util-normalize-identifier@2.0.1:
resolution: {integrity: sha512-sxPqmo70LyARJs0w2UclACPUUEqltCkJ6PhKdMIDuJ3gSf/Q+/GIe3WKl0Ijb/GyH9lOpUkRAO2wp0GVkLvS9Q==}
dependencies:
micromark-util-symbol: 2.0.1
dev: false
/micromark-util-resolve-all@1.1.0:
resolution: {integrity: sha512-b/G6BTMSg+bX+xVCshPTPyAu2tmA0E4X98NSR7eIbeC6ycCqCeE7wjfDIgzEbkzdEVJXRtOG4FbEm/uGbCRouA==}
dependencies:
micromark-util-types: 1.1.0
dev: false
/micromark-util-resolve-all@2.0.1:
resolution: {integrity: sha512-VdQyxFWFT2/FGJgwQnJYbe1jjQoNTS4RjglmSjTUlpUMa95Htx9NHeYW4rGDJzbjvCsl9eLjMQwGeElsqmzcHg==}
dependencies:
micromark-util-types: 2.0.2
dev: false
/micromark-util-sanitize-uri@1.2.0:
resolution: {integrity: sha512-QO4GXv0XZfWey4pYFndLUKEAktKkG5kZTdUNaTAkzbuJxn2tNBOr+QtxR2XpWaMhbImT2dPzyLrPXLlPhph34A==}
dependencies:
@@ -7656,6 +7840,14 @@ packages:
micromark-util-symbol: 1.1.0
dev: false
/micromark-util-sanitize-uri@2.0.1:
resolution: {integrity: sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==}
dependencies:
micromark-util-character: 2.1.1
micromark-util-encode: 2.0.1
micromark-util-symbol: 2.0.1
dev: false
/micromark-util-subtokenize@1.1.0:
resolution: {integrity: sha512-kUQHyzRoxvZO2PuLzMt2P/dwVsTiivCK8icYTeR+3WgbuPqfHgPPy7nFKbeqRivBvn/3N3GBiNC+JRTMSxEC7A==}
dependencies:
@@ -7665,14 +7857,31 @@ packages:
uvu: 0.5.6
dev: false
/micromark-util-subtokenize@2.1.0:
resolution: {integrity: sha512-XQLu552iSctvnEcgXw6+Sx75GflAPNED1qx7eBJ+wydBb2KCbRZe+NwvIEEMM83uml1+2WSXpBAcp9IUCgCYWA==}
dependencies:
devlop: 1.1.0
micromark-util-chunked: 2.0.1
micromark-util-symbol: 2.0.1
micromark-util-types: 2.0.2
dev: false
/micromark-util-symbol@1.1.0:
resolution: {integrity: sha512-uEjpEYY6KMs1g7QfJ2eX1SQEV+ZT4rUD3UcF6l57acZvLNK7PBZL+ty82Z1qhK1/yXIY4bdx04FKMgR0g4IAag==}
dev: false
/micromark-util-symbol@2.0.1:
resolution: {integrity: sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==}
dev: false
/micromark-util-types@1.1.0:
resolution: {integrity: sha512-ukRBgie8TIAcacscVHSiddHjO4k/q3pnedmzMQ4iwDcK0FtFCohKOlFbaOL/mPgfnPsL3C1ZyxJa4sbWrBl3jg==}
dev: false
/micromark-util-types@2.0.2:
resolution: {integrity: sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA==}
dev: false
/micromark@3.2.0:
resolution: {integrity: sha512-uD66tJj54JLYq0De10AhWycZWGQNUvDI55xPgk2sQM5kn1JYlhbCMTtEeT27+vAhW2FBQxLlOmS3pmA7/2z4aA==}
dependencies:
@@ -7697,6 +7906,30 @@ packages:
- supports-color
dev: false
/micromark@4.0.2:
resolution: {integrity: sha512-zpe98Q6kvavpCr1NPVSCMebCKfD7CA2NqZ+rykeNhONIJBpc1tFKt9hucLGwha3jNTNI8lHpctWJWoimVF4PfA==}
dependencies:
'@types/debug': 4.1.12
debug: 4.4.1
decode-named-character-reference: 1.1.0
devlop: 1.1.0
micromark-core-commonmark: 2.0.3
micromark-factory-space: 2.0.1
micromark-util-character: 2.1.1
micromark-util-chunked: 2.0.1
micromark-util-combine-extensions: 2.0.1
micromark-util-decode-numeric-character-reference: 2.0.2
micromark-util-encode: 2.0.1
micromark-util-normalize-identifier: 2.0.1
micromark-util-resolve-all: 2.0.1
micromark-util-sanitize-uri: 2.0.1
micromark-util-subtokenize: 2.1.0
micromark-util-symbol: 2.0.1
micromark-util-types: 2.0.2
transitivePeerDependencies:
- supports-color
dev: false
/micromatch@4.0.8:
resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==}
engines: {node: '>=8.6'}
@@ -8054,7 +8287,7 @@ packages:
mimic-function: 5.0.1
dev: true
/openai@4.98.0(zod@3.25.32):
/openai@4.98.0(zod@3.25.42):
resolution: {integrity: sha512-TmDKur1WjxxMPQAtLG5sgBSCJmX7ynTsGmewKzoDwl1fRxtbLOsiR0FA/AOAAtYUmP6azal+MYQuOENfdU+7yg==}
hasBin: true
peerDependencies:
@@ -8073,7 +8306,7 @@ packages:
form-data-encoder: 1.7.2
formdata-node: 4.4.1
node-fetch: 2.7.0
zod: 3.25.32
zod: 3.25.42
transitivePeerDependencies:
- encoding
dev: false
@@ -8959,6 +9192,17 @@ packages:
- supports-color
dev: false
/remark-parse@11.0.0:
resolution: {integrity: sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA==}
dependencies:
'@types/mdast': 4.0.4
mdast-util-from-markdown: 2.0.2
micromark-util-types: 2.0.2
unified: 11.0.5
transitivePeerDependencies:
- supports-color
dev: false
/remark-rehype@10.1.0:
resolution: {integrity: sha512-EFmR5zppdBp0WQeDVZ/b66CWJipB2q2VLNFMabzDSGR66Z2fQii83G5gTBbgGEnEEA0QRussvrFHxk1HWGJskw==}
dependencies:
@@ -8968,21 +9212,21 @@ packages:
unified: 10.1.2
dev: false
/remark-stringify@10.0.3:
resolution: {integrity: sha512-koyOzCMYoUHudypbj4XpnAKFbkddRMYZHwghnxd7ue5210WzGw6kOBwauJTRUMq16jsovXx8dYNvSSWP89kZ3A==}
/remark-stringify@11.0.0:
resolution: {integrity: sha512-1OSmLd3awB/t8qdoEOMazZkNsfVTeY4fTsgzcQFdXNq8ToTN4ZGwrMnlda4K6smTFKD+GRV6O48i6Z4iKgPPpw==}
dependencies:
'@types/mdast': 3.0.15
mdast-util-to-markdown: 1.5.0
unified: 10.1.2
'@types/mdast': 4.0.4
mdast-util-to-markdown: 2.1.2
unified: 11.0.5
dev: false
/remark@14.0.3:
resolution: {integrity: sha512-bfmJW1dmR2LvaMJuAnE88pZP9DktIFYXazkTfOIKZzi3Knk9lT0roItIA24ydOucI3bV/g/tXBA6hzqq3FV9Ew==}
/remark@15.0.1:
resolution: {integrity: sha512-Eht5w30ruCXgFmxVUSlNWQ9iiimq07URKeFS3hNc8cUWy1llX4KDWfyEDZRycMc+znsN9Ux5/tJ/BFdgdOwA3A==}
dependencies:
'@types/mdast': 3.0.15
remark-parse: 10.0.2
remark-stringify: 10.0.3
unified: 10.1.2
'@types/mdast': 4.0.4
remark-parse: 11.0.0
remark-stringify: 11.0.0
unified: 11.0.5
transitivePeerDependencies:
- supports-color
dev: false
@@ -10107,6 +10351,18 @@ packages:
vfile: 5.3.7
dev: false
/unified@11.0.5:
resolution: {integrity: sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA==}
dependencies:
'@types/unist': 3.0.3
bail: 2.0.2
devlop: 1.1.0
extend: 3.0.2
is-plain-obj: 4.1.0
trough: 2.2.0
vfile: 6.0.3
dev: false
/unist-util-find-after@5.0.0:
resolution: {integrity: sha512-amQa0Ep2m6hE2g72AugUItjbuM8X8cGQnFoHk0pGfrFeT9GZhzN5SW8nRsiGKK7Aif4CrACPENkA6P/Lw6fHGQ==}
dependencies:
@@ -10592,28 +10848,16 @@ packages:
engines: {node: '>=10'}
dev: true
/zod-to-json-schema@3.24.5(zod@3.24.4):
/zod-to-json-schema@3.24.5(zod@3.25.42):
resolution: {integrity: sha512-/AuWwMP+YqiPbsJx5D6TfgRTc4kTLjsh5SOcd4bLsfUg2RcEXrFMJl1DGgdHy2aCfsIA/cr/1JM0xcB2GZji8g==}
peerDependencies:
zod: ^3.24.1
dependencies:
zod: 3.24.4
zod: 3.25.42
dev: false
/zod-to-json-schema@3.24.5(zod@3.25.32):
resolution: {integrity: sha512-/AuWwMP+YqiPbsJx5D6TfgRTc4kTLjsh5SOcd4bLsfUg2RcEXrFMJl1DGgdHy2aCfsIA/cr/1JM0xcB2GZji8g==}
peerDependencies:
zod: ^3.24.1
dependencies:
zod: 3.25.32
dev: false
/zod@3.24.4:
resolution: {integrity: sha512-OdqJE9UDRPwWsrHjLN2F8bPxvwJBK22EHLWtanu0LSYr5YqzsaaW3RMgmjwr8Rypg5k+meEJdSPXJZXE/yqOMg==}
dev: false
/zod@3.25.32:
resolution: {integrity: sha512-OSm2xTIRfW8CV5/QKgngwmQW/8aPfGdaQFlrGoErlgg/Epm7cjb6K6VEyExfe65a3VybUOnu381edLb0dfJl0g==}
/zod@3.25.42:
resolution: {integrity: sha512-PcALTLskaucbeHc41tU/xfjfhcz8z0GdhhDcSgrCTmSazUuqnYqiXO63M0QUBVwpBlsLsNVn5qHSC5Dw3KZvaQ==}
dev: false
/zwitch@2.0.4: