fix: preserve full chat embed mode navigation
This commit is contained in:
1 parent
4450f342d8
commit
e52261fc3d
3 files changed
+71
-9
No files matched your search
@@ -83,6 +83,7 @@ import {
|
|||||||
sortThreadsPinnedFirst,
|
sortThreadsPinnedFirst,
|
||||||
titleOfThread,
|
titleOfThread,
|
||||||
} from "@/core/threads/utils";
|
} from "@/core/threads/utils";
|
||||||
|
import { buildFullChatNavigationPath } from "@/core/page-layout/chats-iframe-route";
|
||||||
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";
|
||||||
@@ -185,15 +186,24 @@ export function GeneralQaSidebar() {
|
|||||||
// 当前嵌入的智能体(URL 上的 ?agent=):用于高亮对应卡片。
|
// 当前嵌入的智能体(URL 上的 ?agent=):用于高亮对应卡片。
|
||||||
const searchParams = useSearchParams();
|
const searchParams = useSearchParams();
|
||||||
const activeAgentId = searchParams.get("agent");
|
const activeAgentId = searchParams.get("agent");
|
||||||
|
const currentSearch = searchParams.toString();
|
||||||
|
const newChatHref = buildFullChatNavigationPath(NEW_CHAT_PATH, currentSearch);
|
||||||
|
const moreAgentsHref = buildFullChatNavigationPath(MORE_AGENTS_HREF, currentSearch);
|
||||||
|
const moreChatsHref = buildFullChatNavigationPath(MORE_CHATS_HREF, currentSearch);
|
||||||
|
|
||||||
// 点击常用智能体 = 在通用问答右侧**原地嵌入**该智能体的对话页:停留在 /chats/new,
|
// 点击常用智能体 = 在通用问答右侧**原地嵌入**该智能体的对话页:停留在 /chats/new,
|
||||||
// 仅给 URL 加 ?agent=<id>,由 NewChatContent 把右侧问答区换成 AgentChatPage。
|
// 仅给 URL 加 ?agent=<id>,由 NewChatContent 把右侧问答区换成 AgentChatPage。
|
||||||
// 不跳转独立页面、左侧栏不变。每次点击都重开一个干净会话(NewChatContent 的 key)。
|
// 不跳转独立页面、左侧栏不变。每次点击都重开一个干净会话(NewChatContent 的 key)。
|
||||||
const handleSelectAgent = useCallback(
|
const handleSelectAgent = useCallback(
|
||||||
(agentId: string) => {
|
(agentId: string) => {
|
||||||
navigate(`/page/workspace/chats/new?agent=${encodeURIComponent(agentId)}`);
|
navigate(
|
||||||
|
buildFullChatNavigationPath(
|
||||||
|
`${NEW_CHAT_PATH}?agent=${encodeURIComponent(agentId)}`,
|
||||||
|
currentSearch,
|
||||||
|
),
|
||||||
|
);
|
||||||
},
|
},
|
||||||
[navigate],
|
[currentSearch, navigate],
|
||||||
);
|
);
|
||||||
|
|
||||||
// ── 最近的对话取数 + 搜索 ──
|
// ── 最近的对话取数 + 搜索 ──
|
||||||
@@ -241,7 +251,7 @@ export function GeneralQaSidebar() {
|
|||||||
const handleNewChatClick = (event: React.MouseEvent<HTMLAnchorElement>) => {
|
const handleNewChatClick = (event: React.MouseEvent<HTMLAnchorElement>) => {
|
||||||
if (event.metaKey || event.ctrlKey || event.shiftKey || event.button === 1) return;
|
if (event.metaKey || event.ctrlKey || event.shiftKey || event.button === 1) return;
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
navigate(NEW_CHAT_PATH, { replace: true, state: { resetAt: Date.now() } });
|
navigate(newChatHref, { replace: true, state: { resetAt: Date.now() } });
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleDelete = useCallback(
|
const handleDelete = useCallback(
|
||||||
@@ -257,10 +267,17 @@ export function GeneralQaSidebar() {
|
|||||||
nextThreadPath = listPathOfThread(threads[threadIndex - 1]!);
|
nextThreadPath = listPathOfThread(threads[threadIndex - 1]!);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
void router.push(nextThreadPath);
|
void router.push(buildFullChatNavigationPath(nextThreadPath, currentSearch));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[agentIdFromPath, deleteThread, router, threadIdFromPath, threads],
|
[
|
||||||
|
agentIdFromPath,
|
||||||
|
currentSearch,
|
||||||
|
deleteThread,
|
||||||
|
router,
|
||||||
|
threadIdFromPath,
|
||||||
|
threads,
|
||||||
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleRenameClick = useCallback((threadId: string, currentTitle: string) => {
|
const handleRenameClick = useCallback((threadId: string, currentTitle: string) => {
|
||||||
@@ -314,7 +331,7 @@ export function GeneralQaSidebar() {
|
|||||||
底色用「主色混入透明」=主色低透明度叠在当前侧栏底色上,浅/红/蓝/深蓝主题都自适应
|
底色用「主色混入透明」=主色低透明度叠在当前侧栏底色上,浅/红/蓝/深蓝主题都自适应
|
||||||
(不要混 white,否则深蓝主题下会变成浅色块)。 */}
|
(不要混 white,否则深蓝主题下会变成浅色块)。 */}
|
||||||
<Link
|
<Link
|
||||||
href={NEW_CHAT_PATH}
|
href={newChatHref}
|
||||||
onClick={handleNewChatClick}
|
onClick={handleNewChatClick}
|
||||||
style={{ borderColor: "var(--primary)", color: "var(--primary)" }}
|
style={{ borderColor: "var(--primary)", color: "var(--primary)" }}
|
||||||
className={cn(
|
className={cn(
|
||||||
@@ -415,7 +432,7 @@ export function GeneralQaSidebar() {
|
|||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
<Link
|
<Link
|
||||||
href={MORE_AGENTS_HREF}
|
href={moreAgentsHref}
|
||||||
className="flex items-center justify-between rounded-lg px-3 py-1.5 text-[12px] text-muted-foreground/70 transition-colors hover:bg-accent/50 hover:text-foreground"
|
className="flex items-center justify-between rounded-lg px-3 py-1.5 text-[12px] text-muted-foreground/70 transition-colors hover:bg-accent/50 hover:text-foreground"
|
||||||
>
|
>
|
||||||
<span>{t.common.more}</span>
|
<span>{t.common.more}</span>
|
||||||
@@ -454,7 +471,10 @@ export function GeneralQaSidebar() {
|
|||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
filteredThreads.map((thread) => {
|
filteredThreads.map((thread) => {
|
||||||
const href = listPathOfThread(thread);
|
const href = buildFullChatNavigationPath(
|
||||||
|
listPathOfThread(thread),
|
||||||
|
currentSearch,
|
||||||
|
);
|
||||||
const isActive = href === pathname;
|
const isActive = href === pathname;
|
||||||
const isBoot = isBootstrapThread(thread);
|
const isBoot = isBootstrapThread(thread);
|
||||||
const pinned = isThreadPinned(thread);
|
const pinned = isThreadPinned(thread);
|
||||||
@@ -576,7 +596,7 @@ export function GeneralQaSidebar() {
|
|||||||
)}
|
)}
|
||||||
{!query && (
|
{!query && (
|
||||||
<Link
|
<Link
|
||||||
href={MORE_CHATS_HREF}
|
href={moreChatsHref}
|
||||||
className="mt-0.5 flex items-center justify-between rounded-lg px-3 py-1.5 text-[12px] text-muted-foreground/70 transition-colors hover:bg-accent/50 hover:text-foreground"
|
className="mt-0.5 flex items-center justify-between rounded-lg px-3 py-1.5 text-[12px] text-muted-foreground/70 transition-colors hover:bg-accent/50 hover:text-foreground"
|
||||||
>
|
>
|
||||||
<span>更多最近对话</span>
|
<span>更多最近对话</span>
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import test from "node:test";
|
|||||||
|
|
||||||
import {
|
import {
|
||||||
buildStandardChatRedirectPath,
|
buildStandardChatRedirectPath,
|
||||||
|
buildFullChatNavigationPath,
|
||||||
buildEmbeddedIframeRedirectPath,
|
buildEmbeddedIframeRedirectPath,
|
||||||
buildChatsIframeRedirectPath,
|
buildChatsIframeRedirectPath,
|
||||||
buildChatsIframeThreadPath,
|
buildChatsIframeThreadPath,
|
||||||
@@ -280,6 +281,27 @@ test("builds a standard full-chat URL without leaking the legacy marker", () =>
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("keeps full-chat presentation flags when the sidebar opens an agent or thread", () => {
|
||||||
|
assert.equal(
|
||||||
|
buildFullChatNavigationPath(
|
||||||
|
"/page/workspace/chats/new?agent=agent-1",
|
||||||
|
"?hideChrome=1&chatMode=full",
|
||||||
|
),
|
||||||
|
"/page/workspace/chats/new?agent=agent-1&hideChrome=1&chatMode=full",
|
||||||
|
);
|
||||||
|
assert.equal(
|
||||||
|
buildFullChatNavigationPath(
|
||||||
|
"/page/workspace/chats/thread-123",
|
||||||
|
"?embed=1&theme=dark-blue&chatMode=full",
|
||||||
|
),
|
||||||
|
"/page/workspace/chats/thread-123?embed=1&theme=dark-blue&chatMode=full",
|
||||||
|
);
|
||||||
|
assert.equal(
|
||||||
|
buildFullChatNavigationPath("/page/workspace/chats/new", ""),
|
||||||
|
"/page/workspace/chats/new",
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
test("preserves explicit Brainy iframe mode and theme across redirects", () => {
|
test("preserves explicit Brainy iframe mode and theme across redirects", () => {
|
||||||
assert.equal(
|
assert.equal(
|
||||||
buildChatsIframeRedirectPath("?chatMode=iframe&theme=dark-blue&ticket=secret"),
|
buildChatsIframeRedirectPath("?chatMode=iframe&theme=dark-blue&ticket=secret"),
|
||||||
|
|||||||
@@ -107,6 +107,26 @@ export function buildStandardChatRedirectPath(
|
|||||||
return query ? `${pathname}?${query}` : pathname;
|
return query ? `${pathname}?${query}` : pathname;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Keep an explicitly selected standard-chat presentation while navigating
|
||||||
|
* between `/chats/new` and a concrete chat thread. Without this, a click in
|
||||||
|
* the full-chat sidebar drops `hideChrome=1&chatMode=full`; an iframe host
|
||||||
|
* then looks like the legacy bare integration and is incorrectly redirected
|
||||||
|
* to the private AgentSelector workspace.
|
||||||
|
*
|
||||||
|
* This deliberately whitelists presentation flags through
|
||||||
|
* `buildStandardChatRedirectPath` instead of copying arbitrary login/query
|
||||||
|
* values into user-facing chat URLs.
|
||||||
|
*/
|
||||||
|
export function buildFullChatNavigationPath(
|
||||||
|
destinationPath: string,
|
||||||
|
search: string,
|
||||||
|
): string {
|
||||||
|
return hasIframePresentationOverride(search) || isExplicitFullChatMode(search)
|
||||||
|
? buildStandardChatRedirectPath(search, destinationPath)
|
||||||
|
: destinationPath;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Preserve only the presentation parameters consumed by the iframe workspace.
|
* Preserve only the presentation parameters consumed by the iframe workspace.
|
||||||
* Login credentials must never be copied into the post-login address bar.
|
* Login credentials must never be copied into the post-login address bar.
|
||||||
|
|||||||
Reference in new issue
Block a user