Files
new-api/web/src/components/ai-elements/response-renderer.tsx
T
Calcium-Ion 31d70fca39 refactor(auth): replace dashboard sessions with stateless tokens and session control (#6329)
* refactor(auth): replace dashboard sessions with stateless tokens

* feat(auth): harden session issuance and distributed enforcement

* fix(proxy): preserve trusted proxy compatibility defaults

* refactor: address dashboard auth review feedback

* refactor: remove classic frontend and flatten web app
2026-07-20 16:48:43 +08:00

228 lines
5.9 KiB
TypeScript

/*
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 <https://www.gnu.org/licenses/>.
For commercial licensing, please contact support@quantumnous.com
*/
import type { ReactNode } from 'react'
import type { FootnoteNode, ParsedNode } from 'stream-markdown-parser'
import { getNodeKey } from './response-content'
import {
hasParsedChildren,
isBlockquoteNode,
isCodeBlockNode,
isDefinitionListNode,
isHeadingNode,
isHtmlBlockNode,
isImageNode,
isLinkNode,
isListNode,
isMathBlockNode,
isMathInlineNode,
isTableNode,
isTextNode,
} from './response-node-guards'
import { renderBlockquote } from './response-renderer-alert'
import {
renderCodeBlock,
renderDefinitionList,
renderHeading,
renderList,
renderMathBlock,
renderMathInline,
} from './response-renderer-blocks'
import { renderDetails } from './response-renderer-details'
import { renderFootnotes as renderFootnotesBlock } from './response-renderer-footnotes'
import {
renderImage,
renderLink,
renderTextNode,
} from './response-renderer-inline'
import { renderTable } from './response-renderer-table'
export function renderChildren(nodes: ParsedNode[]): ReactNode {
return nodes.map((node, index) => renderNode(node, getNodeKey(node, index)))
}
export function renderFootnotes(footnotes: FootnoteNode[]): ReactNode {
return renderFootnotesBlock(footnotes, { renderChildren })
}
function renderNode(node: ParsedNode, key: string): ReactNode {
if (isTextNode(node)) {
return renderTextNode(node)
}
if (isHeadingNode(node)) {
return renderHeading(node, key, { renderChildren })
}
if (node.type === 'paragraph' && hasParsedChildren(node)) {
return (
<p className='my-3 leading-7' key={key}>
{renderChildren(node.children)}
</p>
)
}
if (node.type === 'inline' && hasParsedChildren(node)) {
return <span key={key}>{renderChildren(node.children)}</span>
}
if (isListNode(node)) {
return renderList(node, key, { renderChildren })
}
if (isCodeBlockNode(node)) {
return renderCodeBlock(node, key)
}
if (node.type === 'inline_code' && 'code' in node) {
return (
<code
className='bg-muted/70 text-foreground rounded px-1 py-0.5 font-mono text-[0.9em]'
key={key}
>
{String(node.code)}
</code>
)
}
if (isLinkNode(node)) {
return renderLink(node, key, renderChildren)
}
if (isImageNode(node)) {
return renderImage(node, key)
}
if (isBlockquoteNode(node)) {
return renderBlockquote(node, key, { renderChildren })
}
if (isTableNode(node)) {
return renderTable(node, key, { renderChildren })
}
if (isDefinitionListNode(node)) {
return renderDefinitionList(node, key, { renderChildren })
}
if (node.type === 'strong' && hasParsedChildren(node)) {
return (
<strong className='text-foreground font-semibold' key={key}>
{renderChildren(node.children)}
</strong>
)
}
if (node.type === 'emphasis' && hasParsedChildren(node)) {
return <em key={key}>{renderChildren(node.children)}</em>
}
if (node.type === 'strikethrough' && hasParsedChildren(node)) {
return <del key={key}>{renderChildren(node.children)}</del>
}
if (node.type === 'highlight' && hasParsedChildren(node)) {
return <mark key={key}>{renderChildren(node.children)}</mark>
}
if (node.type === 'insert' && hasParsedChildren(node)) {
return <ins key={key}>{renderChildren(node.children)}</ins>
}
if (node.type === 'subscript' && hasParsedChildren(node)) {
return <sub key={key}>{renderChildren(node.children)}</sub>
}
if (node.type === 'superscript' && hasParsedChildren(node)) {
return <sup key={key}>{renderChildren(node.children)}</sup>
}
if (
(node.type === 'checkbox' || node.type === 'checkbox_input') &&
'checked' in node
) {
return (
<input
checked={Boolean(node.checked)}
className='accent-primary mr-2 size-4 align-[-0.15em]'
disabled
key={key}
readOnly
type='checkbox'
/>
)
}
if (node.type === 'hardbreak') {
return <br key={key} />
}
if (node.type === 'thematic_break') {
return <hr className='border-border/70 my-6' key={key} />
}
if (isMathBlockNode(node)) {
return renderMathBlock(node, key)
}
if (isMathInlineNode(node)) {
return renderMathInline(node, key)
}
if (node.type === 'footnote_reference' && 'id' in node) {
return (
<sup className='text-primary mx-0.5 text-xs' key={key}>
<a
className='underline-offset-2 hover:underline'
href={`#footnote-${String(node.id)}`}
id={`footnote-ref-${String(node.id)}`}
>
[{String(node.id)}]
</a>
</sup>
)
}
if (node.type === 'footnote_anchor') {
return null
}
if (isHtmlBlockNode(node) && node.tag === 'details') {
return renderDetails(node, key, { renderChildren })
}
if (node.type === 'html_block' && 'content' in node) {
return <span key={key}>{String(node.content)}</span>
}
if (node.type === 'html_inline' && 'content' in node) {
return <span key={key}>{String(node.content)}</span>
}
if (hasParsedChildren(node)) {
return <span key={key}>{renderChildren(node.children)}</span>
}
if ('content' in node && typeof node.content === 'string') {
return <span key={key}>{node.content}</span>
}
return <span key={key}>{node.raw}</span>
}