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 && (
|
||||
|
||||
@@ -36,6 +36,8 @@ export function MessageList({ messages, isTyping }: MessageListProps) {
|
||||
timestamp={msg.timestamp}
|
||||
isStreaming={msg.isStreaming}
|
||||
attachments={msg.attachments}
|
||||
multiMessages={(msg as any).multiMessages}
|
||||
streamSegments={(msg as any).streamSegments}
|
||||
/>
|
||||
))}
|
||||
{isTyping && !messages.some((m) => m.isStreaming) && <TypingIndicator />}
|
||||
|
||||
@@ -306,6 +306,11 @@ function BriefingCard({ briefing }: { briefing: Briefing }) {
|
||||
<h4 className="text-xs font-semibold text-purple-600 dark:text-purple-400">
|
||||
昔涟的每日问候
|
||||
</h4>
|
||||
{briefing.summary_source === 'fallback' && (
|
||||
<span className="text-[10px] px-1.5 py-0.5 rounded-full bg-amber-100 text-amber-600 dark:bg-amber-900/30 dark:text-amber-400">
|
||||
本地生成
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
<p className="text-xs text-gray-600 dark:text-gray-400 leading-relaxed whitespace-pre-wrap">
|
||||
{briefing.summary}
|
||||
|
||||
Reference in New Issue
Block a user