初始化模版工程
This commit is contained in:
@@ -0,0 +1,233 @@
|
||||
import React from 'react'
|
||||
import { cn } from '@/utils/cn'
|
||||
import type { ImageAttachment, TaskArtifact } from '../../types'
|
||||
import { useNovaKit } from '../../context/useNovaKit'
|
||||
import { isImageFile } from '../utils'
|
||||
import { TaskArtifactHtml } from '@/components/html-editor'
|
||||
import { Html } from '@/components/html-editor/components/html-render/task-html'
|
||||
import PptPreview from '@/components/ppt-editor'
|
||||
import { ImageAttachmentItem } from '../../message-list/message-item/ImageAttachmentItem'
|
||||
import { UrlScriptPreview } from './UrlScriptPreview'
|
||||
import { ShellExecutePreview } from './ShellExecutePreview'
|
||||
import { MarkdownContent, MarkdownPreview } from './MarkdownPreview'
|
||||
import { CsvPreview } from './CsvPreview'
|
||||
import { VirtualPdfPreview } from './VirtualPdfPreview'
|
||||
import { isScriptLikeFile, normalizeArtifactFileType } from './previewUtils'
|
||||
|
||||
export interface ToolOutputArtifactPreviewProps {
|
||||
artifact: TaskArtifact
|
||||
className?: string
|
||||
}
|
||||
|
||||
const PREVIEW_FILE_TYPES = ['xlsx', 'xls', 'doc', 'docx']
|
||||
const TEXT_LIKE_FILE_TYPES = ['txt', 'text', 'json', 'log', 'xml', 'yaml', 'yml']
|
||||
|
||||
export function ToolOutputArtifactPreview({
|
||||
artifact,
|
||||
className,
|
||||
}: ToolOutputArtifactPreviewProps) {
|
||||
const { api, conversationId, mode } = useNovaKit()
|
||||
const [url, setUrl] = React.useState('')
|
||||
const [isUrlLoading, setIsUrlLoading] = React.useState(false)
|
||||
const editable = mode === 'chat'
|
||||
|
||||
const normalizedFileType = normalizeArtifactFileType(
|
||||
artifact.file_type,
|
||||
artifact.file_name,
|
||||
artifact.path,
|
||||
)
|
||||
|
||||
React.useEffect(() => {
|
||||
const directUrl = artifact.url || (/^https?:\/\//.test(artifact.path) ? artifact.path : '')
|
||||
|
||||
if (directUrl) {
|
||||
setUrl(directUrl)
|
||||
setIsUrlLoading(false)
|
||||
return
|
||||
}
|
||||
|
||||
if (artifact.path) {
|
||||
setIsUrlLoading(true)
|
||||
setUrl('')
|
||||
api
|
||||
.getArtifactUrl?.(
|
||||
artifact,
|
||||
PREVIEW_FILE_TYPES.includes(normalizedFileType)
|
||||
? {
|
||||
'x-oss-process': 'doc/preview,print_1,copy_1,export_1',
|
||||
}
|
||||
: undefined,
|
||||
)
|
||||
.then(res => {
|
||||
const originUrl = typeof res?.data === 'string' ? res.data : ''
|
||||
|
||||
if (PREVIEW_FILE_TYPES.includes(normalizedFileType)) {
|
||||
const shortUrl = originUrl.replace(
|
||||
'oss-cn-hangzhou.aliyuncs.com',
|
||||
'betteryeah.com',
|
||||
)
|
||||
setUrl(
|
||||
shortUrl
|
||||
? `${shortUrl}&x-oss-process=doc%2Fpreview%2Cprint_1%2Ccopy_1%2Cexport_1`
|
||||
: '',
|
||||
)
|
||||
} else {
|
||||
setUrl(originUrl)
|
||||
}
|
||||
setIsUrlLoading(false)
|
||||
})
|
||||
.catch(() => {
|
||||
setIsUrlLoading(false)
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
setIsUrlLoading(false)
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [artifact.path, artifact.url, normalizedFileType])
|
||||
|
||||
const isImage =
|
||||
isImageFile(artifact.path) ||
|
||||
isImageFile(artifact.file_name) ||
|
||||
['jpg', 'jpeg', 'png', 'gif', 'webp', 'svg', 'bmp'].includes(normalizedFileType)
|
||||
|
||||
if (isImage) {
|
||||
const imageAttachment: ImageAttachment = {
|
||||
url: artifact.url || '',
|
||||
path: artifact.path,
|
||||
file_name: artifact.file_name,
|
||||
file_url: artifact.url,
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={cn('flex h-full items-center justify-center p-6 bg-muted/10', className)}>
|
||||
<ImageAttachmentItem image={imageAttachment} />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
const isHtml =
|
||||
normalizedFileType === 'html' ||
|
||||
artifact.file_name?.toLowerCase().endsWith('.html')
|
||||
|
||||
if (isHtml && artifact.content && !artifact.path) {
|
||||
return <Html className={cn('h-full', className)} content={artifact.content} />
|
||||
}
|
||||
|
||||
if (isHtml) {
|
||||
return (
|
||||
<div className={cn('h-full', className)}>
|
||||
<TaskArtifactHtml
|
||||
taskId={conversationId || ''}
|
||||
taskArtifact={artifact}
|
||||
editable={editable}
|
||||
type="web"
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
const isMarkdown =
|
||||
normalizedFileType === 'md' ||
|
||||
normalizedFileType === 'markdown' ||
|
||||
artifact.file_name?.toLowerCase().endsWith('.md')
|
||||
|
||||
if (isMarkdown && url) {
|
||||
return <div className={cn('h-full', className)}><MarkdownPreview url={url} /></div>
|
||||
}
|
||||
|
||||
if (isMarkdown && artifact.content) {
|
||||
return (
|
||||
<div className={cn('h-full overflow-y-auto p-6', className)}>
|
||||
<MarkdownContent content={artifact.content} />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
const isPpt =
|
||||
normalizedFileType === 'ppt' ||
|
||||
normalizedFileType === 'pptx' ||
|
||||
artifact.file_name?.toLowerCase().endsWith('.ppt') ||
|
||||
artifact.file_name?.toLowerCase().endsWith('.pptx')
|
||||
|
||||
if (isPpt && url) {
|
||||
return (
|
||||
<div className={cn('h-full', className)}>
|
||||
<PptPreview
|
||||
url={url}
|
||||
artifact={artifact}
|
||||
taskId={conversationId || ''}
|
||||
editable={editable}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
const isCsv =
|
||||
normalizedFileType === 'csv' ||
|
||||
artifact.file_name?.toLowerCase().endsWith('.csv')
|
||||
|
||||
if (isCsv && artifact.content) {
|
||||
return <div className={cn('h-full', className)}><CsvPreview content={artifact.content} /></div>
|
||||
}
|
||||
|
||||
if (isCsv && url) {
|
||||
return <div className={cn('h-full', className)}><CsvPreview url={url} /></div>
|
||||
}
|
||||
|
||||
const isPdf =
|
||||
normalizedFileType === 'pdf' ||
|
||||
artifact.file_name?.toLowerCase().endsWith('.pdf')
|
||||
|
||||
if (isPdf && url) {
|
||||
return <div className={cn('h-full', className)}><VirtualPdfPreview url={url} /></div>
|
||||
}
|
||||
|
||||
const isScript = isScriptLikeFile(artifact)
|
||||
if (isScript && artifact.content) {
|
||||
return (
|
||||
<div className={cn('h-full', className)}>
|
||||
<ShellExecutePreview output={artifact.content} toolLabel={artifact.file_name} />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
const isTextLike = TEXT_LIKE_FILE_TYPES.includes(normalizedFileType)
|
||||
if (isTextLike && artifact.content) {
|
||||
return (
|
||||
<div className={cn('h-full', className)}>
|
||||
<ShellExecutePreview output={artifact.content} toolLabel={artifact.file_name} />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
if ((isScript || isTextLike) && url) {
|
||||
return <div className={cn('h-full', className)}><UrlScriptPreview url={url} title={artifact.file_name} /></div>
|
||||
}
|
||||
|
||||
if (url) {
|
||||
return (
|
||||
<iframe
|
||||
src={url}
|
||||
className={cn('w-full h-full border-0', className)}
|
||||
title={artifact.file_name}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
if (isUrlLoading) {
|
||||
return (
|
||||
<div className={cn('flex h-full items-center justify-center p-8 text-muted-foreground', className)}>
|
||||
<div className="text-sm">加载预览中...</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={cn('flex h-full items-center justify-center p-8 text-muted-foreground', className)}>
|
||||
<div className="text-sm">此文件类型暂不支持预览</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default ToolOutputArtifactPreview
|
||||
Reference in New Issue
Block a user