/*
Copyright (C) 2023-2026 QuantumNous
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see .
For commercial licensing, please contact support@quantumnous.com
*/
import type { ReactNode } from 'react'
import type {
CodeBlockNode,
DefinitionItemNode,
DefinitionListNode,
HeadingNode,
ListNode,
MathBlockNode,
MathInlineNode,
} from 'stream-markdown-parser'
import {
CodeBlock,
CodeBlockCopyButton,
} from '@/components/ai-elements/code-block'
import { cn } from '@/lib/utils'
import { getNodeKey } from './response-content'
import type { BlockRendererOptions } from './response-types'
const headingClasses = {
1: 'mt-6 mb-3 text-xl font-semibold tracking-normal',
2: 'mt-6 mb-3 text-lg font-semibold tracking-normal',
3: 'mt-5 mb-2 text-base font-semibold tracking-normal',
4: 'mt-5 mb-2 text-sm font-semibold tracking-normal',
5: 'text-muted-foreground mt-4 mb-2 text-sm font-semibold tracking-normal',
6: 'text-muted-foreground mt-4 mb-2 text-xs font-semibold tracking-normal uppercase',
} satisfies Record<1 | 2 | 3 | 4 | 5 | 6, string>
export function renderHeading(
node: HeadingNode,
key: string,
options: BlockRendererOptions
): ReactNode {
const headingLevel = Math.min(Math.max(node.level, 1), 6) as
| 1
| 2
| 3
| 4
| 5
| 6
const className = headingClasses[headingLevel]
const children = options.renderChildren(node.children)
if (headingLevel === 1) {
return (
{children}
)
}
if (headingLevel === 2) {
return (
{children}
)
}
if (headingLevel === 3) {
return (
{children}
)
}
if (headingLevel === 4) {
return (
{children}
)
}
if (headingLevel === 5) {
return (
{children}
)
}
return (
{children}
)
}
export function renderList(
node: ListNode,
key: string,
options: BlockRendererOptions
): ReactNode {
const className = cn(
'my-3 list-outside space-y-1.5 pl-5',
node.ordered ? 'list-decimal' : 'list-disc'
)
const items = node.items.map((item, index) => (
{options.renderChildren(item.children)}
))
if (node.ordered) {
return (
{items}
)
}
return (
)
}
export function renderCodeBlock(node: CodeBlockNode, key: string): ReactNode {
const language = node.language || 'plaintext'
const lineCount = node.code.split('\n').length
return (
14}
key={key}
language={language}
maxExpandedLines={44}
showLineNumbers
showToolbar
title={language}
>
)
}
export function renderDefinitionList(
node: DefinitionListNode,
key: string,
options: BlockRendererOptions
): ReactNode {
return (
{node.items.map((item, index) =>
renderDefinitionItem(item, index, options)
)}
)
}
function renderDefinitionItem(
node: DefinitionItemNode,
index: number,
options: BlockRendererOptions
): ReactNode {
return (
{options.renderChildren(node.term)}
{options.renderChildren(node.definition)}
)
}
export function renderMathBlock(node: MathBlockNode, key: string): ReactNode {
return (
{node.content}
)
}
export function renderMathInline(node: MathInlineNode, key: string): ReactNode {
return (
{node.content}
)
}