feat(task-deeplink): 3qfx/xdfx 新建对话改手动「开始回答」+ xdfx 行动详情卡片 + 独立问答智能体列表栏
- AgentChatPage: 纯绑定深链(3qfx/xdfx)不再自动发开场信息,改由右上角「开始回答」按钮手动触发,从根上消除新建对话竞态导致的重复发送;移除 autoSentArmedRef/streamThreadId 相关逻辑 - task-deeplink.ts: 抽出 fetchActionDetail 返回整包 ActionDetail,fetchTaskIdFromAction 复用之 - TaskFxWorkspace: xdfx 左栏「当前任务」下新增「行动详情」卡片(行动名称/叙事) - StrategyRoutes/WorkspaceRoutes: 独立智能体问答整页新增最左「智能体列表」栏(showAgentList);xdfx 右侧宿主改用 TaskBindChat(精简版) - 新增 agent-chat-sidebar.tsx Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
179a3f2b2f
commit
6aaa4c841e
6 files changed
+298
-49
No files matched your search
@@ -0,0 +1,172 @@
|
||||
"use client";
|
||||
|
||||
import { Bot, Loader2 } from "lucide-react";
|
||||
import Link from "next/link";
|
||||
import { useEffect, useMemo, useState } from "react";
|
||||
|
||||
import { agentColor } from "@/components/workspace/agent-icon-color";
|
||||
import {
|
||||
Select,
|
||||
SelectContent,
|
||||
SelectItem,
|
||||
SelectTrigger,
|
||||
SelectValue,
|
||||
} from "@/components/ui/select";
|
||||
import { useTheme } from "@/contexts/ThemeContext";
|
||||
import { useAgentsPaginated } from "@/core/agents";
|
||||
import { useRouteBase } from "@/core/route-base";
|
||||
import { usePublishSquares } from "@/core/system-settings/hooks";
|
||||
import { desensitize } from "@/lib/desensitize";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
/** 「我的」作用域的下拉选项值;其余选项值为各发布广场(publish square)的 id。 */
|
||||
const MINE_SCOPE = "mine";
|
||||
|
||||
// 列表一次拉取的条数。超出时底部出现「查看全部」入口,跳到完整的智能体管理页,
|
||||
// 避免静默截断。侧边栏只做快速切换,不在此分页。
|
||||
const LIST_PAGE_SIZE = 50;
|
||||
|
||||
/**
|
||||
* 智能体问答页左侧「智能体列表」栏。
|
||||
*
|
||||
* - 顶部下拉:选「我的」(默认,= 我创建/收藏的)或任一「广场」(发布广场 publish square),
|
||||
* 与「智能体管理」页的主标签一致。
|
||||
* - 下方:所选作用域下的智能体列表;点击进入该智能体的新对话(保持当前路由命名空间)。
|
||||
* - 当前正在对话的智能体高亮。
|
||||
*
|
||||
* 仅在独立的智能体问答整页(/page/strategy|workspace/agents/:id/chats/:thread)展示;
|
||||
* 任务工作区 / zzJC·zzXD 模块 / 嵌入态不展示(它们各有自己的左栏)。
|
||||
*/
|
||||
export function AgentChatSidebar({ activeAgentId }: { activeAgentId?: string }) {
|
||||
const routeBase = useRouteBase();
|
||||
const { theme } = useTheme();
|
||||
const isDark = theme === "dark";
|
||||
|
||||
const { data: publishSquares } = usePublishSquares();
|
||||
// 每个发布广场对应一个下拉选项;后端至少返回一个内置默认广场。
|
||||
const squares = useMemo(
|
||||
() => publishSquares?.squares ?? [],
|
||||
[publishSquares?.squares],
|
||||
);
|
||||
|
||||
// 作用域:MINE_SCOPE 或某个广场 id。默认「我的」。
|
||||
const [scope, setScope] = useState<string>(MINE_SCOPE);
|
||||
|
||||
// 若所选广场被管理员删除,回退到「我的」,避免指向不存在的广场。
|
||||
useEffect(() => {
|
||||
if (
|
||||
scope !== MINE_SCOPE &&
|
||||
squares.length > 0 &&
|
||||
!squares.some((sq) => sq.id === scope)
|
||||
) {
|
||||
setScope(MINE_SCOPE);
|
||||
}
|
||||
}, [scope, squares]);
|
||||
|
||||
const isMine = scope === MINE_SCOPE;
|
||||
const { agents, total, isLoading, isFetching } = useAgentsPaginated({
|
||||
scope: isMine ? "mine" : "square",
|
||||
sort: "time_desc",
|
||||
page: 1,
|
||||
pageSize: LIST_PAGE_SIZE,
|
||||
square: isMine ? undefined : scope,
|
||||
});
|
||||
|
||||
return (
|
||||
// 配色与 AgentModuleSidebar 一致:浅色白底,深蓝主题半透明深蓝玻璃面板。
|
||||
<aside className="flex h-full w-(--sidebar-width) shrink-0 flex-col overflow-hidden border-r border-border/70 bg-white text-foreground backdrop-blur-md dark:border-[#2b8ffe]/45 dark:bg-[#032650]/80">
|
||||
{/* 顶部:作用域下拉(我的 / 各广场) */}
|
||||
<div className="shrink-0 border-b border-border/60 px-3 py-2.5">
|
||||
<Select value={scope} onValueChange={setScope}>
|
||||
<SelectTrigger className="h-9 w-full text-sm" title="切换智能体来源">
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent position="popper" side="bottom" sideOffset={4}>
|
||||
<SelectItem value={MINE_SCOPE}>我的</SelectItem>
|
||||
{squares.map((sq) => (
|
||||
<SelectItem key={sq.id} value={sq.id}>
|
||||
{sq.name}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
|
||||
{/* 智能体列表 */}
|
||||
<div className="flex min-h-0 flex-1 flex-col overflow-y-auto px-2 py-3">
|
||||
{isLoading && agents.length === 0 ? (
|
||||
<div className="text-muted-foreground flex h-24 items-center justify-center gap-2 text-xs">
|
||||
<Loader2 className="size-3.5 animate-spin" />
|
||||
加载中…
|
||||
</div>
|
||||
) : agents.length === 0 ? (
|
||||
<p className="px-3 py-1.5 text-xs text-muted-foreground/70">
|
||||
{isMine ? "暂无智能体" : "该广场暂无智能体"}
|
||||
</p>
|
||||
) : (
|
||||
<div
|
||||
className={cn(
|
||||
"flex flex-col gap-1 transition-opacity",
|
||||
isFetching && "opacity-60",
|
||||
)}
|
||||
>
|
||||
{agents.map((agent) => {
|
||||
const href = `${routeBase}/agents/${agent.id}/chats/new`;
|
||||
const isActive = agent.id === activeAgentId;
|
||||
const { from, to } = agentColor(agent.id, isDark);
|
||||
return (
|
||||
<Link
|
||||
key={agent.id}
|
||||
href={href}
|
||||
className={cn(
|
||||
"group relative flex w-full items-start gap-2.5 rounded-xl border px-2.5 py-2 transition-colors",
|
||||
isActive
|
||||
? "border-primary/40 bg-primary/5"
|
||||
: "border-transparent hover:border-border hover:bg-accent/50",
|
||||
)}
|
||||
>
|
||||
<span
|
||||
className="mt-0.5 flex size-9 shrink-0 items-center justify-center rounded-lg text-white shadow-sm"
|
||||
style={{
|
||||
backgroundImage: `linear-gradient(135deg, ${from}, ${to})`,
|
||||
}}
|
||||
>
|
||||
<Bot className="size-[18px]" />
|
||||
</span>
|
||||
<span className="min-w-0 flex-1">
|
||||
<span
|
||||
className={cn(
|
||||
"block truncate text-[13px] font-semibold",
|
||||
isActive ? "text-primary" : "text-foreground",
|
||||
)}
|
||||
>
|
||||
{desensitize(agent.name)}
|
||||
</span>
|
||||
<span className="mt-0.5 block truncate text-[11px] leading-snug text-muted-foreground">
|
||||
{agent.description
|
||||
? desensitize(agent.description)
|
||||
: "智能体助手"}
|
||||
</span>
|
||||
</span>
|
||||
{isActive && (
|
||||
<span className="absolute top-1/2 right-1 h-6 w-1 -translate-y-1/2 rounded-full bg-primary" />
|
||||
)}
|
||||
</Link>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* 底部:超出一页时引导到完整的智能体管理页(不在侧边栏静默截断) */}
|
||||
{total > agents.length && (
|
||||
<Link
|
||||
href={`${routeBase}/agents`}
|
||||
className="text-muted-foreground hover:text-foreground shrink-0 border-t border-border/60 px-3 py-2 text-center text-xs transition-colors"
|
||||
>
|
||||
共 {total} 个,查看全部 →
|
||||
</Link>
|
||||
)}
|
||||
</aside>
|
||||
);
|
||||
}
|
||||
Reference in new issue
Block a user