import { useMemo, type ReactNode } from 'react'
import { cn } from '../../lib/utils'
import { MarkdownBody } from '../markdown'
import { ReasoningStep } from '../reasoning-step'
import { StreamingMarkdownBlock } from '../streaming-markdown-block'
import { ImageGenerating as ImageGeneratingBase } from '../image-generating'
import { useResponseContext } from './context'
import type {
ResponseThinkingProps,
ResponseStepsProps,
ResponseStepProps,
ResponseContentProps,
ResponseImageGeneratingProps,
ResponseDefaultLayoutProps,
} from './types'
// ===== Response.Thinking =====
export function ResponseThinking({ className }: ResponseThinkingProps) {
const ctx = useResponseContext()
const effectiveShowThink = ctx.simulate
? ctx.showThink
: ctx.phase === 'thinking' || ctx.phase === 'streaming'
if (!effectiveShowThink) return null
return {ctx.thinkingIndicator}
}
ResponseThinking.displayName = 'ResponseThinking'
// ===== Response.Steps =====
export function ResponseSteps({ className, children }: ResponseStepsProps) {
return
{children}
}
ResponseSteps.displayName = 'ResponseSteps'
// ===== Response.Step =====
export function ResponseStepComponent({ step, status = 'completed' }: ResponseStepProps) {
return (
)
}
ResponseStepComponent.displayName = 'ResponseStep'
// ===== Response.Content =====
export function ResponseContent({
markdown,
streaming = false,
onStreamComplete,
className,
}: ResponseContentProps) {
const ctx = useResponseContext()
const content = markdown ?? ctx.finalMarkdown
if (streaming) {
return (
)
}
return (
{content}
)
}
ResponseContent.displayName = 'ResponseContent'
// ===== Response.ImageGenerating =====
export function ResponseImageGenerating({
width = '320px',
height = '240px',
className,
}: ResponseImageGeneratingProps) {
return
}
ResponseImageGenerating.displayName = 'ResponseImageGenerating'
// ===== Response.DefaultLayout =====
export function ResponseDefaultLayout({ className }: ResponseDefaultLayoutProps) {
const ctx = useResponseContext()
// 模拟模式:流式渲染 - useMemo 必须在条件语句前调用
const contentElements = useMemo(() => {
if (!ctx.simulate) return []
const el: ReactNode[] = []
const upTo = Math.min(ctx.visibleIndex, ctx.blocks.length - 1)
let lastVisibleComposerIndex = -1
for (let j = 0; j <= upTo; j++) {
if (ctx.blocks[j]?.type === 'composer') lastVisibleComposerIndex = j
}
for (let i = 0; i <= upTo; i++) {
const b = ctx.blocks[i]
if (!b) continue
if (b.type === 'think') {
if (i === ctx.visibleIndex) {
el.push({ctx.thinkingIndicator})
}
continue
}
if (b.type === 'composer' && b.step) {
const inProgress = i === lastVisibleComposerIndex && !ctx.isSimDone
el.push(
)
}
if (b.type === 'md' && b.md) {
el.push(
{
ctx.setVisibleIndex((v) => Math.min(v + 1, ctx.blocks.length))
if (i + 1 >= ctx.blocks.length) ctx.onSimulateComplete?.()
}}
/>
)
}
}
return el
}, [ctx])
// 静态内容元素(非模拟模式)
const staticElements = useMemo(() => {
if (ctx.simulate) return []
const staticEl: ReactNode[] = []
for (let i = 0; i < ctx.blocks.length; i++) {
const b = ctx.blocks[i]
if (!b) continue
if (b.type === 'composer' && b.step) {
staticEl.push(
)
}
if (b.type === 'md' && b.md) {
staticEl.push(
{b.md}
)
}
}
return staticEl
}, [ctx])
// imageGenerating 阶段
if (ctx.phase === 'imageGenerating') {
return (
)
}
// 非模拟模式:静态渲染
if (!ctx.simulate) {
return (
{staticElements}
{(ctx.phase === 'thinking' || ctx.phase === 'streaming') && ctx.thinkingIndicator}
)
}
// 模拟模式
return (
)
}
ResponseDefaultLayout.displayName = 'ResponseDefaultLayout'