fix: 修复19个Bug (P0-P3) — 持续性调试第7轮发现的问题
P0 (5): crypto/rand session ID, TTS fallback可达性, goroutine defer recover, adminAuth前缀修正 P1 (5): 普通用户密码验证, context传递, priority clamp, 超时重试, 自主思考速率限制 P2 (4): Briefing AI降级, 前端消息类型渲染, Docker Compose补全, PWA 192图标 P3 (5): goroutine错误处理, .gitignore完善, reminder created_at, voice Dockerfile, Go版本更新
This commit is contained in:
@@ -2,7 +2,7 @@ import { useState, useEffect, useRef, useCallback } from 'react';
|
||||
import { CyreneAvatar } from '@/components/persona/CyreneAvatar';
|
||||
import { useAuthStore } from '@/store/authStore';
|
||||
import { useSpeechSynthesis } from '@/hooks/useSpeechSynthesis';
|
||||
import type { MessageAttachment } from '@/types/chat';
|
||||
import type { MessageAttachment, MultiMessageItem, StreamSegment } from '@/types/chat';
|
||||
import { ImageLightbox } from './ImageLightbox';
|
||||
|
||||
interface MessageBubbleProps {
|
||||
@@ -11,6 +11,8 @@ interface MessageBubbleProps {
|
||||
timestamp: number;
|
||||
isStreaming?: boolean;
|
||||
attachments?: MessageAttachment[];
|
||||
multiMessages?: MultiMessageItem[];
|
||||
streamSegments?: StreamSegment[];
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -137,7 +139,7 @@ function AIMessageActions({ content }: { content: string }) {
|
||||
);
|
||||
}
|
||||
|
||||
export function MessageBubble({ role, content, timestamp, isStreaming, attachments }: MessageBubbleProps) {
|
||||
export function MessageBubble({ role, content, timestamp, isStreaming, attachments, multiMessages, streamSegments }: MessageBubbleProps) {
|
||||
const isUser = role === 'user';
|
||||
const [lightboxIndex, setLightboxIndex] = useState<number | null>(null);
|
||||
const time = new Date(timestamp).toLocaleTimeString('zh-CN', {
|
||||
@@ -172,13 +174,43 @@ export function MessageBubble({ role, content, timestamp, isStreaming, attachmen
|
||||
${isStreaming ? 'message-streaming' : ''}
|
||||
`}
|
||||
>
|
||||
<p className="whitespace-pre-wrap break-words">
|
||||
{isStreaming ? displayedContent : content}
|
||||
{/* 流式消息末尾闪烁光标 — 用独立 span 避免 ::after 在隐藏字符后错位 */}
|
||||
{hasMoreChars && (
|
||||
<span className="animate-streaming-cursor" />
|
||||
)}
|
||||
</p>
|
||||
|
||||
{/* 多段消息渲染 (multi_message 类型) */}
|
||||
{multiMessages && multiMessages.length > 0 && !isStreaming && (
|
||||
<div className="space-y-2">
|
||||
{multiMessages
|
||||
.sort((a, b) => a.index - b.index)
|
||||
.map((item) => (
|
||||
<p key={item.index} className="whitespace-pre-wrap break-words">
|
||||
{item.content}
|
||||
</p>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* 流式片段渲染 (stream_segments 类型) */}
|
||||
{streamSegments && streamSegments.length > 0 && !isStreaming && (
|
||||
<div className="space-y-1.5">
|
||||
{streamSegments
|
||||
.sort((a, b) => a.index - b.index)
|
||||
.map((seg) => (
|
||||
<p key={seg.index} className="whitespace-pre-wrap break-words text-gray-600 dark:text-gray-400">
|
||||
{seg.text}
|
||||
</p>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* 普通文本 / 流式文本 */}
|
||||
{(!multiMessages || multiMessages.length === 0) && (!streamSegments || streamSegments.length === 0) && (
|
||||
<p className="whitespace-pre-wrap break-words">
|
||||
{isStreaming ? displayedContent : content}
|
||||
{/* 流式消息末尾闪烁光标 — 用独立 span 避免 ::after 在隐藏字符后错位 */}
|
||||
{hasMoreChars && (
|
||||
<span className="animate-streaming-cursor" />
|
||||
)}
|
||||
</p>
|
||||
)}
|
||||
|
||||
{/* 图片附件网格 */}
|
||||
{!isStreaming && imageAttachments.length > 0 && (
|
||||
|
||||
Reference in New Issue
Block a user