refactor: 清理 P1 类型债与 React key 债

- 消除 useChat.ts 中 13 处 (message as any).timestamp,改用已收窄变量的 .timestamp
- 给 ExecutionCancelled 接口补 timestamp?: number 字段,与后端对齐
- 修复 MessageInput 附件列表 key={index}: 给 FileAttachment 加 id 字段(crypto.randomUUID())
- 修复 ConfigPage TagEditor key={i} → key={t}(tag 值唯一)
- 修复 ConfigPage SourceEditor customPaths key={i} → key={p}(路径值唯一)
This commit is contained in:
oudecheng 2026-07-07 17:53:53 +08:00
parent 7eb2933ca5
commit f8d5f0253a
4 changed files with 21 additions and 17 deletions

View File

@ -16,6 +16,7 @@ interface MessageInputProps {
}
interface FileAttachment {
id: string
file: File
attachment: Attachment
preview?: string // 用于图片预览
@ -92,6 +93,7 @@ export function MessageInput({
}
const fileAttachment: FileAttachment = {
id: crypto.randomUUID(),
file,
attachment,
preview: mediaType === 'image' ? base64 : undefined,
@ -170,6 +172,7 @@ export function MessageInput({
}
const fileAttachment: FileAttachment = {
id: crypto.randomUUID(),
file,
attachment,
preview: mediaType === 'image' ? base64 : undefined,
@ -295,7 +298,7 @@ export function MessageInput({
<div className="mb-2 flex flex-wrap gap-2">
{attachments.map((att, index) => (
<div
key={index}
key={att.id}
className="flex items-center gap-2 rounded-lg border border-[var(--border-color)] bg-[var(--bg-tertiary)] px-2 py-1.5 text-sm"
>
{att.preview ? (

View File

@ -192,7 +192,7 @@ function TagEditor({ tags, onChange }: { tags: string[]; onChange: (t: string[])
<div className="space-y-2">
<div className="flex flex-wrap gap-1.5">
{tags.map((t, i) => (
<span key={i} className="inline-flex items-center gap-1 px-2 py-0.5 rounded-md bg-[var(--accent-cyan)]/10 border border-[var(--accent-cyan)]/20 text-xs text-[var(--accent-cyan)]">
<span key={t} className="inline-flex items-center gap-1 px-2 py-0.5 rounded-md bg-[var(--accent-cyan)]/10 border border-[var(--accent-cyan)]/20 text-xs text-[var(--accent-cyan)]">
{t}
<button onClick={() => onChange(tags.filter((_, j) => j !== i))} className="hover:text-white transition-colors"><X className="h-3 w-3" /></button>
</span>
@ -286,8 +286,8 @@ function SourceEditor({
<div className="text-xs font-medium text-[var(--text-muted)] uppercase tracking-wider"></div>
{customPaths.length > 0 && (
<div className="flex flex-wrap gap-1.5">
{customPaths.map((p, i) => (
<span key={i} className="inline-flex items-center gap-1 px-2 py-0.5 rounded-md bg-[var(--accent-cyan)]/10 border border-[var(--accent-cyan)]/20 text-xs text-[var(--accent-cyan)] font-mono">
{customPaths.map((p) => (
<span key={p} className="inline-flex items-center gap-1 px-2 py-0.5 rounded-md bg-[var(--accent-cyan)]/10 border border-[var(--accent-cyan)]/20 text-xs text-[var(--accent-cyan)] font-mono">
{p}
<button onClick={() => removeCustom(p)} className="hover:text-white transition-colors"><X className="h-3 w-3" /></button>
</span>

View File

@ -236,7 +236,7 @@ export function useChat(): UseChatReturn {
id: msg.id,
role: role as ChatMessage['role'],
content: msg.content,
timestamp: (message as any).timestamp ?? Math.floor(Date.now() / 1000),
timestamp: msg.timestamp ?? Math.floor(Date.now() / 1000),
type: 'message',
attachments: msg.attachments,
subagentTaskId: msg.subagent_task_id,
@ -249,7 +249,7 @@ export function useChat(): UseChatReturn {
id: msg.id,
role: 'tool',
content: msg.content,
timestamp: (message as any).timestamp ?? Math.floor(Date.now() / 1000),
timestamp: msg.timestamp ?? Math.floor(Date.now() / 1000),
type: 'tool_call',
toolName: msg.tool_name,
toolCallId: msg.tool_call_id,
@ -264,7 +264,7 @@ export function useChat(): UseChatReturn {
id: msg.id,
role: 'tool',
content: msg.content,
timestamp: (message as any).timestamp ?? Math.floor(Date.now() / 1000),
timestamp: msg.timestamp ?? Math.floor(Date.now() / 1000),
type: 'tool_result',
toolName: msg.tool_name,
toolCallId: msg.tool_call_id,
@ -278,7 +278,7 @@ export function useChat(): UseChatReturn {
id: msg.id,
role: 'tool',
content: `${msg.content}\n\n${msg.resume_hint}`,
timestamp: (message as any).timestamp ?? Math.floor(Date.now() / 1000),
timestamp: msg.timestamp ?? Math.floor(Date.now() / 1000),
type: 'tool_pending',
toolName: msg.tool_name,
toolCallId: msg.tool_call_id,
@ -302,7 +302,7 @@ export function useChat(): UseChatReturn {
id: generateMessageId(),
role: 'assistant',
content: `Error: ${message.message}`,
timestamp: (message as any).timestamp ?? Math.floor(Date.now() / 1000),
timestamp: message.timestamp ?? Math.floor(Date.now() / 1000),
type: 'message',
}
}
@ -362,7 +362,7 @@ export function useChat(): UseChatReturn {
id: generateMessageId(),
role: 'assistant',
content: `Error: ${errMsg.message}`,
timestamp: (message as any).timestamp ?? Math.floor(Date.now() / 1000),
timestamp: errMsg.timestamp ?? Math.floor(Date.now() / 1000),
type: 'message',
}
setSubAgentStack((prev) => {
@ -423,7 +423,7 @@ export function useChat(): UseChatReturn {
id: generateMessageId(),
role: 'assistant',
content: `Error: ${errMsg.message}`,
timestamp: (message as any).timestamp ?? Math.floor(Date.now() / 1000),
timestamp: errMsg.timestamp ?? Math.floor(Date.now() / 1000),
type: 'message',
}
const newStack = [...prev]
@ -800,7 +800,7 @@ export function useChat(): UseChatReturn {
id: msg.id,
role,
content: msg.content,
timestamp: (message as any).timestamp ?? Math.floor(Date.now() / 1000),
timestamp: msg.timestamp ?? Math.floor(Date.now() / 1000),
type: 'message',
attachments: msg.attachments,
reasoningContent: msg.reasoning_content,
@ -833,7 +833,7 @@ export function useChat(): UseChatReturn {
id: msg.id,
role: 'tool',
content: msg.content,
timestamp: (message as any).timestamp ?? Math.floor(Date.now() / 1000),
timestamp: msg.timestamp ?? Math.floor(Date.now() / 1000),
type: 'tool_call',
toolName: msg.tool_name,
toolCallId: msg.tool_call_id,
@ -856,7 +856,7 @@ export function useChat(): UseChatReturn {
id: msg.id,
role: 'tool',
content: msg.content,
timestamp: (message as any).timestamp ?? Math.floor(Date.now() / 1000),
timestamp: msg.timestamp ?? Math.floor(Date.now() / 1000),
type: 'tool_result',
toolName: msg.tool_name,
toolCallId: msg.tool_call_id,
@ -877,7 +877,7 @@ export function useChat(): UseChatReturn {
id: msg.id,
role: 'tool',
content: `${msg.content}\n\n${msg.resume_hint}`,
timestamp: (message as any).timestamp ?? Math.floor(Date.now() / 1000),
timestamp: msg.timestamp ?? Math.floor(Date.now() / 1000),
type: 'tool_pending',
toolName: msg.tool_name,
toolCallId: msg.tool_call_id,
@ -893,7 +893,7 @@ export function useChat(): UseChatReturn {
id: generateMessageId(),
role: 'assistant',
content: (message as { type: 'execution_cancelled'; message: string }).message,
timestamp: (message as any).timestamp ?? Math.floor(Date.now() / 1000),
timestamp: message.timestamp ?? Math.floor(Date.now() / 1000),
type: 'message',
},
])
@ -910,7 +910,7 @@ export function useChat(): UseChatReturn {
id: generateMessageId(),
role: 'assistant',
content: `Error: ${message.message}`,
timestamp: (message as any).timestamp ?? Math.floor(Date.now() / 1000),
timestamp: message.timestamp ?? Math.floor(Date.now() / 1000),
type: 'message',
},
])

View File

@ -260,6 +260,7 @@ export interface TaskMessagesLoaded {
export interface ExecutionCancelled {
type: 'execution_cancelled'
message: string
timestamp?: number
}
export interface StreamDelta {