feat(brand): workspace 组件接入品牌文案和 Logo 切换
- layout.tsx: 包裹 BrandProvider + BrandSessionInitializer,SidebarProvider 注入 rootClassName - welcome.tsx: copy.productLabel 替代硬编码,appLogoSrc 条件渲染 Image/文字 - workspace-header.tsx: 侧边栏折叠时显示品牌缩写,展开时显示 Logo 或 appName
This commit is contained in:
parent
62fd2e6f06
commit
0bd9b9bdcb
@ -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 (
|
||||
<BrandProvider>
|
||||
<WorkspaceBrandShell>{children}</WorkspaceBrandShell>
|
||||
</BrandProvider>
|
||||
);
|
||||
}
|
||||
|
||||
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<Set<string>>(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 (
|
||||
<QueryClientProvider client={queryClient}>
|
||||
<BrandSessionInitializer />
|
||||
<SidebarProvider
|
||||
className="h-screen"
|
||||
className={cn("h-screen", rootClassName)}
|
||||
open={open}
|
||||
onOpenChange={handleOpenChange}
|
||||
>
|
||||
|
||||
@ -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"' }}
|
||||
>
|
||||
<AuroraText
|
||||
{/* <AuroraText
|
||||
className="text-center text-[18px] leading-normal font-normal"
|
||||
colors={colors}
|
||||
>
|
||||
{t.welcome.greeting}
|
||||
</AuroraText>
|
||||
{copy.productLabel}
|
||||
</AuroraText> */}
|
||||
<span className="text-[18px] font-normal text-foreground/70">
|
||||
{copy.productLabel}
|
||||
</span>
|
||||
|
||||
<span className="text-[18px] font-normal text-foreground/70">
|
||||
·
|
||||
</span>
|
||||
{copy.appLogoSrc ? (
|
||||
<Image
|
||||
src={copy.appLogoSrc}
|
||||
alt={copy.appLogoAlt ?? copy.appName}
|
||||
width={104}
|
||||
height={16}
|
||||
draggable={false}
|
||||
// className="h-[16px] w-auto"
|
||||
priority
|
||||
/>
|
||||
) : (
|
||||
<AuroraText
|
||||
className="text-center text-[18px] leading-normal font-normal"
|
||||
colors={colors}
|
||||
>
|
||||
{copy.appName}
|
||||
</AuroraText>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
@ -59,7 +87,8 @@ export function Welcome({
|
||||
)}
|
||||
</div>
|
||||
) : (
|
||||
<div> </div>
|
||||
// <div> </div>
|
||||
<></>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
|
||||
@ -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" ? (
|
||||
<div className="group-has-data-[collapsible=icon]/sidebar-wrapper:-translate-y flex w-full cursor-pointer items-center justify-center">
|
||||
<div className="text-primary block pt-1 font-serif group-hover/workspace-header:hidden">
|
||||
XC
|
||||
{compactAppName}
|
||||
</div>
|
||||
<SidebarTrigger className="hidden pl-2 group-hover/workspace-header:block" />
|
||||
</div>
|
||||
@ -62,9 +68,19 @@ export function WorkspaceHeader({ className }: { className?: string }) {
|
||||
{t.workspaceHeader.sidebarTitle}
|
||||
</Link>
|
||||
) : (
|
||||
<div className="text-primary ml-2 cursor-default font-serif">
|
||||
{/* TODO: 测试标识 */}
|
||||
XClaw{" "}
|
||||
<div className="text-primary ml-2 flex cursor-default items-center gap-2 font-serif">
|
||||
{copy.appLogoSrc ? (
|
||||
<Image
|
||||
src={copy.appLogoSrc}
|
||||
alt={copy.appLogoAlt ?? copy.appName}
|
||||
width={104}
|
||||
height={16}
|
||||
className="h-4 w-auto"
|
||||
priority
|
||||
/>
|
||||
) : (
|
||||
copy.appName
|
||||
)}
|
||||
<span className="text-sm text-ws-text-subtle-strong">v3.3.0 </span>{" "}
|
||||
<span
|
||||
className={cn(
|
||||
@ -80,7 +96,6 @@ export function WorkspaceHeader({ className }: { className?: string }) {
|
||||
>
|
||||
id:{threadId ? threadId.slice(0, 5) : "-"}
|
||||
</span>
|
||||
{" "}
|
||||
{threadId && (
|
||||
<a
|
||||
href={threadUrl}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user