Compare commits
4 Commits
63563ce6a3
...
c45bc4d521
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c45bc4d521 | ||
|
|
9eb494b1b4 | ||
|
|
0bd9b9bdcb | ||
|
|
62fd2e6f06 |
BIN
frontend/public/coxwork.png
Normal file
BIN
frontend/public/coxwork.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 6.4 KiB |
@ -28,6 +28,7 @@ import { MessageList } from "@/components/workspace/messages";
|
||||
import { ThreadContext } from "@/components/workspace/messages/context";
|
||||
import { Tooltip } from "@/components/workspace/tooltip";
|
||||
import { useSpecificChatMode } from "@/components/workspace/use-chat-mode";
|
||||
import { useBrand } from "@/core/brand/provider";
|
||||
import { Welcome } from "@/components/workspace/welcome";
|
||||
import { getAPIClient } from "@/core/api";
|
||||
import { sanitizeArtifactPaths } from "@/core/artifacts/utils";
|
||||
@ -48,6 +49,7 @@ import motivationSlogans from "./motivation-slogans.json";
|
||||
|
||||
export default function ChatPage() {
|
||||
const { t } = useI18n();
|
||||
const { brand } = useBrand();
|
||||
useSpecificChatMode();
|
||||
const [sloganIndex, setSloganIndex] = useState(0);
|
||||
const [settings, setSettings] = useLocalSettings();
|
||||
@ -345,6 +347,7 @@ export default function ChatPage() {
|
||||
className={cn(
|
||||
"m-auto flex h-screen min-h-svh overflow-hidden rounded-t-[20px] transition-[width] duration-300 ease-in-out",
|
||||
artifactsOpen ? "w-full" : "w-[70%]",
|
||||
brand === "sxwz" && artifactsOpen === false && "translate-x-[-172px]",
|
||||
)}
|
||||
>
|
||||
<div className="relative flex size-full min-h-0 justify-between rounded-t-[20px]">
|
||||
@ -374,6 +377,7 @@ export default function ChatPage() {
|
||||
isChatting: false,
|
||||
});
|
||||
router.replace(`/workspace/chats/${threadId}?is_chatting=false`)
|
||||
setArtifactsOpen(false);
|
||||
}
|
||||
}
|
||||
>
|
||||
@ -575,6 +579,7 @@ export default function ChatPage() {
|
||||
className={cn(
|
||||
"pointer-events-auto relative w-full max-w-[720px]",
|
||||
showWelcomeStyle && "-translate-y-[calc(50vh-96px)]",
|
||||
brand === "sxwz" && "-translate-x-[172px]"
|
||||
)}
|
||||
>
|
||||
{!(showWelcomeStyle && thread.isThreadLoading) ? (
|
||||
|
||||
@ -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}
|
||||
>
|
||||
|
||||
@ -1157,7 +1157,11 @@ export const PromptInputSubmit = ({
|
||||
? !!disabled
|
||||
: disabled || !hasContent || isSubmitted;
|
||||
|
||||
let Icon = <ArrowUpIcon className="size-4" />;
|
||||
// let Icon = <ArrowUpIcon className="size-4" />;
|
||||
let Icon = <svg width="12" height="16" viewBox="0 0 12 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M5.75 14.75V0.75M0.75 5.75L5.75 0.75L10.75 5.75" stroke="white" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>;
|
||||
|
||||
|
||||
let text: string = t.inputBox.submit;
|
||||
|
||||
@ -1165,11 +1169,13 @@ export const PromptInputSubmit = ({
|
||||
Icon = <Loader2Icon className="size-4 animate-spin" />;
|
||||
text = t.inputBox.submitting;
|
||||
} else if (status === "streaming") {
|
||||
Icon = <SquareIcon className="size-4" />;
|
||||
Icon = <svg className="!w-[12px] !h-[12px]" width="12" height="12" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect width="12" height="12" rx="2" fill="white"/>
|
||||
</svg>;
|
||||
text = t.inputBox.stop;
|
||||
} else if (status === "error") {
|
||||
// 没有报错状态,先用error状态代替
|
||||
Icon = <XIcon className="size-4" />;
|
||||
// Icon = <XIcon className="size-4" />;
|
||||
// MARK: 这里后端没有返回错误信息,先写死一个文本
|
||||
text = t.inputBox.submit;
|
||||
}
|
||||
@ -1180,10 +1186,10 @@ export const PromptInputSubmit = ({
|
||||
aria-label="Submit"
|
||||
// 被button{bgc:#fff}覆盖了,只能加"!"
|
||||
className={cn(
|
||||
"h-[40px] w-[140px] rounded-[10px] border-0 font-bold transition-all",
|
||||
"h-[36px] w-[36px] rounded-[50%] border-0 font-bold transition-all ",
|
||||
isDisabled
|
||||
? "cursor-not-allowed !bg-gray-200 text-gray-400"
|
||||
: "!bg-[#F0E8FB] text-[#8E47F0] hover:!bg-[#8E47F0] hover:text-[#FFFFFF]",
|
||||
? "cursor-not-allowed !bg-[#15003399] text-gray-400"
|
||||
: "!bg-[#150033] text-[#8E47F0] hover:text-[#FFFFFF]",
|
||||
className,
|
||||
)}
|
||||
size={size}
|
||||
@ -1192,8 +1198,8 @@ export const PromptInputSubmit = ({
|
||||
disabled={isDisabled}
|
||||
{...props}
|
||||
>
|
||||
{/* {children ?? Icon} */}
|
||||
{text}
|
||||
{children ?? Icon}
|
||||
{/* {text} */}
|
||||
</InputGroupButton>
|
||||
</Tooltip>
|
||||
);
|
||||
|
||||
@ -151,7 +151,7 @@ function WorkspaceToolButton({
|
||||
return (
|
||||
<PromptInputButton
|
||||
className={cn(
|
||||
"group h-full rounded-[10px] p-[10px]! hover:bg-ws-surface-subtle hover:text-ws-interactive-primary",
|
||||
"group h-full rounded-[10px] p-[10px]! hover:bg-ws-interactive-hover hover:text-ws-interactive-primary",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
@ -1050,7 +1050,7 @@ export function InputBox({
|
||||
</ModelSelector> */}
|
||||
<PromptInputTools>
|
||||
{/* 占位符 */}
|
||||
<div className="w-[150px] h-[40px]"></div>
|
||||
<div className="w-[36px] h-[36px]"></div>
|
||||
</PromptInputTools>
|
||||
</PromptInputFooter>
|
||||
<PromptInputSubmit
|
||||
|
||||
@ -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
|
||||
className="text-center text-[18px] leading-normal font-normal"
|
||||
colors={colors}
|
||||
>
|
||||
{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}
|
||||
>
|
||||
{t.welcome.greeting}
|
||||
{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}
|
||||
|
||||
39
frontend/src/core/brand/brand-session.test.ts
Normal file
39
frontend/src/core/brand/brand-session.test.ts
Normal file
@ -0,0 +1,39 @@
|
||||
import assert from "node:assert/strict";
|
||||
import test from "node:test";
|
||||
|
||||
const {
|
||||
BRAND_SESSION_STORAGE_KEY,
|
||||
getBrandRootClassName,
|
||||
parseBrandFromSearchParams,
|
||||
resolveBrandSession,
|
||||
} = await import(new URL("./index.ts", import.meta.url).href);
|
||||
|
||||
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("")), null);
|
||||
});
|
||||
|
||||
void test("resolveBrandSession falls back to default without url or storage", () => {
|
||||
assert.equal(resolveBrandSession({ urlBrand: null, storedBrand: null }), "default");
|
||||
});
|
||||
|
||||
void test("resolveBrandSession keeps stored sxwz when later url omits the flag", () => {
|
||||
assert.equal(resolveBrandSession({ urlBrand: null, storedBrand: "sxwz" }), "sxwz");
|
||||
});
|
||||
|
||||
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");
|
||||
});
|
||||
|
||||
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");
|
||||
});
|
||||
|
||||
void test("getBrandRootClassName returns stable workspace hook classes", () => {
|
||||
assert.equal(getBrandRootClassName("default"), "brand-default");
|
||||
assert.equal(getBrandRootClassName("sxwz"), "brand-sxwz");
|
||||
assert.equal(BRAND_SESSION_STORAGE_KEY, "deerflow.brand-session");
|
||||
});
|
||||
76
frontend/src/core/brand/index.ts
Normal file
76
frontend/src/core/brand/index.ts
Normal file
@ -0,0 +1,76 @@
|
||||
export const BRAND_SESSION_STORAGE_KEY = "deerflow.brand-session";
|
||||
export const DEFAULT_BRAND = "default" as const;
|
||||
const SXWZ_BRAND = "sxwz" as const;
|
||||
|
||||
export type Brand = typeof DEFAULT_BRAND | typeof SXWZ_BRAND;
|
||||
|
||||
export type BrandCopy = {
|
||||
productLabel: string;
|
||||
appName: string;
|
||||
appLogoSrc?: string;
|
||||
appLogoAlt?: string;
|
||||
};
|
||||
|
||||
export const BRAND_COPY: Record<Brand, BrandCopy> = {
|
||||
default: {
|
||||
productLabel: "轻办公",
|
||||
appName: "coxworker",
|
||||
appLogoSrc: "/coxwork.png",
|
||||
appLogoAlt: "coxworker",
|
||||
},
|
||||
sxwz: {
|
||||
productLabel: "在线教育智能体",
|
||||
appName: "coxstudy",
|
||||
},
|
||||
};
|
||||
|
||||
export function isBrand(value: string | null): value is Brand {
|
||||
return value === DEFAULT_BRAND || value === SXWZ_BRAND;
|
||||
}
|
||||
|
||||
export function parseBrandFromSearchParams(
|
||||
searchParams: URLSearchParams,
|
||||
): Brand | null {
|
||||
const value = searchParams.get("isSxwz");
|
||||
if (value === "true") return SXWZ_BRAND;
|
||||
if (value === "false") return DEFAULT_BRAND;
|
||||
return null;
|
||||
}
|
||||
|
||||
export function resolveBrandSession({
|
||||
urlBrand,
|
||||
storedBrand,
|
||||
}: {
|
||||
urlBrand: Brand | null;
|
||||
storedBrand: Brand | null;
|
||||
}): Brand {
|
||||
if (urlBrand === SXWZ_BRAND) {
|
||||
return SXWZ_BRAND;
|
||||
}
|
||||
|
||||
if (urlBrand === DEFAULT_BRAND) {
|
||||
return DEFAULT_BRAND;
|
||||
}
|
||||
|
||||
if (storedBrand === SXWZ_BRAND) {
|
||||
return SXWZ_BRAND;
|
||||
}
|
||||
|
||||
return DEFAULT_BRAND;
|
||||
}
|
||||
|
||||
export function getBrandRootClassName(brand: Brand): string {
|
||||
return brand === SXWZ_BRAND ? "brand-sxwz" : "brand-default";
|
||||
}
|
||||
|
||||
export function readStoredBrand(storage: Pick<Storage, "getItem">): Brand | null {
|
||||
const value = storage.getItem(BRAND_SESSION_STORAGE_KEY);
|
||||
return isBrand(value) ? value : null;
|
||||
}
|
||||
|
||||
export function writeStoredBrand(
|
||||
storage: Pick<Storage, "setItem">,
|
||||
brand: Brand,
|
||||
): void {
|
||||
storage.setItem(BRAND_SESSION_STORAGE_KEY, brand);
|
||||
}
|
||||
34
frontend/src/core/brand/provider-client.tsx
Normal file
34
frontend/src/core/brand/provider-client.tsx
Normal file
@ -0,0 +1,34 @@
|
||||
"use client";
|
||||
|
||||
import { useSearchParams } from "next/navigation";
|
||||
import { useLayoutEffect } from "react";
|
||||
|
||||
import { useBrand } from "./provider";
|
||||
|
||||
import {
|
||||
parseBrandFromSearchParams,
|
||||
readStoredBrand,
|
||||
resolveBrandSession,
|
||||
writeStoredBrand,
|
||||
} from "./index";
|
||||
|
||||
export function BrandSessionInitializer() {
|
||||
const searchParams = useSearchParams();
|
||||
const { setBrand } = useBrand();
|
||||
|
||||
useLayoutEffect(() => {
|
||||
const storedBrand = readStoredBrand(window.sessionStorage);
|
||||
const urlBrand = parseBrandFromSearchParams(
|
||||
new URLSearchParams(searchParams.toString()),
|
||||
);
|
||||
const resolvedBrand = resolveBrandSession({
|
||||
urlBrand,
|
||||
storedBrand,
|
||||
});
|
||||
|
||||
writeStoredBrand(window.sessionStorage, resolvedBrand);
|
||||
setBrand(resolvedBrand);
|
||||
}, [searchParams, setBrand]);
|
||||
|
||||
return null;
|
||||
}
|
||||
55
frontend/src/core/brand/provider.tsx
Normal file
55
frontend/src/core/brand/provider.tsx
Normal file
@ -0,0 +1,55 @@
|
||||
"use client";
|
||||
|
||||
import { createContext, useContext, useState, type ReactNode } from "react";
|
||||
|
||||
import {
|
||||
BRAND_COPY,
|
||||
DEFAULT_BRAND,
|
||||
getBrandRootClassName,
|
||||
type Brand,
|
||||
} from "./index";
|
||||
|
||||
type BrandContextValue = {
|
||||
brand: Brand;
|
||||
copy: (typeof BRAND_COPY)[Brand];
|
||||
rootClassName: string;
|
||||
setBrand: (brand: Brand) => void;
|
||||
};
|
||||
|
||||
const BrandContext = createContext<BrandContextValue | null>(null);
|
||||
|
||||
function getInitialBrand(): Brand {
|
||||
if (typeof window === "undefined") {
|
||||
return DEFAULT_BRAND;
|
||||
}
|
||||
|
||||
const storedBrand = window.sessionStorage.getItem("deerflow.brand-session");
|
||||
return storedBrand === "sxwz" ? "sxwz" : DEFAULT_BRAND;
|
||||
}
|
||||
|
||||
export function BrandProvider({ children }: { children: ReactNode }) {
|
||||
const [brand, setBrand] = useState<Brand>(getInitialBrand);
|
||||
|
||||
return (
|
||||
<BrandContext.Provider
|
||||
value={{
|
||||
brand,
|
||||
copy: BRAND_COPY[brand],
|
||||
rootClassName: getBrandRootClassName(brand),
|
||||
setBrand,
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</BrandContext.Provider>
|
||||
);
|
||||
}
|
||||
|
||||
export function useBrand() {
|
||||
const context = useContext(BrandContext);
|
||||
|
||||
if (!context) {
|
||||
throw new Error("useBrand must be used within BrandProvider");
|
||||
}
|
||||
|
||||
return context;
|
||||
}
|
||||
@ -68,6 +68,12 @@
|
||||
@source inline("bg-{background,muted,primary,secondary,accent}");
|
||||
@source inline("border-{border,input}");
|
||||
|
||||
.brand-default {
|
||||
}
|
||||
|
||||
.brand-sxwz {
|
||||
}
|
||||
|
||||
@custom-variant dark (&:is(.dark *));
|
||||
|
||||
@theme {
|
||||
@ -206,6 +212,7 @@
|
||||
--color-ws-surface-subtle: var(--ws-color-surface-subtle);
|
||||
--color-ws-surface-elevated: var(--ws-color-surface-elevated);
|
||||
--color-ws-interactive-primary: var(--ws-color-interactive-primary);
|
||||
--color-ws-interactive-hover: var(--ws-color-interactive-hover);
|
||||
--color-ws-line-default: var(--ws-color-line-default);
|
||||
--color-ws-text-muted: var(--ws-color-text-muted);
|
||||
--color-ws-icon-muted: var(--ws-color-icon-muted);
|
||||
@ -311,7 +318,8 @@
|
||||
--ws-color-fg-primary: #333333;
|
||||
--ws-color-surface-subtle: #f9f8fa;
|
||||
--ws-color-surface-elevated: #fbfafc;
|
||||
--ws-color-interactive-primary: #8e47f0;
|
||||
--ws-color-interactive-hover: #1500331A;
|
||||
--ws-color-interactive-primary: #150033;
|
||||
--ws-color-line-default: #e4e7ec;
|
||||
--ws-color-text-muted: #667085;
|
||||
--ws-color-icon-muted: #a3a1a1;
|
||||
@ -364,7 +372,7 @@
|
||||
--ws-color-fg-primary: #f5f5f5;
|
||||
--ws-color-surface-subtle: #1f1f1f;
|
||||
--ws-color-surface-elevated: #24222a;
|
||||
--ws-color-interactive-primary: #b987ff;
|
||||
--ws-color-interactive-primary: #150033;
|
||||
--ws-color-line-default: #3b3f48;
|
||||
--ws-color-text-muted: #98a2b3;
|
||||
--ws-color-icon-muted: #d0d0d0;
|
||||
|
||||
Loading…
Reference in New Issue
Block a user