feat: 智能体问答页右侧面板受控折叠,收起整列归零并在 header 提供展开入口
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
1 parent
d9de41dd67
commit
90d29ed232
2 files changed
+85
-8
No files matched your search
@@ -144,6 +144,8 @@ export function AgentDetailPanel({
|
|||||||
onAskQuestion,
|
onAskQuestion,
|
||||||
disabled,
|
disabled,
|
||||||
defaultCollapsed,
|
defaultCollapsed,
|
||||||
|
collapsed: collapsedProp,
|
||||||
|
onCollapsedChange,
|
||||||
showRecentChats,
|
showRecentChats,
|
||||||
compactAppearanceActions,
|
compactAppearanceActions,
|
||||||
avatarSrc,
|
avatarSrc,
|
||||||
@@ -163,6 +165,14 @@ export function AgentDetailPanel({
|
|||||||
disabled?: boolean;
|
disabled?: boolean;
|
||||||
/** 初始收起(任务工作区进入时默认收起智能体信息面板)。 */
|
/** 初始收起(任务工作区进入时默认收起智能体信息面板)。 */
|
||||||
defaultCollapsed?: boolean;
|
defaultCollapsed?: boolean;
|
||||||
|
/**
|
||||||
|
* 受控折叠态:传入时由父级持有状态(智能体问答页把展开/收起按钮放在顶部
|
||||||
|
* 「新对话」右侧)。此时收起 = 整列宽度动画归零(不保留细条),面板内置的
|
||||||
|
* 收起按钮不再渲染。不传则沿用面板内置状态 + 细条收起交互(会商等工作区)。
|
||||||
|
*/
|
||||||
|
collapsed?: boolean;
|
||||||
|
/** 受控折叠态变化回调(配合 `collapsed` 使用)。 */
|
||||||
|
onCollapsedChange?: (collapsed: boolean) => void;
|
||||||
/** 在面板底部展示「有关此智能体的最近对话」(仅智能体问答页开启,任务工作区不展示)。 */
|
/** 在面板底部展示「有关此智能体的最近对话」(仅智能体问答页开启,任务工作区不展示)。 */
|
||||||
showRecentChats?: boolean;
|
showRecentChats?: boolean;
|
||||||
/** 简洁模式下挂在右上角收起/展开按钮左侧的外观控件。 */
|
/** 简洁模式下挂在右上角收起/展开按钮左侧的外观控件。 */
|
||||||
@@ -187,7 +197,15 @@ export function AgentDetailPanel({
|
|||||||
editIcon?: ReactNode;
|
editIcon?: ReactNode;
|
||||||
}) {
|
}) {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const [collapsed, setCollapsed] = useState(defaultCollapsed ?? false);
|
// 折叠态:受控(collapsedProp 传入,智能体问答页)优先;否则面板内置(会商等工作区)。
|
||||||
|
const [internalCollapsed, setInternalCollapsed] = useState(
|
||||||
|
defaultCollapsed ?? false,
|
||||||
|
);
|
||||||
|
const collapsed = collapsedProp ?? internalCollapsed;
|
||||||
|
const setCollapsed = (next: boolean) => {
|
||||||
|
if (collapsedProp === undefined) setInternalCollapsed(next);
|
||||||
|
onCollapsedChange?.(next);
|
||||||
|
};
|
||||||
const [mobileOpen, setMobileOpen] = useState(false);
|
const [mobileOpen, setMobileOpen] = useState(false);
|
||||||
const [editing, setEditing] = useState(false);
|
const [editing, setEditing] = useState(false);
|
||||||
const [draft, setDraft] = useState<AgentDraft | null>(null);
|
const [draft, setDraft] = useState<AgentDraft | null>(null);
|
||||||
@@ -927,12 +945,14 @@ export function AgentDetailPanel({
|
|||||||
const panelChrome = (collapseButton: ReactNode) => (
|
const panelChrome = (collapseButton: ReactNode) => (
|
||||||
<>
|
<>
|
||||||
<div className="flex h-12 shrink-0 items-center gap-2 border-b px-4">
|
<div className="flex h-12 shrink-0 items-center gap-2 border-b px-4">
|
||||||
<span className="min-w-0 flex-1 truncate whitespace-nowrap text-sm font-semibold">
|
{/* 标题不参与压缩(此前被简洁模式外观控件挤成单字);空间不足时只
|
||||||
|
裁剪 compactAppearanceActions,编辑等主操作与标题始终完整。 */}
|
||||||
|
<span className="shrink-0 text-sm font-semibold whitespace-nowrap">
|
||||||
{panelTitle ?? "智能体信息"}
|
{panelTitle ?? "智能体信息"}
|
||||||
</span>
|
</span>
|
||||||
<div className="flex min-w-0 items-center justify-end gap-1">
|
<div className="flex min-w-0 flex-1 items-center justify-end gap-1">
|
||||||
|
<div className="flex shrink-0 items-center gap-1">{headerActions}</div>
|
||||||
<div className="flex min-w-0 items-center justify-end gap-1 overflow-hidden">
|
<div className="flex min-w-0 items-center justify-end gap-1 overflow-hidden">
|
||||||
{headerActions}
|
|
||||||
{compactAppearanceActions}
|
{compactAppearanceActions}
|
||||||
</div>
|
</div>
|
||||||
{collapseButton && <div className="shrink-0">{collapseButton}</div>}
|
{collapseButton && <div className="shrink-0">{collapseButton}</div>}
|
||||||
@@ -944,11 +964,37 @@ export function AgentDetailPanel({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{/* ── Desktop (lg+): inline aside, collapsible to a rail ───────────── */}
|
{/* ── Desktop (lg+) ────────────────────────────────────────────────── */}
|
||||||
{embedded ? (
|
{embedded ? (
|
||||||
<aside className="bg-card/40 flex h-full w-full min-w-0 shrink-0 flex-col border-l">
|
<aside className="bg-card/40 flex h-full w-full min-w-0 shrink-0 flex-col border-l">
|
||||||
{panelChrome(null)}
|
{panelChrome(null)}
|
||||||
</aside>
|
</aside>
|
||||||
|
) : collapsedProp !== undefined ? (
|
||||||
|
/* 受控模式(智能体问答页):收起时整列宽度动画归零(含左边框),不再保留细条;
|
||||||
|
展开态的「收起」按钮在面板右上角,收起态的「展开」按钮在页面
|
||||||
|
header「新对话」按钮右侧。 */
|
||||||
|
<div
|
||||||
|
className={cn(
|
||||||
|
"hidden h-full shrink-0 overflow-hidden transition-[width] duration-300 ease-in-out lg:block",
|
||||||
|
collapsed ? "w-0" : "w-[360px]",
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<aside className="bg-card/40 flex h-full w-[360px] flex-col border-l">
|
||||||
|
{panelChrome(
|
||||||
|
<Tooltip content="收起">
|
||||||
|
<Button
|
||||||
|
variant="ghost"
|
||||||
|
size="icon-sm"
|
||||||
|
onClick={() => setCollapsed(true)}
|
||||||
|
aria-label="收起智能体信息"
|
||||||
|
aria-expanded={true}
|
||||||
|
>
|
||||||
|
<PanelRightCloseIcon className="size-4" />
|
||||||
|
</Button>
|
||||||
|
</Tooltip>,
|
||||||
|
)}
|
||||||
|
</aside>
|
||||||
|
</div>
|
||||||
) : collapsed ? (
|
) : collapsed ? (
|
||||||
<div className="hidden shrink-0 items-start border-l px-2 pt-3 lg:flex">
|
<div className="hidden shrink-0 items-start border-l px-2 pt-3 lg:flex">
|
||||||
<div className="flex items-center gap-1">
|
<div className="flex items-center gap-1">
|
||||||
|
|||||||
@@ -1,6 +1,11 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { BotIcon, Compass, PlusSquare } from "lucide-react";
|
import {
|
||||||
|
BotIcon,
|
||||||
|
Compass,
|
||||||
|
PanelRightOpenIcon,
|
||||||
|
PlusSquare,
|
||||||
|
} from "lucide-react";
|
||||||
import { useParams, useRouter } from "next/navigation";
|
import { useParams, useRouter } from "next/navigation";
|
||||||
import { useLocation } from "react-router-dom";
|
import { useLocation } from "react-router-dom";
|
||||||
import { useRouteBase } from "@/core/route-base";
|
import { useRouteBase } from "@/core/route-base";
|
||||||
@@ -191,6 +196,10 @@ export default function AgentChatPage({
|
|||||||
|
|
||||||
// 任务深链上下文(taskId + 开场信息):闸门(rwfx)与纯绑定(3qfx/xdfx)都要读取。
|
// 任务深链上下文(taskId + 开场信息):闸门(rwfx)与纯绑定(3qfx/xdfx)都要读取。
|
||||||
const taskCtxOn = !!(taskGate || taskBind);
|
const taskCtxOn = !!(taskGate || taskBind);
|
||||||
|
// 右侧智能体详情面板折叠态(受控):普通问答默认展开,任务工作区默认收起。
|
||||||
|
// 收起时整列宽度动画关闭;展开态的「收起」按钮在面板右上角,收起态的
|
||||||
|
// 「展开」按钮在顶部 header「新对话」按钮右侧。
|
||||||
|
const [detailCollapsed, setDetailCollapsed] = useState(taskCtxOn);
|
||||||
const isXdfxTaskBind = !!chatBasePath?.includes("/plan-task-action/");
|
const isXdfxTaskBind = !!chatBasePath?.includes("/plan-task-action/");
|
||||||
const taskContext = useMemo<TaskContext | null>(() => {
|
const taskContext = useMemo<TaskContext | null>(() => {
|
||||||
if (!taskCtxOn) return null;
|
if (!taskCtxOn) return null;
|
||||||
@@ -621,7 +630,11 @@ export default function AgentChatPage({
|
|||||||
size="sm"
|
size="sm"
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
className="text-muted-foreground hover:text-foreground mr-1"
|
className="text-muted-foreground hover:text-foreground mr-1"
|
||||||
onClick={() => startAgentGuide("chat")}
|
onClick={() => {
|
||||||
|
// 引导的 chat-detail 步骤会高亮右侧面板,重放前先展开。
|
||||||
|
setDetailCollapsed(false);
|
||||||
|
startAgentGuide("chat");
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
<Compass className="size-4" />
|
<Compass className="size-4" />
|
||||||
使用引导
|
使用引导
|
||||||
@@ -687,6 +700,23 @@ export default function AgentChatPage({
|
|||||||
<PlusSquare /> {t.agents.newChat}
|
<PlusSquare /> {t.agents.newChat}
|
||||||
</Button>
|
</Button>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
|
{/* 右侧智能体详情面板收起时的「展开」入口:仅收起态显示
|
||||||
|
(展开态的「收起」按钮在面板右上角)。桌面端专用(lg+),
|
||||||
|
窄屏面板走抽屉交互;嵌入态面板固定渲染,不显示该开关。 */}
|
||||||
|
{!isEmbedded && detailCollapsed && (
|
||||||
|
<Tooltip content="展开智能体信息">
|
||||||
|
<Button
|
||||||
|
size="sm"
|
||||||
|
variant="ghost"
|
||||||
|
className="text-muted-foreground hover:text-foreground hidden lg:inline-flex"
|
||||||
|
onClick={() => setDetailCollapsed(false)}
|
||||||
|
aria-label="展开智能体信息"
|
||||||
|
aria-expanded={false}
|
||||||
|
>
|
||||||
|
<PanelRightOpenIcon className="size-4" />
|
||||||
|
</Button>
|
||||||
|
</Tooltip>
|
||||||
|
)}
|
||||||
<TokenUsageIndicator
|
<TokenUsageIndicator
|
||||||
enabled={tokenUsageEnabled}
|
enabled={tokenUsageEnabled}
|
||||||
messages={thread.messages}
|
messages={thread.messages}
|
||||||
@@ -851,7 +881,8 @@ export default function AgentChatPage({
|
|||||||
agentName={agentDisplayName}
|
agentName={agentDisplayName}
|
||||||
disabled={thread.isLoading || isDemoMode}
|
disabled={thread.isLoading || isDemoMode}
|
||||||
onAskQuestion={(text) => handleSubmit({ text, files: [] })}
|
onAskQuestion={(text) => handleSubmit({ text, files: [] })}
|
||||||
defaultCollapsed={taskCtxOn}
|
collapsed={detailCollapsed}
|
||||||
|
onCollapsedChange={setDetailCollapsed}
|
||||||
showRecentChats={!taskCtxOn}
|
showRecentChats={!taskCtxOn}
|
||||||
compactAppearanceActions={compactAppearanceActions}
|
compactAppearanceActions={compactAppearanceActions}
|
||||||
/>
|
/>
|
||||||
|
|||||||
Reference in new issue
Block a user