Merge branch 'master' of http://192.168.0.200/knowledge-base-bj/deerflow-code into gzy_dev_new
This commit is contained in:
commit
fb73ae0471
220 files changed
+30857
-1735
No files matched your search
@@ -37,6 +37,7 @@ 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 { isHideChromeActive } from "@/core/embed/embed-session";
|
||||
import { useSidebar } from "@/components/ui/sidebar";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { PanelLeftOpen } from "lucide-react";
|
||||
@@ -100,6 +101,11 @@ export default function ChatPage() {
|
||||
const router = useRouter();
|
||||
const searchParams = useSearchParams();
|
||||
const pathname = usePathname();
|
||||
// `chatMode=full` is used by an embedded standard chat. It intentionally
|
||||
// overrides the user's compact-layout preference for this route only, so
|
||||
// the page keeps the full landing composition and its agent/history sidebar.
|
||||
const fullChatMode =
|
||||
searchParams.get("chatMode")?.trim().toLowerCase() === "full";
|
||||
const { open: historySidebarOpen, toggleSidebar: toggleHistorySidebar } =
|
||||
useSidebar();
|
||||
const { t } = useI18n();
|
||||
@@ -451,7 +457,7 @@ export default function ChatPage() {
|
||||
// 主题底图(workspace-chat-bg)不生效。非紧凑(嵌入/standalone)保持原样。
|
||||
const layoutMode = effectiveLayoutMode;
|
||||
const chromeByPreference =
|
||||
layoutMode === "full"
|
||||
fullChatMode || layoutMode === "full"
|
||||
? true
|
||||
: layoutMode === "compact"
|
||||
? false
|
||||
@@ -459,6 +465,7 @@ export default function ChatPage() {
|
||||
const compactChrome =
|
||||
chromeByPreference && !isChatsIframeWorkspacePath(pathname);
|
||||
const compactLanding = isLandingState && compactChrome;
|
||||
const compactActionsHidden = isHideChromeActive();
|
||||
|
||||
return (
|
||||
<ThreadContext.Provider value={{ thread, isMock }}>
|
||||
@@ -483,7 +490,7 @@ export default function ChatPage() {
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
{/* 仅在收起态显示「展开历史」钮;展开后由侧栏内(新对话右侧)的收起钮负责收起。 */}
|
||||
{compactChrome && !historySidebarOpen && (
|
||||
{compactChrome && !historySidebarOpen && !compactActionsHidden && (
|
||||
<Button
|
||||
type="button"
|
||||
variant="ghost"
|
||||
@@ -507,7 +514,7 @@ export default function ChatPage() {
|
||||
与全量模式 Header 右上角保持一致。全量模式已有顶栏则不重复展示。 */}
|
||||
{/* 简洁模式无全局顶栏:把换肤 + 界面模式切换搬到页面级顶栏右上角,
|
||||
与全量模式 Header 右上角保持一致(content 配色贴浅底顶栏)。 */}
|
||||
{!compactChrome && (
|
||||
{!compactChrome && !compactActionsHidden && (
|
||||
<>
|
||||
<CompactShortcutButtons tone="content" />
|
||||
<ThemeSwitcher tone="content" />
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -21,7 +21,17 @@ export default function LightAppIframeView() {
|
||||
// 把新主题色传给被嵌套应用(不依赖 <html> 的 dark class,避免时序读旧值)。
|
||||
// 深色=dark-blue,浅蓝=blue,其余(红白)=red。
|
||||
const themeColor: AppThemeColor =
|
||||
theme === "dark" ? "dark-blue" : theme === "lightblue" ? "blue" : "red";
|
||||
theme === "dark"
|
||||
? "dark-blue"
|
||||
: theme === "lightgreen"
|
||||
? "green"
|
||||
: theme === "lightblue"
|
||||
? "blue"
|
||||
: theme === "iceblue"
|
||||
? "iceblue"
|
||||
: theme === "lightyellow"
|
||||
? "lightyellow"
|
||||
: "red";
|
||||
const src = useMemo(
|
||||
() => (app ? buildLightAppUrl(app, undefined, themeColor) : ""),
|
||||
[app, themeColor],
|
||||
|
||||
@@ -34,7 +34,8 @@ import { toast } from "sonner";
|
||||
|
||||
import { Pagination } from "@/components/ui/pagination";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { useTheme } from "@/contexts/ThemeContext";
|
||||
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";
|
||||
|
||||
@@ -64,12 +65,79 @@ function hashIndex(str: string, mod: number): number {
|
||||
return mod > 0 ? h % mod : 0;
|
||||
}
|
||||
|
||||
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]",
|
||||
badge: "border-[#07C05F]/15 bg-[#E9F8EC] text-[#049B38]",
|
||||
focus: "focus:border-[#07C05F]/60 focus:ring-[#07C05F]/20",
|
||||
text: "text-[#049B38]",
|
||||
hoverText: "group-hover:text-[#049B38]",
|
||||
link: "text-[#049B38] hover:text-[#038626]",
|
||||
button: "bg-[#07C05F] shadow-[0_1px_3px_rgba(7,192,95,0.2)] hover:bg-[#08DD6E]",
|
||||
};
|
||||
}
|
||||
if (theme === "lightblue") {
|
||||
return {
|
||||
dot: "bg-[#0062D9]",
|
||||
badge: "border-[#0062D9]/15 bg-[#EAF4FF] text-[#0062D9]",
|
||||
focus: "focus:border-[#0062D9]/60 focus:ring-[#0062D9]/20",
|
||||
text: "text-[#0062D9]",
|
||||
hoverText: "group-hover:text-[#0062D9]",
|
||||
link: "text-[#0062D9] hover:text-[#0056C6]",
|
||||
button: "bg-[#0062D9] shadow-[0_1px_3px_rgba(0,98,217,0.2)] hover:bg-[#0079FE]",
|
||||
};
|
||||
}
|
||||
if (theme === "iceblue") {
|
||||
return {
|
||||
dot: "bg-[#9BCEF1]",
|
||||
badge: "border-[#3D6E96]/15 bg-[#E1F1FB] text-[#2D587A]",
|
||||
focus: "focus:border-[#3D6E96]/60 focus:ring-[#3D6E96]/20",
|
||||
text: "text-[#2D587A]",
|
||||
hoverText: "group-hover:text-[#2D587A]",
|
||||
link: "text-[#2D587A] hover:text-[#1E405C]",
|
||||
button: "bg-[#9BCEF1] text-[#18324A] shadow-[0_1px_3px_rgba(155,206,241,0.42)] hover:bg-[#B7DCF5]",
|
||||
};
|
||||
}
|
||||
if (theme === "lightyellow") {
|
||||
return {
|
||||
dot: "bg-[#F3FBD9]",
|
||||
badge: "border-[#4C6330]/15 bg-[#EEF6D4] text-[#4C6330]",
|
||||
focus: "focus:border-[#4C6330]/60 focus:ring-[#F3FBD9]/40",
|
||||
text: "text-[#4C6330]",
|
||||
hoverText: "group-hover:text-[#4C6330]",
|
||||
link: "text-[#4C6330] hover:text-[#34451F]",
|
||||
button: "bg-[#F3FBD9] text-[#303D23] shadow-[0_1px_3px_rgba(243,251,217,0.72)] hover:bg-[#E4EFBF]",
|
||||
};
|
||||
}
|
||||
return {
|
||||
dot: "bg-[#CF1322]",
|
||||
badge: "border-[#CF1322]/15 bg-[#FFF1F0] text-[#CF1322]",
|
||||
focus: "focus:border-[#CF1322]/60 focus:ring-[#CF1322]/20",
|
||||
text: "text-[#CF1322]",
|
||||
hoverText: "group-hover:text-[#CF1322]",
|
||||
link: "text-[#CF1322] hover:text-[#A8071A]",
|
||||
button: "bg-[#CF1322] shadow-[0_1px_3px_rgba(207,19,34,0.2)] hover:bg-[#A8071A]",
|
||||
};
|
||||
}
|
||||
|
||||
export default function LightAppListPage() {
|
||||
const [search, setSearch] = useState("");
|
||||
const [page, setPage] = useState(1);
|
||||
const [copiedId, setCopiedId] = useState<string | null>(null);
|
||||
const { theme } = useTheme();
|
||||
const isLightBlue = theme === 'lightblue';
|
||||
const lightAccent = getLightAccent(theme);
|
||||
|
||||
const { data: apps = [], isLoading } = useLightApps({
|
||||
appType: "window_open",
|
||||
@@ -118,13 +186,13 @@ export default function LightAppListPage() {
|
||||
<div className="space-y-1">
|
||||
<div className="flex items-center gap-2.5">
|
||||
<span className="relative flex size-3">
|
||||
<span className={cn("absolute inline-flex size-full animate-ping rounded-full opacity-75", isLightBlue ? "bg-[#0062D9]" : "bg-[#CF1322]", "dark:bg-[#1677FF]")} />
|
||||
<span className={cn("relative inline-flex size-3 rounded-full", isLightBlue ? "bg-[#0062D9]" : "bg-[#CF1322]", "dark:bg-[#1677FF]")} />
|
||||
<span className={cn("absolute inline-flex size-full animate-ping rounded-full opacity-75", lightAccent.dot, "dark:bg-[#1677FF]")} />
|
||||
<span className={cn("relative inline-flex size-3 rounded-full", lightAccent.dot, "dark:bg-[#1677FF]")} />
|
||||
</span>
|
||||
<h1 className="text-2xl font-bold tracking-tight text-zinc-900 dark:text-[#E8EDF5]">
|
||||
应用列表
|
||||
</h1>
|
||||
<span className={cn("rounded-[5px] border px-2 py-0.5 font-mono text-xs font-medium dark:border-[#1677FF]/30 dark:bg-[#1677FF]/15 dark:text-[#4096FF]", isLightBlue ? "border-[#0062D9]/15 bg-[#EAF4FF] text-[#0062D9]" : "border-[#CF1322]/15 bg-[#FFF1F0] text-[#CF1322]")}>
|
||||
<span className={cn("rounded-[5px] border px-2 py-0.5 font-mono text-xs font-medium dark:border-[#1677FF]/30 dark:bg-[#1677FF]/15 dark:text-[#4096FF]", lightAccent.badge)}>
|
||||
导航工作台
|
||||
</span>
|
||||
</div>
|
||||
@@ -142,7 +210,7 @@ export default function LightAppListPage() {
|
||||
value={search}
|
||||
onChange={(e) => setSearch(e.target.value)}
|
||||
placeholder="键入应用名称、目标地址、或描述,实时过滤检索…"
|
||||
className={cn("w-full rounded-[5px] border border-zinc-200 bg-zinc-50/50 py-3 pl-10 pr-10 text-sm transition focus:bg-white focus:outline-none focus:ring-2 dark:border-[#1A2F5A] dark:bg-[#0C1836] dark:text-[#C5CFE0] dark:placeholder-[#4A6491] dark:focus:border-[#1677FF]/60 dark:focus:bg-[#122347] dark:focus:ring-[#1677FF]/25", isLightBlue ? "focus:border-[#0062D9]/60 focus:ring-[#0062D9]/20" : "focus:border-[#CF1322]/60 focus:ring-[#CF1322]/20")}
|
||||
className={cn("w-full rounded-[5px] border border-zinc-200 bg-zinc-50/50 py-3 pl-10 pr-10 text-sm transition focus:bg-white focus:outline-none focus:ring-2 dark:border-[#1A2F5A] dark:bg-[#0C1836] dark:text-[#C5CFE0] dark:placeholder-[#4A6491] dark:focus:border-[#1677FF]/60 dark:focus:bg-[#122347] dark:focus:ring-[#1677FF]/25", lightAccent.focus)}
|
||||
/>
|
||||
{search && (
|
||||
<button
|
||||
@@ -161,13 +229,13 @@ export default function LightAppListPage() {
|
||||
<div className="-mt-2 flex items-center justify-between">
|
||||
<div className="text-xs font-medium text-zinc-500 dark:text-[#6E88B0]">
|
||||
检索到最终结果:{" "}
|
||||
<span className={cn("font-bold dark:text-[#4096FF]", isLightBlue ? "text-[#0062D9]" : "text-[#CF1322]")}>{apps.length}</span> 个关联应用
|
||||
<span className={cn("font-bold dark:text-[#4096FF]", lightAccent.text)}>{apps.length}</span> 个关联应用
|
||||
</div>
|
||||
{search && (
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setSearch("")}
|
||||
className={cn("text-xs font-medium underline dark:text-[#4096FF] dark:hover:text-[#1677FF]", isLightBlue ? "text-[#0062D9] hover:text-[#0056C6]" : "text-[#CF1322] hover:text-[#A8071A]")}
|
||||
className={cn("text-xs font-medium underline dark:text-[#4096FF] dark:hover:text-[#1677FF]", lightAccent.link)}
|
||||
>
|
||||
重置过滤条件
|
||||
</button>
|
||||
@@ -239,7 +307,7 @@ function AppCard({
|
||||
onOpen: () => void;
|
||||
}) {
|
||||
const { theme } = useTheme();
|
||||
const isLightBlue = theme === 'lightblue';
|
||||
const lightAccent = getLightAccent(theme);
|
||||
const Icon = ICON_POOL[hashIndex(app.appId, ICON_POOL.length)];
|
||||
const cardTheme = THEME_POOL[hashIndex(app.appId, THEME_POOL.length)];
|
||||
|
||||
@@ -256,7 +324,7 @@ function AppCard({
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h4 className={cn("mb-2 line-clamp-1 text-base font-bold leading-snug text-zinc-900 transition-colors dark:text-[#E8EDF5] dark:group-hover:text-[#4096FF]", isLightBlue ? "group-hover:text-[#0062D9]" : "group-hover:text-[#CF1322]")}>
|
||||
<h4 className={cn("mb-2 line-clamp-1 text-base font-bold leading-snug text-zinc-900 transition-colors dark:text-[#E8EDF5] dark:group-hover:text-[#4096FF]", lightAccent.hoverText)}>
|
||||
{app.name}
|
||||
</h4>
|
||||
|
||||
@@ -270,7 +338,7 @@ function AppCard({
|
||||
<div className="mb-4 rounded-[5px] bg-zinc-50/70 p-3 dark:bg-[#0C1836]">
|
||||
<div className="mb-1 flex items-center justify-between text-[10px] font-semibold text-zinc-400 dark:text-[#6E88B0]">
|
||||
<span>拼接后目标路由终端:</span>
|
||||
<span className={cn("text-[9px] font-bold uppercase tracking-wider dark:text-[#4096FF]", isLightBlue ? "text-[#0062D9]" : "text-[#CF1322]")}>
|
||||
<span className={cn("text-[9px] font-bold uppercase tracking-wider dark:text-[#4096FF]", lightAccent.text)}>
|
||||
自动拼接已激活参
|
||||
</span>
|
||||
</div>
|
||||
@@ -312,7 +380,7 @@ function AppCard({
|
||||
<button
|
||||
type="button"
|
||||
onClick={onOpen}
|
||||
className={cn("flex items-center justify-center gap-1.5 rounded-[5px] px-3 py-2 text-xs font-semibold text-white transition active:scale-[0.98] dark:bg-[#1677FF] dark:shadow-none dark:hover:bg-[#0958D9]", isLightBlue ? "bg-[#0062D9] shadow-[0_1px_3px_rgba(0,98,217,0.2)] hover:bg-[#0079FE]" : "bg-[#CF1322] shadow-[0_1px_3px_rgba(207,19,34,0.2)] hover:bg-[#A8071A]")}
|
||||
className={cn("flex items-center justify-center gap-1.5 rounded-[5px] px-3 py-2 text-xs font-semibold text-white transition active:scale-[0.98] dark:bg-[#1677FF] dark:text-white dark:shadow-none dark:hover:bg-[#0958D9]", lightAccent.button)}
|
||||
>
|
||||
<span>进入应用</span>
|
||||
<ArrowUpRight className="size-3.5" />
|
||||
|
||||
@@ -93,7 +93,7 @@ const newParam = (): LightAppRouteParam => ({
|
||||
paramType: "login",
|
||||
source: "access_token",
|
||||
customValue: "",
|
||||
themeValue: "red=red;blue=blue;dark-blue=dark-blue",
|
||||
themeValue: "red=red;green=green;blue=blue;dark-blue=dark-blue",
|
||||
});
|
||||
|
||||
function formFromApp(app: LightApp): FormState {
|
||||
|
||||
@@ -9,7 +9,9 @@ import { cn } from "@/lib/utils";
|
||||
export default function LightAppNoteQAPage() {
|
||||
const { theme } = useTheme();
|
||||
const isDark = theme === "dark";
|
||||
const isLightBlue = theme === 'lightblue';
|
||||
const isLightBlue = theme === 'lightblue' || theme === 'iceblue';
|
||||
const isLightGreen = theme === "lightgreen";
|
||||
const isLightYellow = theme === "lightyellow";
|
||||
const [question, setQuestion] = useState("");
|
||||
|
||||
const handleSubmit = () => {
|
||||
@@ -30,7 +32,9 @@ export default function LightAppNoteQAPage() {
|
||||
style={{
|
||||
background: isDark
|
||||
? "radial-gradient(ellipse 60% 50% at 50% 46%, rgba(59,130,246,0.10) 0%, rgba(139,92,246,0.06) 50%, transparent 70%)"
|
||||
: "radial-gradient(ellipse 60% 50% at 50% 46%, rgba(147,197,253,0.38) 0%, rgba(165,180,252,0.18) 50%, transparent 70%)",
|
||||
: isLightYellow
|
||||
? "radial-gradient(ellipse 60% 50% at 50% 46%, rgba(243,251,217,0.7) 0%, rgba(220,235,173,0.26) 50%, transparent 70%)"
|
||||
: "radial-gradient(ellipse 60% 50% at 50% 46%, rgba(147,197,253,0.38) 0%, rgba(165,180,252,0.18) 50%, transparent 70%)",
|
||||
}}
|
||||
/>
|
||||
|
||||
@@ -42,7 +46,11 @@ export default function LightAppNoteQAPage() {
|
||||
<div
|
||||
className={cn(
|
||||
"flex h-12 w-10 shrink-0 items-center justify-center rounded shadow-sm",
|
||||
isDark ? "bg-violet-400 text-slate-950" : "bg-[#4d3f74] text-white",
|
||||
isDark
|
||||
? "bg-violet-400 text-slate-950"
|
||||
: isLightYellow
|
||||
? "bg-[#4C6330] text-white"
|
||||
: "bg-[#4d3f74] text-white",
|
||||
)}
|
||||
>
|
||||
<BookOpen className="h-6 w-6" />
|
||||
@@ -65,7 +73,9 @@ export default function LightAppNoteQAPage() {
|
||||
"flex items-center gap-1.5 rounded-full px-4 py-1 text-sm font-medium transition-colors",
|
||||
isDark
|
||||
? "bg-sky-500/20 text-sky-300 hover:bg-sky-500/30"
|
||||
: "bg-sky-100 text-sky-600 hover:bg-sky-200",
|
||||
: isLightYellow
|
||||
? "bg-[#EEF6D4] text-[#4C6330] hover:bg-[#E4EFBF]"
|
||||
: "bg-sky-100 text-sky-600 hover:bg-sky-200",
|
||||
)}
|
||||
>
|
||||
<Plus className="h-3.5 w-3.5" />
|
||||
@@ -112,7 +122,11 @@ export default function LightAppNoteQAPage() {
|
||||
"flex h-7 w-7 shrink-0 items-center justify-center rounded-full transition-colors",
|
||||
isDark
|
||||
? "bg-sky-500 text-white hover:bg-sky-400"
|
||||
: isLightBlue
|
||||
: isLightGreen
|
||||
? "bg-[#07C05F] text-white hover:bg-[#08DD6E]"
|
||||
: isLightYellow
|
||||
? "bg-[#F3FBD9] text-[#303D23] hover:bg-[#E4EFBF]"
|
||||
: isLightBlue
|
||||
? "bg-[#0062D9] text-white hover:bg-[#0079FE]"
|
||||
: "bg-[#F5222D] text-white hover:bg-[#CF1322]",
|
||||
)}
|
||||
|
||||
@@ -71,6 +71,7 @@ function summarizeStoredUserInfo(): Record<string, unknown> | null {
|
||||
|
||||
const DEFAULT_USERNAME = 'guest';
|
||||
const DEFAULT_REDIRECT_PATH = '/page/workspace/chats/new';
|
||||
const FULL_CHAT_MODE = 'full';
|
||||
|
||||
type LoginStatus = 'loading' | 'success' | 'error';
|
||||
|
||||
@@ -91,10 +92,21 @@ export default function LoginPage() {
|
||||
() => readEmbedParam(searchParams.toString()) === 'on',
|
||||
[searchParams],
|
||||
);
|
||||
const isIframeLogin = useMemo(
|
||||
() => isChatsIframeLogin(searchParams.toString()),
|
||||
// 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(
|
||||
() =>
|
||||
isWujieEnv() ||
|
||||
searchParams.get('chatMode')?.trim().toLowerCase() === FULL_CHAT_MODE,
|
||||
[searchParams],
|
||||
);
|
||||
const isIframeLogin = useMemo(
|
||||
() =>
|
||||
!fullChatRequested && isChatsIframeLogin(searchParams.toString()),
|
||||
[fullChatRequested, searchParams],
|
||||
);
|
||||
const resolvedUsername = useMemo(() => {
|
||||
const raw = username?.trim();
|
||||
return raw && raw.length > 0 ? raw : DEFAULT_USERNAME;
|
||||
@@ -103,12 +115,20 @@ export default function LoginPage() {
|
||||
// ticket/userId/env 仍按既有登录存储策略持久化。
|
||||
const yUserId = searchParams.get('yUserId')?.trim() ?? '';
|
||||
const redirectPath = useMemo(
|
||||
() =>
|
||||
resolvePostLoginRedirectPath(
|
||||
() => {
|
||||
if (yUserId) return GENERAL_QA_ROUTE;
|
||||
// Keep the mode on the destination URL so PageRoutes can retain the
|
||||
// full chat workspace sidebar while hideChrome hides only the global
|
||||
// application chrome.
|
||||
if (fullChatRequested) {
|
||||
return `${DEFAULT_REDIRECT_PATH}?chatMode=${FULL_CHAT_MODE}`;
|
||||
}
|
||||
return resolvePostLoginRedirectPath(
|
||||
searchParams.toString(),
|
||||
yUserId ? GENERAL_QA_ROUTE : DEFAULT_REDIRECT_PATH,
|
||||
),
|
||||
[searchParams, yUserId],
|
||||
DEFAULT_REDIRECT_PATH,
|
||||
);
|
||||
},
|
||||
[fullChatRequested, searchParams, yUserId],
|
||||
);
|
||||
const toolToken = searchParams.get('token')?.trim() ?? '';
|
||||
// 内网登录透传的访问令牌:存入 userInfo.accessToken,供 XK/PG 中任务 iframe 拼参
|
||||
@@ -131,15 +151,27 @@ export default function LoginPage() {
|
||||
// taskId,无需把它编进 redirect 值里。目标已自带的同名参数优先(不覆盖);登录鉴权类参数
|
||||
// (token/authToken/ticket 等)一律**不透传**,避免泄漏到应用页地址栏。
|
||||
const redirectTo = useMemo(() => {
|
||||
const raw = searchParams.get('redirect')?.trim() ?? '';
|
||||
const raw =
|
||||
searchParams.get('redirect')?.trim() ||
|
||||
searchParams.get('route')?.trim() ||
|
||||
'';
|
||||
if (!raw) return '';
|
||||
if (/^[a-z][a-z0-9+.-]*:/i.test(raw) || raw.startsWith('//') || raw.includes('\\')) return '';
|
||||
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', 'ticket', 'userId', 'env', 'theme', 'yUserId',
|
||||
'redirect', 'route', 'ticket', 'userId', 'env', 'theme', 'yUserId',
|
||||
'token', 'access_token', 'password', 'authToken', 'goPath', 'from',
|
||||
]);
|
||||
new URLSearchParams(searchParams.toString()).forEach((value, key) => {
|
||||
@@ -149,7 +181,7 @@ export default function LoginPage() {
|
||||
});
|
||||
const qs = target.toString();
|
||||
return qs ? `${pathOnly}?${qs}` : pathOnly;
|
||||
}, [searchParams]);
|
||||
}, [fullChatRequested, searchParams]);
|
||||
const externalParams = useMemo(
|
||||
() => ({
|
||||
theme: searchParams.get('theme') ?? '',
|
||||
|
||||
@@ -4,7 +4,7 @@ import AIWritingPage from '@/open-canvas/pages/AIWritingPage';
|
||||
import { getNotebookApiBaseURL } from '@/core/config';
|
||||
import { useTheme, type ThemeType } from '@/contexts/ThemeContext';
|
||||
|
||||
const VALID_THEMES: ThemeType[] = ['dark', 'light', 'lightblue'];
|
||||
const VALID_THEMES: ThemeType[] = ['dark', 'light', 'lightblue', 'lightgreen', 'iceblue', 'lightyellow'];
|
||||
|
||||
interface NotebookTokenData {
|
||||
doc_ids: number[];
|
||||
|
||||
@@ -9,6 +9,7 @@ import { isWujieEnv } from "@/core/embed/wujie";
|
||||
import {
|
||||
isEmbedActive,
|
||||
isChromeHidden,
|
||||
isHideChromeActive,
|
||||
readEmbedParam,
|
||||
readHideChromeParam,
|
||||
} from "@/core/embed/embed-session";
|
||||
@@ -16,6 +17,7 @@ import { useTheme } from "@/contexts/ThemeContext";
|
||||
import { useLocation } from "react-router-dom";
|
||||
import { useActiveThirdLevel } from "@/core/page-layout/use-sidebar-menu";
|
||||
import { useLayoutMode } from "@/core/system-settings/hooks";
|
||||
import { env } from "@/env";
|
||||
|
||||
import WorkspaceRoutes from "./WorkspaceRoutes";
|
||||
import StrategyRoutes from "./StrategyRoutes";
|
||||
@@ -27,6 +29,7 @@ import { RoundtableJobNotifier } from "@/roundtable-planning/components/Roundtab
|
||||
|
||||
function PageLayout() {
|
||||
const [collapsed, setCollapsed] = useState(false);
|
||||
const forceCollapsedSidebar = env.FORCE_COLLAPSED_SIDEBAR;
|
||||
const sidebarEnabled = useMemo(() => readSidebarEnabled(), []);
|
||||
const location = useLocation();
|
||||
const { theme } = useTheme();
|
||||
@@ -42,7 +45,13 @@ function PageLayout() {
|
||||
// 登录时置位)这一旧默认。这样无论是否走门户登录,「全量↔简洁」都能真正控制顶栏+侧边栏。
|
||||
const [layoutMode] = useLayoutMode();
|
||||
const chromeByPreference =
|
||||
layoutMode === "full" ? true : layoutMode === "compact" ? false : sidebarEnabled;
|
||||
forceCollapsedSidebar
|
||||
? false
|
||||
: layoutMode === "full"
|
||||
? true
|
||||
: layoutMode === "compact"
|
||||
? false
|
||||
: sidebarEnabled;
|
||||
// iframe 嵌入模式(外部系统 `&embed=1`,按标签页 sessionStorage):去掉全局导航栏 + 侧边栏,
|
||||
// 只留问答/会商工作区本体。按 location 重算以跟随页内导航;非嵌入标签页恒为 false,不受影响。
|
||||
const embedActive = useMemo(
|
||||
@@ -56,11 +65,29 @@ function PageLayout() {
|
||||
() => isChromeHidden() || readHideChromeParam(location.search) === "on",
|
||||
[location.pathname, location.search],
|
||||
);
|
||||
const hideChromeActive = useMemo(
|
||||
() => isHideChromeActive() || readHideChromeParam(location.search) === "on",
|
||||
[location.pathname, location.search],
|
||||
);
|
||||
// `chatMode=full` is the embed-friendly standard chat view: hide the global
|
||||
// Header/PageSidebar when `hideChrome=1`, but keep WorkspaceSidebar so users
|
||||
// can still pick an agent and access their common agents/history.
|
||||
const fullChatMode = useMemo(() => {
|
||||
const mode = new URLSearchParams(location.search)
|
||||
.get("chatMode")
|
||||
?.trim()
|
||||
.toLowerCase();
|
||||
return (
|
||||
mode === "full" &&
|
||||
location.pathname.startsWith("/page/workspace/chats/")
|
||||
);
|
||||
}, [location.pathname, location.search]);
|
||||
// 「大屏绘制」独立页(/page/strategy/dashboard-agent)本就是「带导航栏 + 侧边栏」的独立页,
|
||||
// 但它常被深链「上一步」从去 chrome 的工作区/新标签打开——那里 chromeByPreference 可能为 false
|
||||
// (界面模式非全量、或会话级 sidebar 未置位),导致整页没有顶栏/侧栏。故对该路由**强制全局 chrome**
|
||||
// (仍尊重 embed=1 嵌入态:外部 iframe 套用时仍隐藏)。
|
||||
const forceChromeForDashboardAgent = location.pathname.includes("/strategy/dashboard-agent");
|
||||
const forceChromeForDashboardAgent =
|
||||
!forceCollapsedSidebar && location.pathname.includes("/strategy/dashboard-agent");
|
||||
const pageSidebarAllowed =
|
||||
(forceChromeForDashboardAgent || chromeByPreference) &&
|
||||
!isChatsIframeWorkspacePath(location.pathname) &&
|
||||
@@ -111,9 +138,17 @@ function PageLayout() {
|
||||
<WorkspaceRoutes
|
||||
pageSidebarCollapsed={collapsed}
|
||||
pageSidebarHidden={!pageSidebarAllowed}
|
||||
workspaceSidebarHidden={hideChromeActive && !fullChatMode}
|
||||
headerHidden={!pageSidebarAllowed}
|
||||
compactWorkspaceSidebar={pageSidebarAllowed}
|
||||
// Full-chat embedding has no global PageSidebar, but it
|
||||
// still needs the full GeneralQaSidebar (common agents and
|
||||
// conversation history) inside the workspace.
|
||||
compactWorkspaceSidebar={pageSidebarAllowed || fullChatMode}
|
||||
compactActionsHidden={hideChromeActive}
|
||||
pageSidebarDark={isDark}
|
||||
forceWorkspaceSidebarCollapsed={
|
||||
forceCollapsedSidebar && !fullChatMode
|
||||
}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
|
||||
@@ -793,7 +793,7 @@ export default function ScheduledTasksPage({
|
||||
</Button>
|
||||
<Button
|
||||
size="icon"
|
||||
variant="destructive"
|
||||
variant={theme === "iceblue" || theme === "lightyellow" ? "default" : "destructive"}
|
||||
title="删除"
|
||||
onClick={(event) => {
|
||||
event.stopPropagation();
|
||||
@@ -1034,7 +1034,7 @@ export default function ScheduledTasksPage({
|
||||
{selectedTaskCanEditRuns && run.can_edit !== false && (
|
||||
<Button
|
||||
size="sm"
|
||||
variant="destructive"
|
||||
variant={theme === "iceblue" || theme === "lightyellow" ? "default" : "destructive"}
|
||||
onClick={() => handleDeleteRun(run.id)}
|
||||
>
|
||||
<Trash2 className="size-4" />
|
||||
|
||||
@@ -21,6 +21,7 @@ import { ActionAnalyzes } from "@/strategy-components/components/task-components
|
||||
import KnowledgeBaseResourceManagement from "@/strategy-components/components/resource-management/KnowledgeBaseResourceManagement";
|
||||
import NetworkActorResourceManagement from "@/strategy-components/components/resource-management/NetworkActorResourceManagement";
|
||||
import RoundtablePlanningPage from "@/roundtable-planning/pages/RoundtablePlanningPage";
|
||||
import PositionRoundtablePage from "@/position-roundtable/pages/PositionRoundtablePage";
|
||||
import DashboardAgentPage from "@/roundtable-planning/pages/DashboardAgentPage";
|
||||
import BusinessChainListPage from "@/roundtable-planning/pages/BusinessChainListPage";
|
||||
import BusinessChainEditorPage from "@/roundtable-planning/pages/BusinessChainEditorPage";
|
||||
@@ -91,6 +92,14 @@ export default function StrategyRoutes() {
|
||||
</ChatRuntime>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path="qa/position-roundtable"
|
||||
element={
|
||||
<ChatRuntime>
|
||||
<PositionRoundtablePage />
|
||||
</ChatRuntime>
|
||||
}
|
||||
/>
|
||||
|
||||
{/* 大屏绘制智能体独立页面(按 taskId 存取;ChatRuntime 提供 ArtifactsProvider 给沙箱预览) */}
|
||||
<Route
|
||||
|
||||
@@ -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';
|
||||
@@ -54,6 +55,7 @@ import AIWritingPage from '@/open-canvas/pages/AIWritingPage';
|
||||
import StudioNotebooksPage from './StudioNotebooksPage';
|
||||
import StudioNotebookDetailPage from './StudioNotebookDetailPage';
|
||||
import RoundtablePlanningPage from '@/roundtable-planning/pages/RoundtablePlanningPage';
|
||||
import PositionRoundtablePage from '@/position-roundtable/pages/PositionRoundtablePage';
|
||||
import BusinessChainEditorPage from '@/roundtable-planning/pages/BusinessChainEditorPage';
|
||||
import BusinessChainListPage from '@/roundtable-planning/pages/BusinessChainListPage';
|
||||
import DispatchChainPreviewPage from '@/roundtable-planning/pages/DispatchChainPreviewPage';
|
||||
@@ -71,14 +73,6 @@ function NewAgentPageWithKey() {
|
||||
return <NewAgentPage key={thread_id} />;
|
||||
}
|
||||
|
||||
function isInIframe(): boolean {
|
||||
try {
|
||||
return window.self !== window.top;
|
||||
} catch {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
function shouldShowCompactTopActions(pathname: string): boolean {
|
||||
return (
|
||||
pathname === '/page/workspace/agents' ||
|
||||
@@ -117,30 +111,35 @@ export function WorkspaceLayout({
|
||||
pageSidebarHidden = false,
|
||||
workspaceSidebarHidden = false,
|
||||
headerHidden = false,
|
||||
compactActionsHidden = false,
|
||||
compactWorkspaceSidebar = false,
|
||||
defaultHistorySidebarOpen,
|
||||
wrapperOverflowHidden = false,
|
||||
insetOverflowHidden = false,
|
||||
pageSidebarDark = false,
|
||||
forceWorkspaceSidebarCollapsed = false,
|
||||
}: {
|
||||
children: ReactNode;
|
||||
pageSidebarCollapsed: boolean;
|
||||
pageSidebarHidden?: boolean;
|
||||
workspaceSidebarHidden?: boolean;
|
||||
headerHidden?: boolean;
|
||||
compactActionsHidden?: boolean;
|
||||
compactWorkspaceSidebar?: boolean;
|
||||
/** 全量模式下通用问答等页:聊天记录侧栏默认展开;未指定时紧凑模式仍默认收起。 */
|
||||
defaultHistorySidebarOpen?: boolean;
|
||||
wrapperOverflowHidden?: boolean;
|
||||
insetOverflowHidden?: boolean;
|
||||
pageSidebarDark?: boolean;
|
||||
/** Deployment-level compact mode: keep only the icon menu rail. */
|
||||
forceWorkspaceSidebarCollapsed?: boolean;
|
||||
}) {
|
||||
// 全量模式下通用问答(/page/workspace/chats*)的历史侧栏默认展开;其余紧凑页
|
||||
// (如智能体对话,为聊天区更宽而默认收起)维持原默认。
|
||||
const pathname = useLocation().pathname;
|
||||
const isGeneralQa = pathname.startsWith('/page/workspace/chats');
|
||||
const showCompactTopActions =
|
||||
headerHidden && shouldShowCompactTopActions(pathname);
|
||||
headerHidden && !compactActionsHidden && shouldShowCompactTopActions(pathname);
|
||||
|
||||
const pageSidebarWidth = pageSidebarHidden
|
||||
? '0rem'
|
||||
@@ -169,11 +168,19 @@ export function WorkspaceLayout({
|
||||
key={String(compactWorkspaceSidebar)}
|
||||
data-page-sidebar-collapsed={pageSidebarCollapsed}
|
||||
defaultOpen={
|
||||
defaultHistorySidebarOpen ?? (isGeneralQa || !compactWorkspaceSidebar)
|
||||
forceWorkspaceSidebarCollapsed
|
||||
? false
|
||||
: (defaultHistorySidebarOpen ?? (isGeneralQa || !compactWorkspaceSidebar))
|
||||
}
|
||||
open={forceWorkspaceSidebarCollapsed ? false : undefined}
|
||||
onOpenChange={forceWorkspaceSidebarCollapsed ? () => undefined : undefined}
|
||||
disableToggle={forceWorkspaceSidebarCollapsed}
|
||||
>
|
||||
{!workspaceSidebarHidden && (
|
||||
<WorkspaceSidebar compactChrome={compactWorkspaceSidebar} />
|
||||
<WorkspaceSidebar
|
||||
compactChrome={compactWorkspaceSidebar}
|
||||
forceCollapsed={forceWorkspaceSidebarCollapsed}
|
||||
/>
|
||||
)}
|
||||
<SidebarInset
|
||||
className={cn('min-w-0', insetOverflowHidden && 'overflow-hidden')}
|
||||
@@ -224,15 +231,21 @@ function NewChatEntryRoute({
|
||||
layoutProps: {
|
||||
pageSidebarCollapsed: boolean;
|
||||
pageSidebarHidden: boolean;
|
||||
workspaceSidebarHidden: boolean;
|
||||
headerHidden: boolean;
|
||||
compactWorkspaceSidebar: boolean;
|
||||
compactActionsHidden: boolean;
|
||||
pageSidebarDark: boolean;
|
||||
forceWorkspaceSidebarCollapsed: boolean;
|
||||
};
|
||||
}) {
|
||||
if (isInIframe()) {
|
||||
return <Navigate to="/page/workspace/chats_iframe/new" replace />;
|
||||
}
|
||||
|
||||
// Do not infer the chat experience from window.self !== window.top here.
|
||||
// A generic `/chats/new` deep link must render the same standard chat both
|
||||
// top-level and inside an iframe. The dedicated iframe protocol page remains
|
||||
// available only through its explicit `/chats_iframe/...` route (LoginPage
|
||||
// selects it for `from=iframe`). Otherwise a normal deep link was silently
|
||||
// redirected to IframeChatPage, whose new-thread state intentionally has no
|
||||
// manually selected agent and therefore stopped at "选择 Agent".
|
||||
return (
|
||||
<WorkspaceLayout {...layoutProps} defaultHistorySidebarOpen>
|
||||
<ChatRuntime>
|
||||
@@ -242,25 +255,94 @@ 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,
|
||||
workspaceSidebarHidden = false,
|
||||
headerHidden = false,
|
||||
compactActionsHidden = false,
|
||||
compactWorkspaceSidebar = false,
|
||||
pageSidebarDark = false,
|
||||
forceWorkspaceSidebarCollapsed = false,
|
||||
}: {
|
||||
pageSidebarCollapsed?: boolean;
|
||||
pageSidebarHidden?: boolean;
|
||||
workspaceSidebarHidden?: boolean;
|
||||
headerHidden?: boolean;
|
||||
compactActionsHidden?: boolean;
|
||||
compactWorkspaceSidebar?: boolean;
|
||||
pageSidebarDark?: boolean;
|
||||
forceWorkspaceSidebarCollapsed?: boolean;
|
||||
}) {
|
||||
const layoutProps = {
|
||||
pageSidebarCollapsed,
|
||||
pageSidebarHidden,
|
||||
workspaceSidebarHidden,
|
||||
headerHidden,
|
||||
compactActionsHidden,
|
||||
compactWorkspaceSidebar,
|
||||
pageSidebarDark,
|
||||
forceWorkspaceSidebarCollapsed,
|
||||
};
|
||||
|
||||
return (
|
||||
@@ -271,7 +353,7 @@ export default function WorkspaceRoutes({
|
||||
path="chats"
|
||||
element={
|
||||
<WorkspaceLayout {...layoutProps}>
|
||||
<ChatsPage compactChrome={headerHidden} />
|
||||
<ChatsPage compactChrome={headerHidden && !compactActionsHidden} />
|
||||
</WorkspaceLayout>
|
||||
}
|
||||
/>
|
||||
@@ -291,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"
|
||||
@@ -357,7 +417,7 @@ export default function WorkspaceRoutes({
|
||||
<AgentChatPage
|
||||
showAgentList
|
||||
compactAppearanceActions={
|
||||
headerHidden ? <CompactAppearanceActions /> : undefined
|
||||
headerHidden && !compactActionsHidden ? <CompactAppearanceActions /> : undefined
|
||||
}
|
||||
/>
|
||||
</ChatRuntime>
|
||||
@@ -394,7 +454,7 @@ export default function WorkspaceRoutes({
|
||||
<WorkspaceLayout {...layoutProps}>
|
||||
<AIWritingPage
|
||||
hideAdvancedFields
|
||||
compactChrome={headerHidden}
|
||||
compactChrome={headerHidden && !compactActionsHidden}
|
||||
/>
|
||||
</WorkspaceLayout>
|
||||
}
|
||||
@@ -428,7 +488,17 @@ export default function WorkspaceRoutes({
|
||||
element={
|
||||
<WorkspaceLayout {...layoutProps}>
|
||||
<ChatRuntime>
|
||||
<RoundtablePlanningPage compactChrome={headerHidden} />
|
||||
<RoundtablePlanningPage compactChrome={headerHidden && !compactActionsHidden} />
|
||||
</ChatRuntime>
|
||||
</WorkspaceLayout>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path="roundtable/position"
|
||||
element={
|
||||
<WorkspaceLayout {...layoutProps}>
|
||||
<ChatRuntime>
|
||||
<PositionRoundtablePage compactChrome={headerHidden && !compactActionsHidden} />
|
||||
</ChatRuntime>
|
||||
</WorkspaceLayout>
|
||||
}
|
||||
@@ -490,7 +560,7 @@ export default function WorkspaceRoutes({
|
||||
path="scheduled/tasks"
|
||||
element={
|
||||
<WorkspaceLayout {...layoutProps}>
|
||||
<ScheduledTasksPage compactChrome={headerHidden} />
|
||||
<ScheduledTasksPage compactChrome={headerHidden && !compactActionsHidden} />
|
||||
</WorkspaceLayout>
|
||||
}
|
||||
/>
|
||||
|
||||
@@ -651,15 +651,25 @@
|
||||
box-shadow: 0 0 0 2px rgba(0, 98, 217, 0.16);
|
||||
}
|
||||
|
||||
/* ── TDesign Select focus / hover ────────────────────────────────────── */
|
||||
/* ── TDesign Select interaction ──────────────────────────────────────── */
|
||||
[data-theme="lightblue"] .light-app-management-page .t-select:hover .t-input,
|
||||
[data-theme="lightblue"] .light-app-management-page .t-select:hover .t-input__wrap {
|
||||
border-color: rgba(0, 98, 217, 0.5);
|
||||
border-color: #A6A6A6;
|
||||
}
|
||||
[data-theme="lightblue"] .light-app-management-page .t-select.t-is-focused .t-input,
|
||||
[data-theme="lightblue"] .light-app-management-page .t-select.t-is-focused .t-input__wrap {
|
||||
border-color: #0062D9;
|
||||
box-shadow: 0 0 0 2px rgba(0, 98, 217, 0.16);
|
||||
/* Opening the options with a mouse is not a validation or selected state. */
|
||||
border-color: #DBDBDB;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
/* Keep a visible focus ring for keyboard navigation only. */
|
||||
@supports selector(.t-select:has(.t-input__inner:focus-visible)) {
|
||||
[data-theme="lightblue"] .light-app-management-page .t-select.t-is-focused:has(.t-input__inner:focus-visible) .t-input,
|
||||
[data-theme="lightblue"] .light-app-management-page .t-select.t-is-focused:has(.t-input__inner:focus-visible) .t-input__wrap {
|
||||
border-color: #0062D9;
|
||||
box-shadow: 0 0 0 2px rgba(0, 98, 217, 0.16);
|
||||
}
|
||||
}
|
||||
|
||||
/* ── 启用开关 ───────────────────────────────────────────────────────── */
|
||||
@@ -679,6 +689,163 @@
|
||||
background-color: #0056C6;
|
||||
}
|
||||
|
||||
/* Ice blue theme — use #9BCEF1 for surfaces and #3D6E96 for actions so
|
||||
text on primary controls remains readable. */
|
||||
[data-theme="iceblue"] .light-app-management-page .lam-primary-btn,
|
||||
[data-theme="iceblue"] .light-app-management-page .lam-dialog-save,
|
||||
[data-theme="iceblue"] .light-app-management-page .lam-switch-on {
|
||||
background-color: #9BCEF1;
|
||||
}
|
||||
[data-theme="iceblue"] .light-app-management-page .lam-primary-btn,
|
||||
[data-theme="iceblue"] .light-app-management-page .lam-dialog-save {
|
||||
color: #18324A;
|
||||
box-shadow: 0 2px 6px rgba(155, 206, 241, 0.42);
|
||||
}
|
||||
[data-theme="iceblue"] .light-app-management-page .lam-primary-btn:hover,
|
||||
[data-theme="iceblue"] .light-app-management-page .lam-dialog-save:hover {
|
||||
background-color: #B7DCF5;
|
||||
}
|
||||
[data-theme="iceblue"] .light-app-management-page .lam-primary-btn:active,
|
||||
[data-theme="iceblue"] .light-app-management-page .lam-dialog-save:active {
|
||||
background-color: #84B9DE;
|
||||
}
|
||||
[data-theme="iceblue"] .light-app-management-page .lam-search-input:focus {
|
||||
border-color: rgba(61, 110, 150, 0.5);
|
||||
box-shadow: 0 0 0 4px rgba(155, 206, 241, 0.42);
|
||||
}
|
||||
[data-theme="iceblue"] .light-app-management-page .lam-view-toggle-btn.is-active,
|
||||
[data-theme="iceblue"] .light-app-management-page .lam-cat-toggle-btn.is-active,
|
||||
[data-theme="iceblue"] .light-app-management-page .lam-count strong,
|
||||
[data-theme="iceblue"] .light-app-management-page .lam-table tr:hover .lam-row-name,
|
||||
[data-theme="iceblue"] .light-app-management-page .lam-param-popover-close,
|
||||
[data-theme="iceblue"] .light-app-management-page .lam-param-key,
|
||||
[data-theme="iceblue"] .light-app-management-page .lam-card:hover .lam-card-title,
|
||||
[data-theme="iceblue"] .light-app-management-page .lam-dialog-close:hover,
|
||||
[data-theme="iceblue"] .light-app-management-page .lam-add-param-btn:hover {
|
||||
color: #2D587A;
|
||||
}
|
||||
[data-theme="iceblue"] .light-app-management-page .lam-add-param-btn:hover,
|
||||
[data-theme="iceblue"] .light-app-management-page .lam-input:focus,
|
||||
[data-theme="iceblue"] .light-app-management-page .lam-dialog input[type="text"]:focus {
|
||||
border-color: #5B91B9;
|
||||
}
|
||||
[data-theme="iceblue"] .light-app-management-page .lam-input:focus,
|
||||
[data-theme="iceblue"] .light-app-management-page .lam-dialog input[type="text"]:focus,
|
||||
[data-theme="iceblue"] .light-app-management-page .lam-dialog [data-slot="input"]:focus-visible,
|
||||
[data-theme="iceblue"] .light-app-management-page .lam-dialog textarea:focus-visible,
|
||||
[data-theme="iceblue"] .light-app-management-page .t-select.t-is-focused .t-input,
|
||||
[data-theme="iceblue"] .light-app-management-page .t-select.t-is-focused .t-input__wrap {
|
||||
box-shadow: 0 0 0 2px rgba(155, 206, 241, 0.58);
|
||||
}
|
||||
[data-theme="iceblue"] .light-app-management-page .lam-dialog-icon {
|
||||
background-color: #E1F1FB;
|
||||
color: #2D587A;
|
||||
}
|
||||
|
||||
/* Pale yellow: #F3FBD9 surfaces pair with dark olive interaction text. */
|
||||
[data-theme="lightyellow"] .light-app-management-page .lam-primary-btn,
|
||||
[data-theme="lightyellow"] .light-app-management-page .lam-dialog-save,
|
||||
[data-theme="lightyellow"] .light-app-management-page .lam-switch-on {
|
||||
background-color: #F3FBD9;
|
||||
}
|
||||
[data-theme="lightyellow"] .light-app-management-page .lam-primary-btn,
|
||||
[data-theme="lightyellow"] .light-app-management-page .lam-dialog-save {
|
||||
color: #303D23;
|
||||
box-shadow: 0 2px 6px rgba(243, 251, 217, 0.72);
|
||||
}
|
||||
[data-theme="lightyellow"] .light-app-management-page .lam-primary-btn:hover,
|
||||
[data-theme="lightyellow"] .light-app-management-page .lam-dialog-save:hover {
|
||||
background-color: #E4EFBF;
|
||||
}
|
||||
[data-theme="lightyellow"] .light-app-management-page .lam-primary-btn:active,
|
||||
[data-theme="lightyellow"] .light-app-management-page .lam-dialog-save:active {
|
||||
background-color: #DCEBAD;
|
||||
}
|
||||
[data-theme="lightyellow"] .light-app-management-page .lam-search-input:focus {
|
||||
border-color: rgba(76, 99, 48, 0.5);
|
||||
box-shadow: 0 0 0 4px rgba(243, 251, 217, 0.72);
|
||||
}
|
||||
[data-theme="lightyellow"] .light-app-management-page .lam-view-toggle-btn.is-active,
|
||||
[data-theme="lightyellow"] .light-app-management-page .lam-cat-toggle-btn.is-active,
|
||||
[data-theme="lightyellow"] .light-app-management-page .lam-count strong,
|
||||
[data-theme="lightyellow"] .light-app-management-page .lam-table tr:hover .lam-row-name,
|
||||
[data-theme="lightyellow"] .light-app-management-page .lam-param-popover-close,
|
||||
[data-theme="lightyellow"] .light-app-management-page .lam-param-key,
|
||||
[data-theme="lightyellow"] .light-app-management-page .lam-card:hover .lam-card-title,
|
||||
[data-theme="lightyellow"] .light-app-management-page .lam-dialog-close:hover,
|
||||
[data-theme="lightyellow"] .light-app-management-page .lam-add-param-btn:hover {
|
||||
color: #4C6330;
|
||||
}
|
||||
[data-theme="lightyellow"] .light-app-management-page .lam-add-param-btn:hover,
|
||||
[data-theme="lightyellow"] .light-app-management-page .lam-input:focus,
|
||||
[data-theme="lightyellow"] .light-app-management-page .lam-dialog input[type="text"]:focus {
|
||||
border-color: #B4C985;
|
||||
}
|
||||
[data-theme="lightyellow"] .light-app-management-page .lam-input:focus,
|
||||
[data-theme="lightyellow"] .light-app-management-page .lam-dialog input[type="text"]:focus,
|
||||
[data-theme="lightyellow"] .light-app-management-page .lam-dialog [data-slot="input"]:focus-visible,
|
||||
[data-theme="lightyellow"] .light-app-management-page .lam-dialog textarea:focus-visible,
|
||||
[data-theme="lightyellow"] .light-app-management-page .t-select.t-is-focused .t-input,
|
||||
[data-theme="lightyellow"] .light-app-management-page .t-select.t-is-focused .t-input__wrap {
|
||||
box-shadow: 0 0 0 2px rgba(243, 251, 217, 0.78);
|
||||
}
|
||||
[data-theme="lightyellow"] .light-app-management-page .lam-dialog-icon {
|
||||
background-color: #EEF6D4;
|
||||
color: #4C6330;
|
||||
}
|
||||
|
||||
/* WeKnora green theme. */
|
||||
[data-theme="lightgreen"] .light-app-management-page .lam-primary-btn,
|
||||
[data-theme="lightgreen"] .light-app-management-page .lam-dialog-save,
|
||||
[data-theme="lightgreen"] .light-app-management-page .lam-switch-on {
|
||||
background-color: #07C05F;
|
||||
}
|
||||
[data-theme="lightgreen"] .light-app-management-page .lam-primary-btn,
|
||||
[data-theme="lightgreen"] .light-app-management-page .lam-dialog-save {
|
||||
box-shadow: 0 2px 6px rgba(7, 192, 95, 0.2);
|
||||
}
|
||||
[data-theme="lightgreen"] .light-app-management-page .lam-primary-btn:hover,
|
||||
[data-theme="lightgreen"] .light-app-management-page .lam-dialog-save:hover {
|
||||
background-color: #08DD6E;
|
||||
}
|
||||
[data-theme="lightgreen"] .light-app-management-page .lam-primary-btn:active,
|
||||
[data-theme="lightgreen"] .light-app-management-page .lam-dialog-save:active {
|
||||
background-color: #049B38;
|
||||
}
|
||||
[data-theme="lightgreen"] .light-app-management-page .lam-search-input:focus {
|
||||
border-color: rgba(7, 192, 95, 0.4);
|
||||
box-shadow: 0 0 0 4px rgba(7, 192, 95, 0.1);
|
||||
}
|
||||
[data-theme="lightgreen"] .light-app-management-page .lam-view-toggle-btn.is-active,
|
||||
[data-theme="lightgreen"] .light-app-management-page .lam-cat-toggle-btn.is-active,
|
||||
[data-theme="lightgreen"] .light-app-management-page .lam-count strong,
|
||||
[data-theme="lightgreen"] .light-app-management-page .lam-table tr:hover .lam-row-name,
|
||||
[data-theme="lightgreen"] .light-app-management-page .lam-param-popover-close,
|
||||
[data-theme="lightgreen"] .light-app-management-page .lam-param-key,
|
||||
[data-theme="lightgreen"] .light-app-management-page .lam-card:hover .lam-card-title,
|
||||
[data-theme="lightgreen"] .light-app-management-page .lam-dialog-close:hover,
|
||||
[data-theme="lightgreen"] .light-app-management-page .lam-add-param-btn:hover {
|
||||
color: #049B38;
|
||||
}
|
||||
[data-theme="lightgreen"] .light-app-management-page .lam-add-param-btn:hover {
|
||||
border-color: #07C05F;
|
||||
}
|
||||
[data-theme="lightgreen"] .light-app-management-page .lam-dialog-icon {
|
||||
background-color: #E9F8EC;
|
||||
color: #049B38;
|
||||
}
|
||||
[data-theme="lightgreen"] .light-app-management-page .lam-input:focus,
|
||||
[data-theme="lightgreen"] .light-app-management-page .lam-dialog input[type="text"]:focus,
|
||||
[data-theme="lightgreen"] .light-app-management-page .t-select.t-is-focused .t-input,
|
||||
[data-theme="lightgreen"] .light-app-management-page .t-select.t-is-focused .t-input__wrap {
|
||||
border-color: #07C05F;
|
||||
box-shadow: 0 0 0 2px rgba(7, 192, 95, 0.16);
|
||||
}
|
||||
[data-theme="lightgreen"] .light-app-management-page .t-select:hover .t-input,
|
||||
[data-theme="lightgreen"] .light-app-management-page .t-select:hover .t-input__wrap {
|
||||
border-color: rgba(7, 192, 95, 0.5);
|
||||
}
|
||||
|
||||
/* ── iframe 菜单排序:拖拽行 + 拖拽手柄 ───────────────────────────────── */
|
||||
.light-app-management-page .lam-reorder-row {
|
||||
cursor: grab;
|
||||
|
||||
@@ -7,6 +7,16 @@
|
||||
background-color: #F9FCFF;
|
||||
}
|
||||
|
||||
[data-theme="lightgreen"] .light-app-management-page,
|
||||
[data-theme="lightgreen"] .light-app-list-page {
|
||||
background-color: #F9FDF9;
|
||||
}
|
||||
|
||||
[data-theme="lightyellow"] .light-app-management-page,
|
||||
[data-theme="lightyellow"] .light-app-list-page {
|
||||
background-color: #FCFDF5;
|
||||
}
|
||||
|
||||
.dark .light-app-management-page,
|
||||
.dark .light-app-list-page {
|
||||
background-color: var(--color-bg-page);
|
||||
|
||||
Reference in new issue
Block a user