feat(chat): 修复 Strategy/Workspace 统一聊天布局与历史路由归属
This commit is contained in:
1 parent
dbf62e5889
commit
519e4c0c01
7 files changed
+107
-70
No files matched your search
@@ -64,7 +64,7 @@ import {
|
|||||||
import {
|
import {
|
||||||
useDeleteThread,
|
useDeleteThread,
|
||||||
useRenameThread,
|
useRenameThread,
|
||||||
useThreadsPage,
|
useThreads,
|
||||||
useToggleThreadFavorite,
|
useToggleThreadFavorite,
|
||||||
useToggleThreadPin,
|
useToggleThreadPin,
|
||||||
} from "@/core/threads/hooks";
|
} from "@/core/threads/hooks";
|
||||||
@@ -351,8 +351,8 @@ export function AgentChatSidebar({ activeAgentId }: { activeAgentId?: string })
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 当前智能体的「最近对话」面板(参考首页 GeneralQaSidebar 的「最近的对话」区块)。
|
* 「最近的对话」面板(参考首页 GeneralQaSidebar 的「最近的对话」区块)。
|
||||||
* 按 agent_id 在服务端过滤;支持重命名/置顶/收藏/分享/导出/删除。
|
* 跨智能体的近期会话,点击打开;支持重命名/置顶/收藏/分享/导出/删除。
|
||||||
*/
|
*/
|
||||||
function RecentChatsPane({ activeAgentId }: { activeAgentId?: string }) {
|
function RecentChatsPane({ activeAgentId }: { activeAgentId?: string }) {
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
@@ -361,12 +361,7 @@ function RecentChatsPane({ activeAgentId }: { activeAgentId?: string }) {
|
|||||||
const pathOf = usePathOfThread();
|
const pathOf = usePathOfThread();
|
||||||
|
|
||||||
const { thread_id: threadIdFromPath } = useParams<{ thread_id: string }>();
|
const { thread_id: threadIdFromPath } = useParams<{ thread_id: string }>();
|
||||||
const { data: threadPage } = useThreadsPage({
|
const { data: threads = [] } = useThreads();
|
||||||
page: 1,
|
|
||||||
pageSize: 50,
|
|
||||||
metadata: { agent_id: activeAgentId ?? "" },
|
|
||||||
});
|
|
||||||
const threads = threadPage?.threads ?? [];
|
|
||||||
const { mutate: deleteThread } = useDeleteThread();
|
const { mutate: deleteThread } = useDeleteThread();
|
||||||
const { mutate: renameThread } = useRenameThread();
|
const { mutate: renameThread } = useRenameThread();
|
||||||
const { toggle: togglePin } = useToggleThreadPin();
|
const { toggle: togglePin } = useToggleThreadPin();
|
||||||
@@ -378,8 +373,8 @@ function RecentChatsPane({ activeAgentId }: { activeAgentId?: string }) {
|
|||||||
const [renameValue, setRenameValue] = useState("");
|
const [renameValue, setRenameValue] = useState("");
|
||||||
const [shareThreadId, setShareThreadId] = useState<string | null>(null);
|
const [shareThreadId, setShareThreadId] = useState<string | null>(null);
|
||||||
|
|
||||||
// 服务端先按当前智能体过滤;客户端再排除调度/会商/报告结构&写作配置向导等
|
// 排除调度/会商/报告结构&写作配置向导等系统会话,与首页一致;
|
||||||
// 系统会话。智能体创建(bootstrap)会话保留展示(带机器人图标、点击回创建页);置顶优先。
|
// 智能体创建(bootstrap)会话保留展示(带机器人图标、点击回创建页);置顶优先。
|
||||||
const visibleThreads = useMemo(
|
const visibleThreads = useMemo(
|
||||||
() => sortThreadsPinnedFirst(threads.filter((thread) => !isHiddenFromRecentChats(thread))),
|
() => sortThreadsPinnedFirst(threads.filter((thread) => !isHiddenFromRecentChats(thread))),
|
||||||
[threads],
|
[threads],
|
||||||
@@ -484,11 +479,7 @@ function RecentChatsPane({ activeAgentId }: { activeAgentId?: string }) {
|
|||||||
) : (
|
) : (
|
||||||
filteredThreads.map((thread) => {
|
filteredThreads.map((thread) => {
|
||||||
const isBoot = isBootstrapThread(thread);
|
const isBoot = isBootstrapThread(thread);
|
||||||
// 正常记录依据自身保存的 agent_id 跳转;早期记录可能尚未写入
|
const href = isBoot ? bootstrapResumePath(thread.thread_id) : pathOf(thread);
|
||||||
// agent_id,此时保留当前智能体上下文,避免从智能体页误落到通用问答。
|
|
||||||
const href = isBoot
|
|
||||||
? bootstrapResumePath(thread.thread_id)
|
|
||||||
: pathOf(thread, { agent_id: activeAgentId });
|
|
||||||
const isActive = href === pathname;
|
const isActive = href === pathname;
|
||||||
const pinned = isThreadPinned(thread);
|
const pinned = isThreadPinned(thread);
|
||||||
const favorited = isThreadFavorite(thread);
|
const favorited = isThreadFavorite(thread);
|
||||||
|
|||||||
@@ -7,7 +7,8 @@
|
|||||||
* 共享的 WorkspaceNavChatList / FrequentAgentsList / RecentChatList 里塞会越来越难
|
* 共享的 WorkspaceNavChatList / FrequentAgentsList / RecentChatList 里塞会越来越难
|
||||||
* 维护,故单独成一个组件,只在 `/page/workspace/chats*` 的全量布局下挂载,互不影响。
|
* 维护,故单独成一个组件,只在 `/page/workspace/chats*` 的全量布局下挂载,互不影响。
|
||||||
*
|
*
|
||||||
* 三块结构(自上而下):
|
* 可挂在 workspace / strategy 两个普通问答路由下;导航目标由 RouteBaseProvider
|
||||||
|
* 决定。三块结构(自上而下):
|
||||||
* 1. 「新对话」胶囊按钮 + 收起整列的圆形按钮;
|
* 1. 「新对话」胶囊按钮 + 收起整列的圆形按钮;
|
||||||
* 2. 「常用智能体」—— 彩色图标卡(名称 + 两行简介,命中为选中态:主色描边 + 右侧主色条);
|
* 2. 「常用智能体」—— 彩色图标卡(名称 + 两行简介,命中为选中态:主色描边 + 右侧主色条);
|
||||||
* 3. 「最近的对话」—— 计数徽标 + 折叠、历史搜索框、会话列表(悬浮「更多」菜单:
|
* 3. 「最近的对话」—— 计数徽标 + 折叠、历史搜索框、会话列表(悬浮「更多」菜单:
|
||||||
@@ -85,6 +86,7 @@ import {
|
|||||||
titleOfThread,
|
titleOfThread,
|
||||||
} from "@/core/threads/utils";
|
} from "@/core/threads/utils";
|
||||||
import { buildFullChatNavigationPath } from "@/core/page-layout/chats-iframe-route";
|
import { buildFullChatNavigationPath } from "@/core/page-layout/chats-iframe-route";
|
||||||
|
import { useRouteBase } from "@/core/route-base";
|
||||||
import { env } from "@/env";
|
import { env } from "@/env";
|
||||||
import { desensitize } from "@/lib/desensitize";
|
import { desensitize } from "@/lib/desensitize";
|
||||||
import { isIMEComposing } from "@/lib/ime";
|
import { isIMEComposing } from "@/lib/ime";
|
||||||
@@ -93,11 +95,6 @@ import { cn } from "@/lib/utils";
|
|||||||
import { agentColor } from "./agent-icon-color";
|
import { agentColor } from "./agent-icon-color";
|
||||||
import { SidebarCollapseButton } from "./workspace-header";
|
import { SidebarCollapseButton } from "./workspace-header";
|
||||||
|
|
||||||
const NEW_CHAT_PATH = "/page/workspace/chats/new";
|
|
||||||
/** 「更多」常用智能体跳转目标:智能体管理页。 */
|
|
||||||
const MORE_AGENTS_HREF = "/page/workspace/agents";
|
|
||||||
/** 「更多最近对话」跳转目标。 */
|
|
||||||
const MORE_CHATS_HREF = "/page/workspace/chats";
|
|
||||||
/** 常用智能体最多展示个数。 */
|
/** 常用智能体最多展示个数。 */
|
||||||
const MAX_FREQUENT = 4;
|
const MAX_FREQUENT = 4;
|
||||||
|
|
||||||
@@ -142,6 +139,7 @@ function SectionLabel({
|
|||||||
|
|
||||||
export function GeneralQaSidebar() {
|
export function GeneralQaSidebar() {
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
|
const routeBase = useRouteBase();
|
||||||
// 智能体图标配色分主题:深蓝主题用规范蓝-青系,其它(浅色)保持原高彩度色,互不影响。
|
// 智能体图标配色分主题:深蓝主题用规范蓝-青系,其它(浅色)保持原高彩度色,互不影响。
|
||||||
const { theme } = useTheme();
|
const { theme } = useTheme();
|
||||||
const isDark = theme === "dark";
|
const isDark = theme === "dark";
|
||||||
@@ -188,9 +186,14 @@ export function GeneralQaSidebar() {
|
|||||||
const searchParams = useSearchParams();
|
const searchParams = useSearchParams();
|
||||||
const activeAgentId = searchParams.get("agent");
|
const activeAgentId = searchParams.get("agent");
|
||||||
const currentSearch = searchParams.toString();
|
const currentSearch = searchParams.toString();
|
||||||
const newChatHref = buildFullChatNavigationPath(NEW_CHAT_PATH, currentSearch);
|
const chatsBasePath = `${routeBase}/chats`;
|
||||||
const moreAgentsHref = buildFullChatNavigationPath(MORE_AGENTS_HREF, currentSearch);
|
const newChatPath = `${chatsBasePath}/new`;
|
||||||
const moreChatsHref = buildFullChatNavigationPath(MORE_CHATS_HREF, currentSearch);
|
const newChatHref = buildFullChatNavigationPath(newChatPath, currentSearch);
|
||||||
|
const moreAgentsHref = buildFullChatNavigationPath(
|
||||||
|
`${routeBase}/agents`,
|
||||||
|
currentSearch,
|
||||||
|
);
|
||||||
|
const moreChatsHref = buildFullChatNavigationPath(chatsBasePath, currentSearch);
|
||||||
|
|
||||||
// 点击常用智能体 = 在通用问答右侧**原地嵌入**该智能体的对话页:停留在 /chats/new,
|
// 点击常用智能体 = 在通用问答右侧**原地嵌入**该智能体的对话页:停留在 /chats/new,
|
||||||
// 仅给 URL 加 ?agent=<id>,由 NewChatContent 把右侧问答区换成 AgentChatPage。
|
// 仅给 URL 加 ?agent=<id>,由 NewChatContent 把右侧问答区换成 AgentChatPage。
|
||||||
@@ -199,19 +202,18 @@ export function GeneralQaSidebar() {
|
|||||||
(agentId: string) => {
|
(agentId: string) => {
|
||||||
navigate(
|
navigate(
|
||||||
buildFullChatNavigationPath(
|
buildFullChatNavigationPath(
|
||||||
`${NEW_CHAT_PATH}?agent=${encodeURIComponent(agentId)}`,
|
routeBase === "/page/workspace"
|
||||||
|
? `${newChatPath}?agent=${encodeURIComponent(agentId)}`
|
||||||
|
: `${routeBase}/agents/${encodeURIComponent(agentId)}/chats/new`,
|
||||||
currentSearch,
|
currentSearch,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
[currentSearch, navigate],
|
[currentSearch, navigate, newChatPath, routeBase],
|
||||||
);
|
);
|
||||||
|
|
||||||
// ── 最近的对话取数 + 搜索 ──
|
// ── 最近的对话取数 + 搜索 ──
|
||||||
const { thread_id: threadIdFromPath, agent_id: agentIdFromPath } = useParams<{
|
const { thread_id: threadIdFromPath } = useParams<{ thread_id: string }>();
|
||||||
thread_id: string;
|
|
||||||
agent_id?: string;
|
|
||||||
}>();
|
|
||||||
const { data: threads = [] } = useThreads();
|
const { data: threads = [] } = useThreads();
|
||||||
const { mutate: deleteThread } = useDeleteThread();
|
const { mutate: deleteThread } = useDeleteThread();
|
||||||
const { mutate: renameThread } = useRenameThread();
|
const { mutate: renameThread } = useRenameThread();
|
||||||
@@ -224,6 +226,14 @@ export function GeneralQaSidebar() {
|
|||||||
const [renameValue, setRenameValue] = useState("");
|
const [renameValue, setRenameValue] = useState("");
|
||||||
const [shareThreadId, setShareThreadId] = useState<string | null>(null);
|
const [shareThreadId, setShareThreadId] = useState<string | null>(null);
|
||||||
|
|
||||||
|
const pathForThread = useCallback(
|
||||||
|
(thread: AgentThread) =>
|
||||||
|
isBootstrapThread(thread)
|
||||||
|
? listPathOfThread(thread)
|
||||||
|
: pathOfThread(thread, undefined, routeBase),
|
||||||
|
[routeBase],
|
||||||
|
);
|
||||||
|
|
||||||
const visibleThreads = useMemo(
|
const visibleThreads = useMemo(
|
||||||
() =>
|
() =>
|
||||||
// 智能体创建(bootstrap)会话也展示(带机器人图标、点击回创建页);
|
// 智能体创建(bootstrap)会话也展示(带机器人图标、点击回创建页);
|
||||||
@@ -254,21 +264,22 @@ export function GeneralQaSidebar() {
|
|||||||
deleteThread({ threadId });
|
deleteThread({ threadId });
|
||||||
if (threadId === threadIdFromPath) {
|
if (threadId === threadIdFromPath) {
|
||||||
const threadIndex = threads.findIndex((th) => th.thread_id === threadId);
|
const threadIndex = threads.findIndex((th) => th.thread_id === threadId);
|
||||||
let nextThreadPath = pathOfThread("new", { agent_id: agentIdFromPath });
|
let nextThreadPath = newChatPath;
|
||||||
if (threadIndex > -1) {
|
if (threadIndex > -1) {
|
||||||
if (threads[threadIndex + 1]) {
|
if (threads[threadIndex + 1]) {
|
||||||
nextThreadPath = listPathOfThread(threads[threadIndex + 1]!);
|
nextThreadPath = pathForThread(threads[threadIndex + 1]!);
|
||||||
} else if (threads[threadIndex - 1]) {
|
} else if (threads[threadIndex - 1]) {
|
||||||
nextThreadPath = listPathOfThread(threads[threadIndex - 1]!);
|
nextThreadPath = pathForThread(threads[threadIndex - 1]!);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
void router.push(buildFullChatNavigationPath(nextThreadPath, currentSearch));
|
void router.push(buildFullChatNavigationPath(nextThreadPath, currentSearch));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[
|
[
|
||||||
agentIdFromPath,
|
|
||||||
currentSearch,
|
currentSearch,
|
||||||
deleteThread,
|
deleteThread,
|
||||||
|
newChatPath,
|
||||||
|
pathForThread,
|
||||||
router,
|
router,
|
||||||
threadIdFromPath,
|
threadIdFromPath,
|
||||||
threads,
|
threads,
|
||||||
@@ -467,7 +478,7 @@ export function GeneralQaSidebar() {
|
|||||||
) : (
|
) : (
|
||||||
filteredThreads.map((thread) => {
|
filteredThreads.map((thread) => {
|
||||||
const href = buildFullChatNavigationPath(
|
const href = buildFullChatNavigationPath(
|
||||||
listPathOfThread(thread),
|
pathForThread(thread),
|
||||||
currentSearch,
|
currentSearch,
|
||||||
);
|
);
|
||||||
const isActive = href === pathname;
|
const isActive = href === pathname;
|
||||||
|
|||||||
@@ -34,7 +34,9 @@ export function WorkspaceSidebar({
|
|||||||
// 通用问答(全量模式)左栏:新建回答 → 常用智能体 → 聊天记录(需求 0616 #1)。
|
// 通用问答(全量模式)左栏:新建回答 → 常用智能体 → 聊天记录(需求 0616 #1)。
|
||||||
// 仅在通用问答(/page/workspace/chats*)下启用三块式布局;点击常用智能体后是
|
// 仅在通用问答(/page/workspace/chats*)下启用三块式布局;点击常用智能体后是
|
||||||
// 「原地嵌入」(停留在 /chats/new?agent=...,仅右侧问答区换成智能体对话),左侧栏不变。
|
// 「原地嵌入」(停留在 /chats/new?agent=...,仅右侧问答区换成智能体对话),左侧栏不变。
|
||||||
const isGeneralQa = pathname.startsWith("/page/workspace/chats");
|
const isGeneralQa =
|
||||||
|
pathname.startsWith("/page/workspace/chats") ||
|
||||||
|
pathname.startsWith("/page/strategy/chats");
|
||||||
|
|
||||||
if (forceCollapsed) {
|
if (forceCollapsed) {
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
import {
|
import {
|
||||||
BotIcon,
|
BotIcon,
|
||||||
Compass,
|
Compass,
|
||||||
|
PanelLeftCloseIcon,
|
||||||
PanelLeftOpenIcon,
|
PanelLeftOpenIcon,
|
||||||
PanelRightOpenIcon,
|
PanelRightOpenIcon,
|
||||||
PlusSquare,
|
PlusSquare,
|
||||||
@@ -14,7 +15,6 @@ import { useCallback, useEffect, useMemo, useRef, useState, type ReactNode } fro
|
|||||||
|
|
||||||
import type { PromptInputMessage } from "@/components/ai-elements/prompt-input";
|
import type { PromptInputMessage } from "@/components/ai-elements/prompt-input";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import { useSidebarSafe } from "@/components/ui/sidebar";
|
|
||||||
import { AgentChatSidebar } from "@/components/workspace/agents/agent-chat-sidebar";
|
import { AgentChatSidebar } from "@/components/workspace/agents/agent-chat-sidebar";
|
||||||
import { AgentDetailPanel } from "@/components/workspace/agents/agent-detail-panel";
|
import { AgentDetailPanel } from "@/components/workspace/agents/agent-detail-panel";
|
||||||
import { AgentQuestionChips } from "@/components/workspace/agents/agent-question-chips";
|
import { AgentQuestionChips } from "@/components/workspace/agents/agent-question-chips";
|
||||||
@@ -87,6 +87,7 @@ import {
|
|||||||
AGENTFX_REPORT_JSON_OUTPUT_PATH,
|
AGENTFX_REPORT_JSON_OUTPUT_PATH,
|
||||||
notifyTaskReportSaved,
|
notifyTaskReportSaved,
|
||||||
} from "@/strategy-components/lib/agentfx-report-schema";
|
} from "@/strategy-components/lib/agentfx-report-schema";
|
||||||
|
import { cn } from "@/lib/utils";
|
||||||
import type { Message } from "@langchain/langgraph-sdk";
|
import type { Message } from "@langchain/langgraph-sdk";
|
||||||
|
|
||||||
/** 内置「模板数据转换器」agent 的稳定 id。仅此 agent 的对话页显示「页面模板 + 渲染预览」调试入口。 */
|
/** 内置「模板数据转换器」agent 的稳定 id。仅此 agent 的对话页显示「页面模板 + 渲染预览」调试入口。 */
|
||||||
@@ -227,7 +228,9 @@ export default function AgentChatPage({
|
|||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
const [showFollowups, setShowFollowups] = useState(false);
|
const [showFollowups, setShowFollowups] = useState(false);
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const workspaceSidebar = useSidebarSafe();
|
// 独立智能体页的左列是 AgentChatSidebar,而不是外层 WorkspaceSidebar;
|
||||||
|
// 由本页维护其收展状态,避免顶部按钮切到了不可见的另一层侧栏。
|
||||||
|
const [agentSidebarOpen, setAgentSidebarOpen] = useState(true);
|
||||||
|
|
||||||
const { agent_id: agentIdFromPath } = useParams<{
|
const { agent_id: agentIdFromPath } = useParams<{
|
||||||
agent_id: string;
|
agent_id: string;
|
||||||
@@ -797,7 +800,11 @@ export default function AgentChatPage({
|
|||||||
{showAgentList && (
|
{showAgentList && (
|
||||||
<div
|
<div
|
||||||
data-agent-guide="chat-sidebar"
|
data-agent-guide="chat-sidebar"
|
||||||
className="flex h-full shrink-0"
|
aria-hidden={!agentSidebarOpen}
|
||||||
|
className={cn(
|
||||||
|
"flex h-full shrink-0 overflow-hidden transition-[width] duration-200 ease-out",
|
||||||
|
agentSidebarOpen ? "w-(--sidebar-width)" : "pointer-events-none w-0",
|
||||||
|
)}
|
||||||
>
|
>
|
||||||
<AgentChatSidebar activeAgentId={agent_id} />
|
<AgentChatSidebar activeAgentId={agent_id} />
|
||||||
</div>
|
</div>
|
||||||
@@ -809,23 +816,25 @@ export default function AgentChatPage({
|
|||||||
<main className="relative flex size-full min-h-0 min-w-0 flex-col">
|
<main className="relative flex size-full min-h-0 min-w-0 flex-col">
|
||||||
{/* Header in normal flow, fixed height. */}
|
{/* Header in normal flow, fixed height. */}
|
||||||
<header className="bg-card/90 border-border/70 flex h-12 shrink-0 items-center gap-2 border-b px-4 backdrop-blur-md">
|
<header className="bg-card/90 border-border/70 flex h-12 shrink-0 items-center gap-2 border-b px-4 backdrop-blur-md">
|
||||||
{/* 工作区左栏收起后,在智能体聊天区左上角提供展开入口。 */}
|
{/* 智能体列表栏的本地收展入口。 */}
|
||||||
{showAgentList &&
|
{showAgentList && !isEmbedded && (
|
||||||
!isEmbedded &&
|
<Button
|
||||||
workspaceSidebar &&
|
type="button"
|
||||||
!workspaceSidebar.open && (
|
variant="ghost"
|
||||||
<Button
|
size="icon-sm"
|
||||||
type="button"
|
onClick={() => setAgentSidebarOpen((open) => !open)}
|
||||||
variant="ghost"
|
title={agentSidebarOpen ? "收起左侧栏" : "展开左侧栏"}
|
||||||
size="icon-sm"
|
aria-label={agentSidebarOpen ? "收起左侧栏" : "展开左侧栏"}
|
||||||
onClick={workspaceSidebar.toggleSidebar}
|
aria-expanded={agentSidebarOpen}
|
||||||
title="展开左侧栏"
|
className="shrink-0 text-muted-foreground hover:text-foreground"
|
||||||
aria-label="展开左侧栏"
|
>
|
||||||
className="shrink-0 text-muted-foreground hover:text-foreground"
|
{agentSidebarOpen ? (
|
||||||
>
|
<PanelLeftCloseIcon className="size-4" />
|
||||||
|
) : (
|
||||||
<PanelLeftOpenIcon className="size-4" />
|
<PanelLeftOpenIcon className="size-4" />
|
||||||
</Button>
|
)}
|
||||||
)}
|
</Button>
|
||||||
|
)}
|
||||||
<div className="flex shrink-0 items-center gap-1.5 rounded-md border px-2 py-1">
|
<div className="flex shrink-0 items-center gap-1.5 rounded-md border px-2 py-1">
|
||||||
<BotIcon className="text-primary h-3.5 w-3.5" />
|
<BotIcon className="text-primary h-3.5 w-3.5" />
|
||||||
<span
|
<span
|
||||||
@@ -1121,6 +1130,7 @@ export default function AgentChatPage({
|
|||||||
onAskQuestion={(text) => handleSubmit({ text, files: [] })}
|
onAskQuestion={(text) => handleSubmit({ text, files: [] })}
|
||||||
collapsed={detailCollapsed}
|
collapsed={detailCollapsed}
|
||||||
onCollapsedChange={setDetailCollapsed}
|
onCollapsedChange={setDetailCollapsed}
|
||||||
|
showRecentChats={!taskCtxOn}
|
||||||
compactAppearanceActions={compactAppearanceActions}
|
compactAppearanceActions={compactAppearanceActions}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ import { isEmbedActive, isHideChromeActive } from "@/core/embed/embed-session";
|
|||||||
import { isWujieEnv } from "@/core/embed/wujie";
|
import { isWujieEnv } from "@/core/embed/wujie";
|
||||||
import { useSidebar } from "@/components/ui/sidebar";
|
import { useSidebar } from "@/components/ui/sidebar";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import { MessageSquarePlus, PanelLeftOpen } from "lucide-react";
|
import { MessageSquarePlus, PanelLeftClose, PanelLeftOpen } from "lucide-react";
|
||||||
import { useNavigate } from "react-router-dom";
|
import { useNavigate } from "react-router-dom";
|
||||||
import { useAgentSelector, useAgentsPaginated } from "@/core/agents";
|
import { useAgentSelector, useAgentsPaginated } from "@/core/agents";
|
||||||
import { useI18n } from "@/core/i18n/hooks";
|
import { useI18n } from "@/core/i18n/hooks";
|
||||||
@@ -728,18 +728,22 @@ export default function ChatPage() {
|
|||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<div className="flex min-w-0 flex-1 items-center overflow-hidden text-sm font-medium">
|
<div className="flex min-w-0 flex-1 items-center overflow-hidden text-sm font-medium">
|
||||||
{/* 左栏收起后,展开入口固定在聊天区左上角。 */}
|
{/* 左上角始终提供左栏切换;展开态显示“收起”,收起态显示“展开”。 */}
|
||||||
{compactChrome && !historySidebarOpen && !compactActionsHidden && (
|
{compactChrome && !compactActionsHidden && (
|
||||||
<Button
|
<Button
|
||||||
type="button"
|
type="button"
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
size="icon-sm"
|
size="icon-sm"
|
||||||
onClick={toggleHistorySidebar}
|
onClick={toggleHistorySidebar}
|
||||||
title="展开左侧栏"
|
title={historySidebarOpen ? "收起左侧栏" : "展开左侧栏"}
|
||||||
aria-label="展开左侧栏"
|
aria-label={historySidebarOpen ? "收起左侧栏" : "展开左侧栏"}
|
||||||
className="mr-1 shrink-0 text-muted-foreground hover:text-foreground"
|
className="mr-1 shrink-0 text-muted-foreground hover:text-foreground"
|
||||||
>
|
>
|
||||||
<PanelLeftOpen className="size-4" />
|
{historySidebarOpen ? (
|
||||||
|
<PanelLeftClose className="size-4" />
|
||||||
|
) : (
|
||||||
|
<PanelLeftOpen className="size-4" />
|
||||||
|
)}
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
<ThreadTitle
|
<ThreadTitle
|
||||||
@@ -753,7 +757,7 @@ export default function ChatPage() {
|
|||||||
enabled={tokenUsageEnabled}
|
enabled={tokenUsageEnabled}
|
||||||
messages={displayThread.messages}
|
messages={displayThread.messages}
|
||||||
/>
|
/>
|
||||||
{compactChrome && !historySidebarOpen && !compactActionsHidden && (
|
{compactChrome && !compactActionsHidden && (
|
||||||
<Button
|
<Button
|
||||||
type="button"
|
type="button"
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
import { Navigate, Route, Routes } from "react-router-dom";
|
import { Navigate, Route, Routes } from "react-router-dom";
|
||||||
import { Toaster } from "sonner";
|
import { Toaster } from "sonner";
|
||||||
|
|
||||||
import { SidebarProvider } from "@/components/ui/sidebar";
|
import { SidebarInset, SidebarProvider } from "@/components/ui/sidebar";
|
||||||
|
import { WorkspaceSidebar } from "@/components/workspace/workspace-sidebar";
|
||||||
import { getAccountDisplayName, getStoredAuth } from "@/core/auth";
|
import { getAccountDisplayName, getStoredAuth } from "@/core/auth";
|
||||||
import { isSentimentAgentVisible } from "@/core/sentiment-agent/config";
|
import { isSentimentAgentVisible } from "@/core/sentiment-agent/config";
|
||||||
import { RouteBaseProvider } from "@/core/route-base";
|
import { RouteBaseProvider } from "@/core/route-base";
|
||||||
@@ -79,6 +80,24 @@ import AdminScheduledTasksPage from "./AdminScheduledTasksPage";
|
|||||||
import AdminUserActivityPage from "./AdminUserActivityPage";
|
import AdminUserActivityPage from "./AdminUserActivityPage";
|
||||||
import { ChatRuntime } from "./WorkspaceRoutes";
|
import { ChatRuntime } from "./WorkspaceRoutes";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Strategy's legacy `/chats/:thread_id` route shares ChatPage's header.
|
||||||
|
* Unlike WorkspaceRoutes it did not render a WorkspaceSidebar, leaving the
|
||||||
|
* header's expand/collapse control with no visible panel to operate.
|
||||||
|
*/
|
||||||
|
function StrategyChatThreadLayout() {
|
||||||
|
return (
|
||||||
|
<div className="flex size-full min-h-0 overflow-hidden">
|
||||||
|
<WorkspaceSidebar compactChrome />
|
||||||
|
<SidebarInset className="min-w-0">
|
||||||
|
<ChatRuntime>
|
||||||
|
<ChatPage />
|
||||||
|
</ChatRuntime>
|
||||||
|
</SidebarInset>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
export default function StrategyRoutes() {
|
export default function StrategyRoutes() {
|
||||||
const storedAuth = getStoredAuth();
|
const storedAuth = getStoredAuth();
|
||||||
const canManageBusinessMappings = !storedAuth || storedAuth.system_role === "admin";
|
const canManageBusinessMappings = !storedAuth || storedAuth.system_role === "admin";
|
||||||
@@ -342,11 +361,7 @@ export default function StrategyRoutes() {
|
|||||||
<Route path="chats" element={<ChatsPage />} />
|
<Route path="chats" element={<ChatsPage />} />
|
||||||
<Route
|
<Route
|
||||||
path="chats/:thread_id"
|
path="chats/:thread_id"
|
||||||
element={
|
element={<StrategyChatThreadLayout />}
|
||||||
<ChatRuntime>
|
|
||||||
<ChatPage />
|
|
||||||
</ChatRuntime>
|
|
||||||
}
|
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Route path="agents" element={<AgentsPage />} />
|
<Route path="agents" element={<AgentsPage />} />
|
||||||
|
|||||||
@@ -182,7 +182,11 @@ export function WorkspaceLayout({
|
|||||||
'--header-height': headerHidden ? '0rem' : '4rem',
|
'--header-height': headerHidden ? '0rem' : '4rem',
|
||||||
} as CSSProperties
|
} as CSSProperties
|
||||||
}
|
}
|
||||||
key={String(compactWorkspaceSidebar)}
|
// Agent and general-chat routes share this layout component. Keep their
|
||||||
|
// sidebar state separate: otherwise React preserves the collapsed agent
|
||||||
|
// state when a history item switches to `/chats/:thread_id`, while the
|
||||||
|
// new page's header button is meant to control GeneralQaSidebar.
|
||||||
|
key={`${String(compactWorkspaceSidebar)}:${isGeneralQa ? 'general' : 'workspace'}`}
|
||||||
data-page-sidebar-collapsed={pageSidebarCollapsed}
|
data-page-sidebar-collapsed={pageSidebarCollapsed}
|
||||||
defaultOpen={
|
defaultOpen={
|
||||||
forceWorkspaceSidebarCollapsed
|
forceWorkspaceSidebarCollapsed
|
||||||
|
|||||||
Reference in new issue
Block a user