From 0bd9b9bdcb5f710a17f0eacbba7818a74c75a34d Mon Sep 17 00:00:00 2001 From: mt Date: Wed, 10 Jun 2026 17:51:46 +0800 Subject: [PATCH] =?UTF-8?q?feat(brand):=20workspace=20=E7=BB=84=E4=BB=B6?= =?UTF-8?q?=E6=8E=A5=E5=85=A5=E5=93=81=E7=89=8C=E6=96=87=E6=A1=88=E5=92=8C?= =?UTF-8?q?=20Logo=20=E5=88=87=E6=8D=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - layout.tsx: 包裹 BrandProvider + BrandSessionInitializer,SidebarProvider 注入 rootClassName - welcome.tsx: copy.productLabel 替代硬编码,appLogoSrc 条件渲染 Image/文字 - workspace-header.tsx: 侧边栏折叠时显示品牌缩写,展开时显示 Logo 或 appName --- frontend/src/app/workspace/layout.tsx | 17 ++++++++- frontend/src/components/workspace/welcome.tsx | 37 +++++++++++++++++-- .../components/workspace/workspace-header.tsx | 25 ++++++++++--- 3 files changed, 69 insertions(+), 10 deletions(-) diff --git a/frontend/src/app/workspace/layout.tsx b/frontend/src/app/workspace/layout.tsx index 81604687..211272a2 100644 --- a/frontend/src/app/workspace/layout.tsx +++ b/frontend/src/app/workspace/layout.tsx @@ -14,12 +14,25 @@ import { SidebarInset, SidebarProvider } from "@/components/ui/sidebar"; import { Toaster } from "@/components/ui/sonner"; import { CommandPalette } from "@/components/workspace/command-palette"; import { WorkspaceSidebar } from "@/components/workspace/workspace-sidebar"; +import { BrandProvider, useBrand } from "@/core/brand/provider"; +import { BrandSessionInitializer } from "@/core/brand/provider-client"; import { getLocalSettings, useLocalSettings } from "@/core/settings"; +import { cn } from "@/lib/utils"; const queryClient = new QueryClient(); export default function WorkspaceLayout({ children, +}: Readonly<{ children: React.ReactNode }>) { + return ( + + {children} + + ); +} + +function WorkspaceBrandShell({ + children, }: Readonly<{ children: React.ReactNode }>) { const [settings, setSettings] = useLocalSettings(); const [open, setOpen] = useState(false); // SSR default: open (matches server render) @@ -27,6 +40,7 @@ export default function WorkspaceLayout({ const pressedKeysRef = useRef>(new Set()); const comboTriggeredRef = useRef(false); const searchParams = useSearchParams(); + const { rootClassName } = useBrand(); // iframe 技能模式(mode=skill)时隐藏侧边栏 const isSkillMode = searchParams.get("mode") === "skill"; @@ -110,8 +124,9 @@ export default function WorkspaceLayout({ ); return ( + diff --git a/frontend/src/components/workspace/welcome.tsx b/frontend/src/components/workspace/welcome.tsx index 2cf5beb8..32c92482 100644 --- a/frontend/src/components/workspace/welcome.tsx +++ b/frontend/src/components/workspace/welcome.tsx @@ -1,8 +1,10 @@ "use client"; +import Image from "next/image"; import { useSearchParams } from "next/navigation"; import { useMemo } from "react"; +import { useBrand } from "@/core/brand/provider"; import { useI18n } from "@/core/i18n/hooks"; import { cn } from "@/lib/utils"; @@ -16,6 +18,7 @@ export function Welcome({ mode?: "ultra" | "pro" | "thinking" | "flash"; }) { const { t } = useI18n(); + const { copy } = useBrand(); const searchParams = useSearchParams(); const isUltra = useMemo(() => mode === "ultra", [mode]); const colors = useMemo(() => { @@ -39,12 +42,37 @@ export function Welcome({ className="flex items-center gap-2" style={{ fontFamily: '"Microsoft YaHei"' }} > - - {t.welcome.greeting} - + {copy.productLabel} + */} + + {copy.productLabel} + + + + · + + {copy.appLogoSrc ? ( + {copy.appLogoAlt + ) : ( + + {copy.appName} + + )} )} @@ -59,7 +87,8 @@ export function Welcome({ )} ) : ( -
+ //
+ <> )} ); diff --git a/frontend/src/components/workspace/workspace-header.tsx b/frontend/src/components/workspace/workspace-header.tsx index 420c7439..63e4655c 100644 --- a/frontend/src/components/workspace/workspace-header.tsx +++ b/frontend/src/components/workspace/workspace-header.tsx @@ -1,6 +1,7 @@ "use client"; import { MessageSquarePlus } from "lucide-react"; +import Image from "next/image"; import Link from "next/link"; import { usePathname } from "next/navigation"; import { toast } from "sonner"; @@ -13,6 +14,7 @@ import { useSidebar, } from "@/components/ui/sidebar"; import { useThreadChat } from "@/components/workspace/chats"; +import { useBrand } from "@/core/brand/provider"; import { useI18n } from "@/core/i18n/hooks"; import { POST_MESSAGE_TYPES, sendToParent } from "@/core/iframe-messages"; import { env } from "@/env"; @@ -21,10 +23,14 @@ import { copyToClipboard } from "@/lib/utils"; export function WorkspaceHeader({ className }: { className?: string }) { const { t } = useI18n(); + const { copy } = useBrand(); const { state } = useSidebar(); const pathname = usePathname(); const { threadId } = useThreadChat(); const threadUrl = threadId ? `/workspace/chats/${threadId}` : ""; + const compactAppName = copy.appName.startsWith("cox") + ? `C${copy.appName.charAt(3).toUpperCase()}` + : copy.appName.slice(0, 2).toUpperCase(); const handleCopyThreadId = async () => { if (!threadId) return; @@ -51,7 +57,7 @@ export function WorkspaceHeader({ className }: { className?: string }) { {state === "collapsed" ? (
- XC + {compactAppName}
@@ -62,9 +68,19 @@ export function WorkspaceHeader({ className }: { className?: string }) { {t.workspaceHeader.sidebarTitle} ) : ( -
- {/* TODO: 测试标识 */} - XClaw{" "} +
+ {copy.appLogoSrc ? ( + {copy.appLogoAlt + ) : ( + copy.appName + )} v3.3.0 {" "} id:{threadId ? threadId.slice(0, 5) : "-"} - {" "} {threadId && (