Files
test1/components/nova-sdk/message-list/message-item/MessageFooter.tsx
2026-03-20 07:33:46 +00:00

106 lines
3.0 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import React, { useMemo } from 'react'
import { Copy } from 'lucide-react'
import { cn } from '@/utils/cn'
export interface MessageFooterProps {
shouldShowTimestamp: boolean
shouldShowCopyButton: boolean
timestamp?: string | number
isUserInput: boolean
isSummary: boolean
showSystemAttachments: boolean
showUserFile: boolean
onCopyMessage: () => void
}
function parseTimestamp(timestamp: string | number): Date {
if (typeof timestamp === 'number') return new Date(timestamp)
const num = Number(timestamp)
if (!isNaN(num)) return new Date(num)
// 截断超过3位的小数秒Python 微秒精度 → JS 毫秒精度)
const normalized = timestamp.replace(/(\.\d{3})\d+/, '$1')
return new Date(normalized)
}
function formatTimestamp(timestamp: string | number): string {
const date = parseTimestamp(timestamp)
const now = new Date()
const todayStart = new Date(now.getFullYear(), now.getMonth(), now.getDate())
const dateStart = new Date(date.getFullYear(), date.getMonth(), date.getDate())
const diffDays = Math.round(
(todayStart.getTime() - dateStart.getTime()) / (1000 * 60 * 60 * 24),
)
const hhmm = date.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit', hour12: false })
if (diffDays === 0) {
return hhmm
} else if (diffDays >= 1) {
return `${date.getMonth() + 1}/${date.getDate()} ${hhmm}`
} else if (now.getFullYear() === date.getFullYear()) {
return `${date.getMonth() + 1}/${date.getDate()}`
} else {
return `${date.getFullYear()}`
}
}
/**
* 消息底部 - 显示时间戳和复制按钮,对应 next-agent MessageFooter
*/
export const MessageFooter: React.FC<MessageFooterProps> = ({
shouldShowTimestamp,
shouldShowCopyButton,
timestamp,
isUserInput,
isSummary,
showSystemAttachments,
showUserFile,
onCopyMessage,
}) => {
// hooks 必须在任何 return 之前调用
const displayText = useMemo(
() => (timestamp != null ? formatTimestamp(timestamp) : ''),
[timestamp],
)
const fullTime = useMemo(() => {
if (timestamp == null) return ''
return parseTimestamp(timestamp).toLocaleString()
}, [timestamp])
if (!shouldShowTimestamp) return null
return (
<div
className={cn(
'opacity-0 group-hover:opacity-100 flex items-center bottom-1 gap-3',
{
'right-0 absolute': isUserInput,
'flex-row-reverse': isSummary,
'mt-2.5': isSummary && showSystemAttachments,
'px-3': showUserFile,
},
)}
>
<span
className="text-xs text-muted-foreground whitespace-nowrap"
title={fullTime}
>
{displayText}
</span>
{shouldShowCopyButton && (
<button
className="w-6 h-6 rounded flex items-center justify-center hover:bg-muted transition-colors"
onClick={onCopyMessage}
type="button"
>
<Copy className="w-3.5 h-3.5 text-muted-foreground cursor-pointer" />
</button>
)}
</div>
)
}
export default React.memo(MessageFooter)