diff --git a/frontend-web/src/components/workspace/input-box.tsx b/frontend-web/src/components/workspace/input-box.tsx index ac6bd91c..4d0a0be8 100644 --- a/frontend-web/src/components/workspace/input-box.tsx +++ b/frontend-web/src/components/workspace/input-box.tsx @@ -143,6 +143,7 @@ export function InputBox({ hideAdminRecommendedQuestions, compact, hideContextTools, + hideMemoryTool = true, ...props }: Omit, "onSubmit"> & { assistantId?: string | null; @@ -153,6 +154,11 @@ export function InputBox({ /** 隐藏输入框工具条上的「写作 / 记忆 / 参考文献」上下文开关按钮(任务深链工作区等 * 受控场景用;文件/模型等其他按钮不受影响)。 */ hideContextTools?: boolean; + /** + * 仅控制「记忆」开关的可见性。产品当前全局隐藏,传入 false 即可无损恢复。 + * 写作和参考文献开关不受影响。 + */ + hideMemoryTool?: boolean; disabled?: boolean; context: Omit< AgentThreadContext, @@ -752,7 +758,7 @@ export function InputBox({ )} /> )} - {!frontendRagActive && !hideContextTools && ; @@ -47,9 +49,9 @@ export function ThreadTitle({ if (!thread.values?.title) { return null; } - const visibleTitle = desensitize(displayTitle); - return ( - + const visibleTitle = desensitize(displayTitle); + return ( + {visibleTitle} diff --git a/frontend-web/src/core/page-layout/sidebar-menu.ts b/frontend-web/src/core/page-layout/sidebar-menu.ts index c3a1ee68..697c3025 100644 --- a/frontend-web/src/core/page-layout/sidebar-menu.ts +++ b/frontend-web/src/core/page-layout/sidebar-menu.ts @@ -88,12 +88,12 @@ export const sidebarMenuItems: MenuItem[] = [ }, { id: "qa-zzxd", label: "XD应用", icon: Layout, path: "/page/strategy/qa/zzxd" }, { id: "qa-research", label: "课题研究", icon: FileText, path: "/page/canvas/ai-writing" }, - { - id: "qa-research-chat", - label: "课题研究(对话版)", - icon: FileText, - path: "/page/canvas/ai-writing-chat", - }, + // { + // id: "qa-research-chat", + // label: "课题研究(对话版)", + // icon: FileText, + // path: "/page/canvas/ai-writing-chat", + // }, { id: "qa-deep-research", label: "深度研究", diff --git a/frontend-web/src/pages/ChatPage.tsx b/frontend-web/src/pages/ChatPage.tsx index 33819cf9..e516a9f2 100644 --- a/frontend-web/src/pages/ChatPage.tsx +++ b/frontend-web/src/pages/ChatPage.tsx @@ -38,12 +38,16 @@ import { CompactModeGuide } from "@/components/workspace/compact-mode-guide"; import { Welcome } from "@/components/workspace/welcome"; import { CompactWelcome } from "@/components/workspace/compact-welcome"; import { readSidebarEnabled } from "@/core/page-layout/sidebar-enabled"; -import { isChatsIframeWorkspacePath } from "@/core/page-layout/chats-iframe-route"; +import { + buildFullChatNavigationPath, + isChatsIframeWorkspacePath, +} from "@/core/page-layout/chats-iframe-route"; import { isEmbedActive, isHideChromeActive } from "@/core/embed/embed-session"; import { isWujieEnv } from "@/core/embed/wujie"; import { useSidebar } from "@/components/ui/sidebar"; import { Button } from "@/components/ui/button"; -import { PanelLeftOpen } from "lucide-react"; +import { MessageSquarePlus, PanelLeftOpen } from "lucide-react"; +import { useNavigate } from "react-router-dom"; import { useAgentSelector, useAgentsPaginated } from "@/core/agents"; import { useI18n } from "@/core/i18n/hooks"; import { useModels } from "@/core/models/hooks"; @@ -114,6 +118,7 @@ function clearBrowserContextParamsFromHash() { export default function ChatPage() { const routeBase = useRouteBase(); const router = useRouter(); + const navigate = useNavigate(); const searchParams = useSearchParams(); const pathname = usePathname(); // `chatMode=full` is used by an embedded standard chat. It intentionally @@ -671,6 +676,20 @@ export default function ChatPage() { chromeByPreference && !isChatsIframeWorkspacePath(pathname); const compactLanding = isLandingState && compactChrome; const compactActionsHidden = isHideChromeActive(); + const headerNewChatPath = useMemo( + () => + buildFullChatNavigationPath( + `${routeBase}/chats/new`, + searchParams.toString(), + ), + [routeBase, searchParams], + ); + const handleHeaderNewChat = useCallback(() => { + navigate(headerNewChatPath, { + replace: true, + state: { resetAt: Date.now() }, + }); + }, [headerNewChatPath, navigate]); // 简洁模式新手引导:仅面向「无全局导航栏/侧边栏」的正常简洁布局。iframe 嵌入 // (embed=1)、去 chrome(hideChrome=1)、强制收起侧栏(无界部署)等环境同样没有 // 全局 chrome,但不该在外部系统里弹引导。 @@ -709,27 +728,43 @@ export default function ChatPage() { )} >
- -
-
- {/* 仅在收起态显示「展开历史」钮;展开后由侧栏内(新对话右侧)的收起钮负责收起。 */} + {/* 左栏收起后,展开入口固定在聊天区左上角。 */} {compactChrome && !historySidebarOpen && !compactActionsHidden && ( )} + +
+
+ {compactChrome && !historySidebarOpen && !compactActionsHidden && ( + + )} @@ -797,11 +832,10 @@ export default function ChatPage() { isLandingState && !compactLanding && "-translate-y-[calc(50vh-96px)]", - compactLanding - ? "max-w-[calc(var(--container-width-xl)+150px)]" - : isLandingState - ? "max-w-[calc(var(--container-width-md)+150px)]" - : "max-w-(--container-width-lg)", + // 新对话落地态在全量与简洁模式使用同一输入框宽度。 + isLandingState + ? "max-w-[calc(var(--container-width-md)+150px)]" + : "max-w-(--container-width-lg)", )} >
diff --git a/frontend-web/src/pages/iframe-chat-mode-buttons.test.ts b/frontend-web/src/pages/iframe-chat-mode-buttons.test.ts index e9144784..562f277d 100644 --- a/frontend-web/src/pages/iframe-chat-mode-buttons.test.ts +++ b/frontend-web/src/pages/iframe-chat-mode-buttons.test.ts @@ -4,7 +4,7 @@ import test from "node:test"; // @ts-ignore Node's test runner resolves the local .ts module directly. import { buildIframeModeButtons } from "./iframe-chat-mode-buttons.ts"; -test("includes the context compression toggle in the iframe chat toolbar", () => { +test("hides the memory toggle while retaining the other iframe chat toolbar controls", () => { const buttons = buildIframeModeButtons({ agentSelected: false, writingModeEnabled: false, @@ -20,7 +20,6 @@ test("includes the context compression toggle in the iframe chat toolbar", () => buttons.map((button) => button.key), [ "writing", - "memory", "references", "contextCompression", "pro", @@ -29,6 +28,22 @@ test("includes the context compression toggle in the iframe chat toolbar", () => ); }); +test("can restore the iframe memory toggle when the feature is re-enabled", () => { + const buttons = buildIframeModeButtons({ + agentSelected: false, + writingModeEnabled: false, + memoryInjectionEnabled: true, + hideMemoryButton: false, + referenceModeEntryEnabled: true, + ragModeEnabled: false, + ragModeLocked: false, + contextCompressionEnabled: false, + proModeEnabled: false, + }); + + assert.equal(buttons.some((button) => button.key === "memory"), true); +}); + test("hides the references toggle when reference mode is disabled globally", () => { const buttons = buildIframeModeButtons({ agentSelected: false, diff --git a/frontend-web/src/pages/iframe-chat-mode-buttons.ts b/frontend-web/src/pages/iframe-chat-mode-buttons.ts index 506fde9f..814726a2 100644 --- a/frontend-web/src/pages/iframe-chat-mode-buttons.ts +++ b/frontend-web/src/pages/iframe-chat-mode-buttons.ts @@ -15,6 +15,8 @@ type BuildIframeModeButtonsInput = { agentSelected: boolean; writingModeEnabled: boolean; memoryInjectionEnabled: boolean; + /** 产品当前全局隐藏记忆入口;保留 false 供后续无损恢复。 */ + hideMemoryButton?: boolean; referenceModeEntryEnabled: boolean; ragModeEnabled: boolean; ragModeLocked: boolean; @@ -26,6 +28,7 @@ export function buildIframeModeButtons({ agentSelected, writingModeEnabled, memoryInjectionEnabled, + hideMemoryButton = true, referenceModeEntryEnabled, ragModeEnabled, ragModeLocked, @@ -65,7 +68,9 @@ export function buildIframeModeButtons({ active: agentSelected, }, ]; - return referenceModeEntryEnabled - ? buttons - : buttons.filter((button) => button.key !== "references"); + return buttons.filter( + (button) => + (referenceModeEntryEnabled || button.key !== "references") && + (!hideMemoryButton || button.key !== "memory"), + ); }