From d0afbf1897be56146e568f5d6120a9420ea83627 Mon Sep 17 00:00:00 2001 From: MT-Fire <798521692@qq.com> Date: Sat, 13 Jun 2026 11:29:39 +0800 Subject: [PATCH] =?UTF-8?q?refactor(brand):=20=E9=87=8D=E6=9E=84=E5=93=81?= =?UTF-8?q?=E7=89=8C=E7=B3=BB=E7=BB=9F=EF=BC=8C=E7=A7=BB=E9=99=A4=20SESSIO?= =?UTF-8?q?N=5FBRAND=20=E5=B9=B6=E5=AE=8C=E5=96=84=E5=88=9D=E5=A7=8B?= =?UTF-8?q?=E5=8C=96=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 将 DEFAULT_BRAND 重命名为 XCLAW_BRAND,统一品牌命名规范 - 删除未使用的 SESSION_BRAND 类型和相关配置 - 优化 getInitialBrandFromBrowser 逻辑:根据 isSxwz 参数直接设置并返回对应品牌 - 更新品牌颜色 tokens 和 CSS 选择器测试 - 同步更新 provider.tsx 和相关测试用例 --- frontend/src/core/brand/brand-session.test.ts | 36 ++++++++------ frontend/src/core/brand/index.ts | 49 +++++++++++++------ frontend/src/core/brand/provider.tsx | 10 ++-- .../styles/globals-brand-selectors.test.ts | 2 +- frontend/src/styles/globals.css | 2 +- frontend/src/styles/workspace-color-tokens.ts | 10 ++-- 6 files changed, 63 insertions(+), 46 deletions(-) diff --git a/frontend/src/core/brand/brand-session.test.ts b/frontend/src/core/brand/brand-session.test.ts index 24b812a5..6f25da61 100644 --- a/frontend/src/core/brand/brand-session.test.ts +++ b/frontend/src/core/brand/brand-session.test.ts @@ -17,12 +17,12 @@ const SXWZ_BRAND_PRIMARY = `#${"000F33"}`; void test("parseBrandFromSearchParams returns correct brand per param value", () => { assert.equal(parseBrandFromSearchParams(new URLSearchParams("isSxwz=true")), "sxwz"); - assert.equal(parseBrandFromSearchParams(new URLSearchParams("isSxwz=false")), "default"); + assert.equal(parseBrandFromSearchParams(new URLSearchParams("isSxwz=false")), "xclaw"); assert.equal(parseBrandFromSearchParams(new URLSearchParams("")), null); }); -void test("resolveBrandSession falls back to default without url or storage", () => { - assert.equal(resolveBrandSession({ urlBrand: null, storedBrand: null }), "default"); +void test("resolveBrandSession falls back to xclaw without url or storage", () => { + assert.equal(resolveBrandSession({ urlBrand: null, storedBrand: null }), "xclaw"); }); void test("resolveBrandSession keeps stored sxwz when later url omits the flag", () => { @@ -31,28 +31,28 @@ void test("resolveBrandSession keeps stored sxwz when later url omits the flag", void test("resolveBrandSession downgrades stored sxwz when url explicitly sets isSxwz=false", () => { const urlBrand = parseBrandFromSearchParams(new URLSearchParams("isSxwz=false")); - assert.equal(resolveBrandSession({ urlBrand, storedBrand: "sxwz" }), "default"); + assert.equal(resolveBrandSession({ urlBrand, storedBrand: "sxwz" }), "xclaw"); }); void test("resolveBrandSession upgrades to sxwz when url flag is true", () => { const urlBrand = parseBrandFromSearchParams(new URLSearchParams("isSxwz=true")); - assert.equal(resolveBrandSession({ urlBrand, storedBrand: "default" }), "sxwz"); + assert.equal(resolveBrandSession({ urlBrand, storedBrand: "xclaw" }), "sxwz"); }); void test("getBrandRootClassName returns stable workspace hook classes", () => { - assert.equal(getBrandRootClassName("default"), "brand-default"); + assert.equal(getBrandRootClassName("xclaw"), "brand-xclaw"); assert.equal(getBrandRootClassName("sxwz"), "brand-sxwz"); assert.equal(BRAND_SESSION_STORAGE_KEY, "deerflow.brand-session"); }); void test("getBrandPrimaryColor returns the right global brand primary", () => { - assert.equal(getBrandPrimaryColor("default"), DEFAULT_BRAND_PRIMARY); + assert.equal(getBrandPrimaryColor("xclaw"), DEFAULT_BRAND_PRIMARY); assert.equal(getBrandPrimaryColor("sxwz"), SXWZ_BRAND_PRIMARY); }); void test("getBrandPrimaryColorWithAlpha preserves alpha values across brands", () => { assert.equal( - getBrandPrimaryColorWithAlpha("default", "1A"), + getBrandPrimaryColorWithAlpha("xclaw", "1A"), `${DEFAULT_BRAND_PRIMARY}1A`, ); assert.equal( @@ -60,7 +60,7 @@ void test("getBrandPrimaryColorWithAlpha preserves alpha values across brands", `${SXWZ_BRAND_PRIMARY}1A`, ); assert.equal( - getBrandPrimaryColorWithAlpha("default", "99"), + getBrandPrimaryColorWithAlpha("xclaw", "99"), `${DEFAULT_BRAND_PRIMARY}99`, ); assert.equal( @@ -71,25 +71,29 @@ void test("getBrandPrimaryColorWithAlpha preserves alpha values across brands", void test("getInitialBrandFromBrowser prioritizes url brand on first render", () => { const searchParams = new URLSearchParams("isSxwz=true"); - assert.equal(getInitialBrandFromBrowser({ searchParams, storedBrand: null }), "sxwz"); + const storage = { + getItem: () => null, + setItem: () => {}, + }; + assert.equal(getInitialBrandFromBrowser({ searchParams, storage }), "sxwz"); assert.equal( getInitialBrandFromBrowser({ searchParams: new URLSearchParams("isSxwz=false"), - storedBrand: "sxwz", + storage, }), - "default", + "xclaw", ); assert.equal( getInitialBrandFromBrowser({ searchParams: new URLSearchParams(""), - storedBrand: "sxwz", + storage, }), - "sxwz", + "xclaw", ); }); void test("syncBrandClassName rewrites brand classes on arbitrary targets", () => { - const classSet = new Set(["foo", "brand-default"]); + const classSet = new Set(["foo", "brand-xclaw"]); const target = { classList: { add: (value: string) => classSet.add(value), @@ -100,6 +104,6 @@ void test("syncBrandClassName rewrites brand classes on arbitrary targets", () = syncBrandClassName(target, "sxwz"); assert.equal(classSet.has("foo"), true); - assert.equal(classSet.has("brand-default"), false); + assert.equal(classSet.has("brand-xclaw"), false); assert.equal(classSet.has("brand-sxwz"), true); }); diff --git a/frontend/src/core/brand/index.ts b/frontend/src/core/brand/index.ts index 37b905f6..4387ad4b 100644 --- a/frontend/src/core/brand/index.ts +++ b/frontend/src/core/brand/index.ts @@ -1,12 +1,12 @@ export const BRAND_SESSION_STORAGE_KEY = "deerflow.brand-session"; -export const DEFAULT_BRAND = "default" as const; -const SXWZ_BRAND = "sxwz" as const; +export const XCLAW_BRAND = "xclaw" as const; +export const SXWZ_BRAND = "sxwz" as const; const BRAND_PRIMARY_COLORS = { - default: `#${"150033"}`, + xclaw: `#${"150033"}`, sxwz: `#${"000F33"}`, } as const; -export type Brand = typeof DEFAULT_BRAND | typeof SXWZ_BRAND; +export type Brand = typeof XCLAW_BRAND | typeof SXWZ_BRAND; export type BrandCopy = { productLabel: string; @@ -16,7 +16,7 @@ export type BrandCopy = { }; export const BRAND_COPY: Record = { - default: { + xclaw: { productLabel: "轻办公", appName: "coxworker", appLogoSrc: "/coxwork.png", @@ -29,7 +29,7 @@ export const BRAND_COPY: Record = { }; export function isBrand(value: string | null): value is Brand { - return value === DEFAULT_BRAND || value === SXWZ_BRAND; + return value === XCLAW_BRAND || value === SXWZ_BRAND; } export function parseBrandFromSearchParams( @@ -37,7 +37,7 @@ export function parseBrandFromSearchParams( ): Brand | null { const value = searchParams.get("isSxwz"); if (value === "true") return SXWZ_BRAND; - if (value === "false") return DEFAULT_BRAND; + if (value === "false") return XCLAW_BRAND; return null; } @@ -52,30 +52,47 @@ export function resolveBrandSession({ return SXWZ_BRAND; } - if (urlBrand === DEFAULT_BRAND) { - return DEFAULT_BRAND; + if (urlBrand === XCLAW_BRAND) { + return XCLAW_BRAND; } if (storedBrand === SXWZ_BRAND) { return SXWZ_BRAND; } - return DEFAULT_BRAND; + return XCLAW_BRAND; } export function getInitialBrandFromBrowser({ searchParams, - storedBrand, + storage, }: { searchParams: URLSearchParams; - storedBrand: Brand | null; + storage: Pick; }): Brand { - const urlBrand = parseBrandFromSearchParams(searchParams); - return resolveBrandSession({ urlBrand, storedBrand }); + const isSxwz = searchParams.get("isSxwz"); + + if (isSxwz === "true") { + storage.setItem(BRAND_SESSION_STORAGE_KEY, SXWZ_BRAND); + return SXWZ_BRAND; + } + + if (isSxwz === "false") { + storage.setItem(BRAND_SESSION_STORAGE_KEY, XCLAW_BRAND); + return XCLAW_BRAND; + } + + const storedBrand = storage.getItem(BRAND_SESSION_STORAGE_KEY); + if (storedBrand === SXWZ_BRAND || storedBrand === XCLAW_BRAND) { + return storedBrand; + } + + return XCLAW_BRAND; } export function getBrandRootClassName(brand: Brand): string { - return brand === SXWZ_BRAND ? "brand-sxwz" : "brand-default"; + if (brand === SXWZ_BRAND) return "brand-sxwz"; + return "brand-xclaw"; } type BrandClassTarget = { @@ -89,7 +106,7 @@ export function syncBrandClassName( target: BrandClassTarget, brand: Brand, ): void { - target.classList.remove("brand-default", "brand-sxwz"); + target.classList.remove("brand-xclaw", "brand-sxwz"); target.classList.add(getBrandRootClassName(brand)); } diff --git a/frontend/src/core/brand/provider.tsx b/frontend/src/core/brand/provider.tsx index bc2e5a8b..9be6ec29 100644 --- a/frontend/src/core/brand/provider.tsx +++ b/frontend/src/core/brand/provider.tsx @@ -4,7 +4,7 @@ import { createContext, useContext, useState, type ReactNode } from "react"; import { BRAND_COPY, - DEFAULT_BRAND, + XCLAW_BRAND, getInitialBrandFromBrowser, getBrandRootClassName, type Brand, @@ -21,16 +21,12 @@ const BrandContext = createContext(null); function getInitialBrand(): Brand { if (typeof window === "undefined") { - return DEFAULT_BRAND; + return XCLAW_BRAND; } - const storedBrand = window.sessionStorage.getItem("deerflow.brand-session"); return getInitialBrandFromBrowser({ searchParams: new URLSearchParams(window.location.search), - storedBrand: - storedBrand === "sxwz" || storedBrand === DEFAULT_BRAND - ? storedBrand - : null, + storage: window.sessionStorage, }); } diff --git a/frontend/src/styles/globals-brand-selectors.test.ts b/frontend/src/styles/globals-brand-selectors.test.ts index 8bf43c86..f2a1da3e 100644 --- a/frontend/src/styles/globals-brand-selectors.test.ts +++ b/frontend/src/styles/globals-brand-selectors.test.ts @@ -8,6 +8,6 @@ const currentDir = path.dirname(url.fileURLToPath(import.meta.url)); const globalsCss = readFileSync(path.join(currentDir, "globals.css"), "utf8"); void test("brand selectors target :root to outrank default root variables", () => { - assert.match(globalsCss, /:root\.brand-default\s*\{/); + assert.match(globalsCss, /:root\.brand-xclaw\s*\{/); assert.match(globalsCss, /:root\.brand-sxwz\s*\{/); }); diff --git a/frontend/src/styles/globals.css b/frontend/src/styles/globals.css index 9b4a7f99..b1fe575e 100644 --- a/frontend/src/styles/globals.css +++ b/frontend/src/styles/globals.css @@ -68,7 +68,7 @@ @source inline("bg-{background,muted,primary,secondary,accent}"); @source inline("border-{border,input}"); -.brand-default { +.brand-xclaw { --brand-color-primary: #150033; --brand-color-primary-10: #1500331a; --brand-color-primary-60: #15003399; diff --git a/frontend/src/styles/workspace-color-tokens.ts b/frontend/src/styles/workspace-color-tokens.ts index bca4630c..1ea4902f 100644 --- a/frontend/src/styles/workspace-color-tokens.ts +++ b/frontend/src/styles/workspace-color-tokens.ts @@ -14,17 +14,17 @@ export type WorkspaceColorToken = { }; export const BRAND_PRIMARY_COLOR_TOKENS = { - default: "#150033", + xclaw: "#150033", sxwz: "#000F33", - defaultAlpha10: "#1500331A", + xclawAlpha10: "#1500331A", sxwzAlpha10: "#000F331A", - defaultAlpha60: "#15003399", + xclawAlpha60: "#15003399", sxwzAlpha60: "#000F3399", } as const; // Token 键保持语义化且稳定:`ws--`(不要再使用原始 hex 命名)。 export const WORKSPACE_COLOR_TOKENS = { - "ws-base-1": { light: BRAND_PRIMARY_COLOR_TOKENS.default, dark: "#f4ebff" }, + "ws-base-1": { light: BRAND_PRIMARY_COLOR_TOKENS.xclaw, dark: "#f4ebff" }, "ws-fg-primary": { light: "#333333", dark: "#f5f5f5" }, "ws-surface-subtle": { light: "#f9f8fa", dark: "#1f1f1f" }, "ws-surface-elevated": { light: "#fbfafc", dark: "#24222a" }, @@ -36,7 +36,7 @@ export const WORKSPACE_COLOR_TOKENS = { "ws-text-subtle-strong": { light: "#000000c5", dark: "#ffffffcc" }, "ws-border-hairline": { light: "#00000015", dark: "#ffffff1f" }, "ws-accent-tint-soft": { - light: BRAND_PRIMARY_COLOR_TOKENS.defaultAlpha10, + light: BRAND_PRIMARY_COLOR_TOKENS.xclawAlpha10, dark: "#f4ebff24", }, "ws-surface-app": { light: "#f8f9fb", dark: "#20242c" },