feat: v0.1.1 迭代——人设外键重构、Chat UI优化、意识节点防幻觉、日志双视图

1. 人设外键重构:persona_template 成为 system_prompt 唯一权威来源,
   agent/系统节点通过 persona_id FK 引用,含数据迁移脚本
2. Chat UI:去掉底部AI提示、加号改为弹出菜单、新建对话乐观跳转
3. 意识节点:无可用worker时禁止编造agent_id,只能自行完成或拒绝
4. 日志页面:双tab布局(系统日志 + 工作流日志列表选择)
5. 其他:SSE流式聊天、对话删除/重命名、standalone模式修复

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
2026-06-05 06:18:47 +00:00
parent e3b8686d45
commit 6f1bc27101
39 changed files with 2904 additions and 524 deletions
+49 -13
View File
@@ -1,6 +1,6 @@
import { useState, useEffect } from 'react';
import { useTranslation } from 'react-i18next';
import { Plus, Trash2, MessageSquare, Workflow as WorkflowIcon } from 'lucide-react';
import { Plus, Trash2, MessageSquare, Workflow as WorkflowIcon, Pencil, Check } from 'lucide-react';
import apiClient from '../../api/client';
import type { Workflow } from '../../types';
import { useChatStore } from '../../store/useChatStore';
@@ -13,15 +13,17 @@ export function LeftPanel({ activeTab }: LeftPanelProps) {
const { t } = useTranslation();
const [workflows, setWorkflows] = useState<Workflow[]>([]);
const [loadingWorkflows, setLoadingWorkflows] = useState(false);
const [renamingId, setRenamingId] = useState<string | null>(null);
const [renameValue, setRenameValue] = useState('');
const {
sessions,
activeSessionId,
setActiveSessionId,
removeSession,
createChat,
selectedWorkflow,
setSelectedWorkflow,
updateSessionTitle,
} = useChatStore();
useEffect(() => {
@@ -57,8 +59,8 @@ export function LeftPanel({ activeTab }: LeftPanelProps) {
};
}, [activeTab]);
const handleNewChat = async () => {
await createChat(t('chat.newChat'), '你好');
const handleNewChat = () => {
setActiveSessionId(null);
};
const handleDeleteChat = (e: React.MouseEvent, id: string) => {
@@ -66,6 +68,20 @@ export function LeftPanel({ activeTab }: LeftPanelProps) {
removeSession(id);
};
const handleStartRename = (e: React.MouseEvent, id: string, title: string) => {
e.stopPropagation();
setRenamingId(id);
setRenameValue(title);
};
const handleConfirmRename = (e: React.MouseEvent) => {
e.stopPropagation();
if (renamingId && renameValue.trim()) {
updateSessionTitle(renamingId, renameValue.trim());
}
setRenamingId(null);
};
const isChats = activeTab === 'chats';
return (
@@ -95,6 +111,7 @@ export function LeftPanel({ activeTab }: LeftPanelProps) {
) : (
sessions.map((session) => {
const isActive = activeSessionId === session.id;
const isRenaming = renamingId === session.id;
return (
<div
key={session.id}
@@ -109,16 +126,35 @@ export function LeftPanel({ activeTab }: LeftPanelProps) {
<MessageSquare size={12} className={isActive ? 'text-accent' : 'text-text-muted'} />
</div>
<div className="flex-1 min-w-0">
<h3 className={`text-xs truncate ${isActive ? 'text-text-primary font-medium' : 'text-text-secondary'}`}>
{session.title}
</h3>
{isRenaming ? (
<input
autoFocus
value={renameValue}
onChange={(e) => setRenameValue(e.target.value)}
onClick={(e) => e.stopPropagation()}
onKeyDown={(e) => { if (e.key === 'Enter') handleConfirmRename(e as any); if (e.key === 'Escape') setRenamingId(null); }}
className="w-full text-xs bg-bg-input border border-border-primary rounded px-1.5 py-0.5 text-text-primary focus:outline-none focus:border-accent"
/>
) : (
<h3 className={`text-xs truncate ${isActive ? 'text-text-primary font-medium' : 'text-text-secondary'}`}>
{session.title}
</h3>
)}
</div>
<button
onClick={(e) => handleDeleteChat(e, session.id)}
className="opacity-0 group-hover:opacity-100 p-1 rounded text-text-muted hover:text-danger transition-all"
>
<Trash2 size={11} />
</button>
{isRenaming ? (
<button onClick={handleConfirmRename} className="p-1 rounded text-accent hover:bg-accent-light transition-all">
<Check size={11} />
</button>
) : (
<div className="flex items-center opacity-0 group-hover:opacity-100 transition-all">
<button onClick={(e) => handleStartRename(e, session.id, session.title)} className="p-1 rounded text-text-muted hover:text-accent transition-all">
<Pencil size={11} />
</button>
<button onClick={(e) => handleDeleteChat(e, session.id)} className="p-1 rounded text-text-muted hover:text-danger transition-all">
<Trash2 size={11} />
</button>
</div>
)}
</div>
);
})