feat(agents): 完成云智能体工作台与交互流程

This commit is contained in:
brother7 committed 2026-09-10 19:25:12 +08:00
1 parent 46fbae9dec
commit 64a3cf4934
26 files changed
+1955 -49

No files matched your search

+66
View File
@@ -0,0 +1,66 @@
import { useEffect, useRef, useState } from 'react';
import { Button } from '@/components/ui/button';
import { cloudAgentsApi } from '@/lib/cloud-agents-api';
import type { CloudAgentEntry, CloudThread } from '../../../shared/cloud-agents';
import { cloudStatus } from './CloudChat';
export function CloudOverview({ section, onAgent, onThread }: {
section: 'received' | 'activity'; onAgent: (entry: CloudAgentEntry) => void; onThread: (thread: CloudThread) => void;
}) {
const [agents, setAgents] = useState<CloudAgentEntry[]>([]);
const [threads, setThreads] = useState<CloudThread[]>([]);
const [error, setError] = useState('');
const [loading, setLoading] = useState(true);
const [offset, setOffset] = useState<number | null>(null);
const [refresh, setRefresh] = useState(0);
const paged = useRef(false);
useEffect(() => {
let live = true;
paged.current = false;
setLoading(true);
const load = async () => {
try {
if (section === 'received') {
const result = await cloudAgentsApi.call('received', {});
if (live) setAgents(result.agents);
} else {
const result = await cloudAgentsApi.call('threads', {});
if (live) { setThreads(result.threads); setOffset(result.next_offset); }
}
if (live) setError('');
} catch(e) { if (live) setError(e instanceof Error ? e.message : '暂时无法读取'); }
finally { if (live) setLoading(false); }
};
void load();
const timer = window.setInterval(() => { if (!paged.current) void load(); }, 15000);
return () => { live = false; window.clearInterval(timer); };
}, [section, refresh]);
const more = async () => {
if (offset === null) return;
paged.current = true;
setLoading(true);
try { const page = await cloudAgentsApi.call('threads', { offset }); setThreads(v => [...v, ...page.threads]); setOffset(page.next_offset); }
catch(e) { setError(e instanceof Error ? e.message : '加载失败'); } finally { setLoading(false); }
};
return <section className="space-y-4">
{error && <p role="alert" className="text-sm text-destructive">{error}<Button variant="ghost" onClick={() => setRefresh(v => v + 1)}>重新连接</Button></p>}
{section === 'received' ? <>
<p className="text-sm text-muted-foreground">这里显示其他创作者授权你使用的智能体。由创建者承担费用,你的会话和文件仅属于你。</p>
{!loading && !agents.length && <p className="py-12 text-center text-sm text-muted-foreground">还没有收到分享</p>}
<div className="grid gap-4 sm:grid-cols-2 lg:grid-cols-3">{agents.map(agent => <button key={agent.slug} onClick={() => onAgent(agent)}
className="rounded-xl border border-border p-5 text-left transition-colors hover:bg-muted/30 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring">
<h2 className="font-medium">{agent.name}</h2><p className="mt-2 line-clamp-3 text-sm leading-6 text-muted-foreground">{agent.purpose}</p>
<p className="mt-4 text-xs text-muted-foreground">版本 {agent.published_version} · 打开对话</p>
</button>)}</div></> : <>
<p className="text-sm text-muted-foreground">查看自己的对话、自动任务结果和待确认事项。</p>
{!loading && !threads.length && <p className="py-12 text-center text-sm text-muted-foreground">还没有运行记录</p>}
{threads.map(thread => <button key={thread.thread_id} onClick={() => onThread(thread)}
className="flex min-h-20 w-full items-center justify-between gap-4 rounded-xl border border-border p-4 text-left transition-colors hover:bg-muted/30 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring">
<div className="min-w-0"><h2 className="truncate text-sm font-medium">{thread.unread && <span className="mr-2 inline-block h-2 w-2 rounded-full bg-violet-500" aria-label="未读" />}{thread.title || '未命名对话'}</h2>
<p className="mt-2 text-xs text-muted-foreground">{thread.mode === 'preview' ? '草稿预览' : '正式运行'} · {new Date(thread.updated_at).toLocaleString()}</p></div>
<span className="shrink-0 text-sm text-muted-foreground">{cloudStatus(thread.status)}</span>
</button>)}
{offset !== null && <Button variant="outline" disabled={loading} onClick={() => void more()}>加载更早记录</Button>}
</>}
</section>;
}