功能:完善自定义主题与独立登录会话
This commit is contained in:
1 parent
3779bb69ef
commit
f1e53ef5c0
23 files changed
+1742
-196
No files matched your search
@@ -28,7 +28,7 @@ import {
|
||||
getNodeTitle,
|
||||
} from "@/components/knowledge-graph/graphUtils";
|
||||
import "@/components/knowledge-graph/graphStyles.css";
|
||||
import { useTheme } from "@/contexts/ThemeContext";
|
||||
import { themeToBaseMode, useTheme } from "@/contexts/ThemeContext";
|
||||
import {
|
||||
useGraphBlacklist,
|
||||
useGraphBlacklistActions,
|
||||
@@ -799,7 +799,7 @@ export default function KnowledgeGraphPage() {
|
||||
searchMatchIds={searchMatchIds}
|
||||
layoutType={layoutType}
|
||||
showLabels={showLabels}
|
||||
theme={theme}
|
||||
theme={themeToBaseMode(theme)}
|
||||
onSelectNode={handleSelectNode}
|
||||
onTooltip={setTooltip}
|
||||
onZoomChange={setZoomLevel}
|
||||
|
||||
@@ -35,6 +35,7 @@ import { toast } from "sonner";
|
||||
import { Pagination } from "@/components/ui/pagination";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { useTheme, type ThemeType } from "@/contexts/ThemeContext";
|
||||
import { isCustomThemeId } from "@/core/theme/custom-theme";
|
||||
import { useLightApps, type LightApp } from "@/strategy-components/api/light-apps";
|
||||
import { buildLightAppUrl } from "@/strategy-components/lib/light-app";
|
||||
|
||||
@@ -65,6 +66,17 @@ function hashIndex(str: string, mod: number): number {
|
||||
}
|
||||
|
||||
function getLightAccent(theme: ThemeType) {
|
||||
if (isCustomThemeId(theme)) {
|
||||
return {
|
||||
dot: "bg-[var(--primary)]",
|
||||
badge: "border-[var(--border)] bg-[var(--accent)] text-[var(--accent-foreground)]",
|
||||
focus: "focus:border-[var(--brand-900)] focus:ring-[var(--ring)]",
|
||||
text: "text-[var(--accent-foreground)]",
|
||||
hoverText: "group-hover:text-[var(--accent-foreground)]",
|
||||
link: "text-[var(--accent-foreground)] hover:text-[var(--brand-950)]",
|
||||
button: "bg-[var(--primary)] text-[var(--button-foreground,var(--primary-foreground))] shadow-sm hover:bg-[var(--theme-button-hover)]",
|
||||
};
|
||||
}
|
||||
if (theme === "lightgreen") {
|
||||
return {
|
||||
dot: "bg-[#07C05F]",
|
||||
|
||||
@@ -90,12 +90,14 @@ export default function LoginPage() {
|
||||
() => readEmbedParam(searchParams.toString()) === 'on',
|
||||
[searchParams],
|
||||
);
|
||||
// External consumers may still send `from=iframe` for the legacy protocol
|
||||
// chat. `chatMode=full` is an explicit escape hatch: always use the regular
|
||||
// full ChatPage instead, which is the route expected by ordinary iframe
|
||||
// nesting and browser deep links.
|
||||
// Wujie is an application container, not the Brainy iframe protocol. It
|
||||
// must therefore land on the regular full ChatPage even when an old portal
|
||||
// URL still carries `from=iframe`. Plain embeds can request the same route
|
||||
// explicitly with `chatMode=full`.
|
||||
const fullChatRequested = useMemo(
|
||||
() => searchParams.get('chatMode')?.trim().toLowerCase() === FULL_CHAT_MODE,
|
||||
() =>
|
||||
isWujieEnv() ||
|
||||
searchParams.get('chatMode')?.trim().toLowerCase() === FULL_CHAT_MODE,
|
||||
[searchParams],
|
||||
);
|
||||
const redirectPath = useMemo(
|
||||
@@ -148,6 +150,15 @@ export default function LoginPage() {
|
||||
const normalized = raw.startsWith('/') ? raw : `/${raw}`;
|
||||
const [pathOnly, ownQuery = ''] = normalized.split('?');
|
||||
const target = new URLSearchParams(ownQuery);
|
||||
// Keep the Wujie/full-chat decision on the target URL. The destination
|
||||
// route uses it to retain the full agent/history workspace sidebar.
|
||||
if (
|
||||
fullChatRequested &&
|
||||
pathOnly === DEFAULT_REDIRECT_PATH &&
|
||||
!target.has('chatMode')
|
||||
) {
|
||||
target.set('chatMode', FULL_CHAT_MODE);
|
||||
}
|
||||
// 登录流程自身消费的参数,不透传到目标页(尤以鉴权类为甚)。
|
||||
const LOGIN_OWNED = new Set([
|
||||
'redirect', 'route', 'ticket', 'userId', 'env', 'theme', 'yUserId',
|
||||
@@ -160,7 +171,7 @@ export default function LoginPage() {
|
||||
});
|
||||
const qs = target.toString();
|
||||
return qs ? `${pathOnly}?${qs}` : pathOnly;
|
||||
}, [searchParams]);
|
||||
}, [fullChatRequested, searchParams]);
|
||||
const externalParams = useMemo(
|
||||
() => ({
|
||||
theme: searchParams.get('theme') ?? '',
|
||||
|
||||
@@ -2,6 +2,7 @@ import type { CSSProperties, ReactNode } from 'react';
|
||||
import { Navigate, Route, Routes, useLocation, useParams } from 'react-router-dom';
|
||||
|
||||
import { getStoredAuth } from '@/core/auth';
|
||||
import { isWujieEnv } from '@/core/embed/wujie';
|
||||
|
||||
import { SidebarInset, SidebarProvider } from '@/components/ui/sidebar';
|
||||
import { PromptInputProvider } from '@/components/ai-elements/prompt-input';
|
||||
@@ -254,6 +255,66 @@ function NewChatEntryRoute({
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* `chats_iframe` is reserved for the Brainy postMessage protocol. Some legacy
|
||||
* Wujie links still target it (or carry `from=iframe` during login), but Wujie
|
||||
* needs the normal full chat workspace instead. Redirect it before mounting
|
||||
* IframeChatPage; `chatMode=iframe` remains an explicit opt-in escape hatch
|
||||
* for a Wujie integration that really implements that protocol.
|
||||
*/
|
||||
function IframeChatEntryRoute({
|
||||
layoutProps,
|
||||
hasThreadParam,
|
||||
}: {
|
||||
layoutProps: {
|
||||
pageSidebarCollapsed: boolean;
|
||||
pageSidebarHidden: boolean;
|
||||
workspaceSidebarHidden: boolean;
|
||||
headerHidden: boolean;
|
||||
compactWorkspaceSidebar: boolean;
|
||||
compactActionsHidden: boolean;
|
||||
pageSidebarDark: boolean;
|
||||
forceWorkspaceSidebarCollapsed: boolean;
|
||||
};
|
||||
hasThreadParam: boolean;
|
||||
}) {
|
||||
const location = useLocation();
|
||||
const { thread_id } = useParams<{ thread_id: string }>();
|
||||
const requestedMode = new URLSearchParams(location.search)
|
||||
.get('chatMode')
|
||||
?.trim()
|
||||
.toLowerCase();
|
||||
|
||||
if (isWujieEnv() && requestedMode !== 'iframe') {
|
||||
const targetParams = new URLSearchParams(location.search);
|
||||
targetParams.set('chatMode', 'full');
|
||||
const targetPath = hasThreadParam && thread_id
|
||||
? `/page/workspace/chats/${thread_id}`
|
||||
: '/page/workspace/chats/new';
|
||||
const targetSearch = targetParams.toString();
|
||||
return (
|
||||
<Navigate
|
||||
to={targetSearch ? `${targetPath}?${targetSearch}` : targetPath}
|
||||
replace
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<WorkspaceLayout
|
||||
{...layoutProps}
|
||||
pageSidebarHidden
|
||||
workspaceSidebarHidden
|
||||
wrapperOverflowHidden={hasThreadParam}
|
||||
insetOverflowHidden={hasThreadParam}
|
||||
>
|
||||
<ChatRuntime>
|
||||
<IframeChatPage />
|
||||
</ChatRuntime>
|
||||
</WorkspaceLayout>
|
||||
);
|
||||
}
|
||||
|
||||
export default function WorkspaceRoutes({
|
||||
pageSidebarCollapsed = false,
|
||||
pageSidebarHidden = false,
|
||||
@@ -312,33 +373,11 @@ export default function WorkspaceRoutes({
|
||||
/>
|
||||
<Route
|
||||
path="chats_iframe/new"
|
||||
element={
|
||||
<WorkspaceLayout
|
||||
{...layoutProps}
|
||||
pageSidebarHidden
|
||||
workspaceSidebarHidden
|
||||
>
|
||||
<ChatRuntime>
|
||||
<IframeChatPage />
|
||||
</ChatRuntime>
|
||||
</WorkspaceLayout>
|
||||
}
|
||||
element={<IframeChatEntryRoute layoutProps={layoutProps} hasThreadParam={false} />}
|
||||
/>
|
||||
<Route
|
||||
path="chats_iframe/:thread_id"
|
||||
element={
|
||||
<WorkspaceLayout
|
||||
{...layoutProps}
|
||||
pageSidebarHidden
|
||||
workspaceSidebarHidden
|
||||
wrapperOverflowHidden
|
||||
insetOverflowHidden
|
||||
>
|
||||
<ChatRuntime>
|
||||
<IframeChatPage />
|
||||
</ChatRuntime>
|
||||
</WorkspaceLayout>
|
||||
}
|
||||
element={<IframeChatEntryRoute layoutProps={layoutProps} hasThreadParam />}
|
||||
/>
|
||||
<Route
|
||||
path="agents"
|
||||
|
||||
Reference in new issue
Block a user