diff --git a/frontend-web/public/agent-avatar-red.png b/frontend-web/public/agent-avatar-red.png new file mode 100644 index 00000000..229a7cda Binary files /dev/null and b/frontend-web/public/agent-avatar-red.png differ diff --git a/frontend-web/public/cm-assistant-logo-red.png b/frontend-web/public/cm-assistant-logo-red.png new file mode 100644 index 00000000..d2a9a38b Binary files /dev/null and b/frontend-web/public/cm-assistant-logo-red.png differ diff --git a/frontend-web/src/components/ui/sidebar.tsx b/frontend-web/src/components/ui/sidebar.tsx index f544abf3..8fd95752 100644 --- a/frontend-web/src/components/ui/sidebar.tsx +++ b/frontend-web/src/components/ui/sidebar.tsx @@ -27,7 +27,9 @@ import { const SIDEBAR_COOKIE_NAME = "sidebar_state"; const SIDEBAR_COOKIE_MAX_AGE = 60 * 60 * 24 * 7; -const SIDEBAR_WIDTH = "16rem"; +// Keep the workbench navigation compact across every theme. The visual theme +// only changes tokens; the information density should remain consistent. +const SIDEBAR_WIDTH = "14rem"; const SIDEBAR_WIDTH_MOBILE = "18rem"; const SIDEBAR_WIDTH_ICON = "3rem"; const SIDEBAR_KEYBOARD_SHORTCUT = "b"; diff --git a/frontend-web/src/components/workspace/agents/agent-brand-icon.tsx b/frontend-web/src/components/workspace/agents/agent-brand-icon.tsx index 4a82bf61..0364ff8b 100644 --- a/frontend-web/src/components/workspace/agents/agent-brand-icon.tsx +++ b/frontend-web/src/components/workspace/agents/agent-brand-icon.tsx @@ -1,7 +1,6 @@ "use client"; -import { BotIcon, SparklesIcon } from "lucide-react"; - +import { useTheme } from "@/contexts/ThemeContext"; import { cn } from "@/lib/utils"; type AgentBrandIconProps = { @@ -12,27 +11,15 @@ type AgentBrandIconProps = { const SIZE_STYLES = { nav: { frame: "size-[22px] rounded-md", - bot: "size-3.5", - badge: "-right-1 -top-1 size-2.5", - sparkle: "size-1.5", }, sm: { frame: "size-10 rounded-full", - bot: "size-[18px]", - badge: "-right-1 -top-1 size-3.5", - sparkle: "size-2", }, md: { frame: "size-12 rounded-full", - bot: "size-6", - badge: "-right-1 -top-1 size-4", - sparkle: "size-2.5", }, lg: { frame: "size-16 rounded-full", - bot: "size-7", - badge: "-right-1 -top-1 size-[18px]", - sparkle: "size-3", }, } as const; @@ -41,50 +28,26 @@ export function AgentBrandIcon({ size = "md", }: AgentBrandIconProps) { const styles = SIZE_STYLES[size]; - const showBadge = size === "nav"; - - if (size !== "nav") { - return ( - - - - ); - } + const { theme } = useTheme(); + const avatarSrc = + theme === "light" ? "/agent-avatar-red.png" : "/agent-avatar.png"; return ( - - {showBadge && ( - - - - )} + ); } diff --git a/frontend-web/src/components/workspace/agents/agent-card.tsx b/frontend-web/src/components/workspace/agents/agent-card.tsx index 62870b9a..2a4dad79 100644 --- a/frontend-web/src/components/workspace/agents/agent-card.tsx +++ b/frontend-web/src/components/workspace/agents/agent-card.tsx @@ -567,7 +567,7 @@ export function AgentCard({ agent, layout = "card", onPublisherClick }: AgentCar {isFromOthers && ( 来自他人 @@ -576,7 +576,7 @@ export function AgentCard({ agent, layout = "card", onPublisherClick }: AgentCar {(isOwned || isAdmin) && !isBuiltin && agent.published && !agent.is_private && ( @@ -675,7 +675,7 @@ export function AgentCard({ agent, layout = "card", onPublisherClick }: AgentCar {desensitize(group)} @@ -687,7 +687,7 @@ export function AgentCard({ agent, layout = "card", onPublisherClick }: AgentCar { e.stopPropagation(); @@ -841,8 +841,8 @@ export function AgentCard({ agent, layout = "card", onPublisherClick }: AgentCar size="sm" className={cn( isList - ? "shrink-0 rounded-xl bg-[#1f63e9] px-4 font-medium text-white hover:bg-[#1857d4]" - : "h-11 w-full rounded-lg border-[#b9d1ff] bg-white font-medium text-[#135ee8] shadow-none hover:border-[#79a8ff] hover:bg-[#f7faff] hover:text-[#135ee8] dark:border-blue-700 dark:bg-slate-900 dark:text-blue-300", + ? "bg-primary hover:bg-primary/90 shrink-0 rounded-xl px-4 font-medium text-primary-foreground" + : "border-primary/35 text-primary hover:border-primary/55 hover:bg-primary/5 h-11 w-full rounded-lg bg-white font-medium shadow-none dark:bg-slate-900 dark:text-primary", )} onClick={handleChat} > @@ -857,8 +857,8 @@ export function AgentCard({ agent, layout = "card", onPublisherClick }: AgentCar 私密 ) : agent.published || isBuiltin ? ( - - + + 已发布 ) : ( @@ -944,15 +944,15 @@ export function AgentCard({ agent, layout = "card", onPublisherClick }: AgentCar const cardLayout = ( {agent.featured_order != null ? ( <> - + 推荐 @@ -991,7 +991,7 @@ export function AgentCard({ agent, layout = "card", onPublisherClick }: AgentCar )} - + {cardStatusBadge} + diff --git a/frontend-web/src/components/workspace/agents/agent-gallery.tsx b/frontend-web/src/components/workspace/agents/agent-gallery.tsx index 0def486c..4a9bde5a 100644 --- a/frontend-web/src/components/workspace/agents/agent-gallery.tsx +++ b/frontend-web/src/components/workspace/agents/agent-gallery.tsx @@ -271,17 +271,17 @@ export function AgentGallery() { }; const renderEmpty = (description: string, showNewButton = false) => ( - + - {t.agents.emptyTitle} - + {t.agents.emptyTitle} + {description} {showNewButton && ( @@ -368,20 +368,20 @@ export function AgentGallery() { }; return ( - + {/* Page header */} - + - + {t.agents.title} - + 创建并管理专属的 AI 智能体,助力更高效的工作与决策。 {t.agents.newAgent} @@ -401,12 +401,12 @@ export function AgentGallery() { }} className="flex min-h-0 flex-1 flex-col overflow-hidden" > - - + + 我的 @@ -415,7 +415,7 @@ export function AgentGallery() { key={sq.id} value={sq.id} title={sq.description || sq.name} - className="h-14 rounded-none border-x-0 border-t-0 border-b-2 border-transparent bg-transparent px-5 text-[15px] shadow-none after:hidden data-[state=active]:border-x-0 data-[state=active]:border-t-0 data-[state=active]:!border-b-[#2166e8] data-[state=active]:bg-transparent data-[state=active]:text-[#2166e8] data-[state=active]:!shadow-none" + className="h-12 rounded-none border-x-0 border-t-0 border-b-2 border-transparent bg-transparent px-5 text-[15px] shadow-none after:hidden data-[state=active]:border-x-0 data-[state=active]:border-t-0 data-[state=active]:!border-b-primary data-[state=active]:bg-transparent data-[state=active]:text-primary data-[state=active]:!shadow-none" > {sq.name} @@ -423,7 +423,7 @@ export function AgentGallery() { {privateFeatureAvailable && ( 私密广场 @@ -433,7 +433,7 @@ export function AgentGallery() { 内置智能体 @@ -441,24 +441,24 @@ export function AgentGallery() { )} {showSearchBar && ( - - + + setSearch(event.target.value)} placeholder="搜索智能体名称或描述" - className="h-12 rounded-xl border-slate-200 bg-white pl-11 text-sm shadow-none placeholder:text-slate-400 focus-visible:border-blue-400 focus-visible:ring-blue-100 dark:border-slate-700 dark:bg-slate-900" + className="h-11 rounded-xl border-slate-200 bg-white pl-11 text-sm shadow-none placeholder:text-slate-400 focus-visible:border-primary/50 focus-visible:ring-primary/15 dark:border-slate-700 dark:bg-slate-900" /> {(activeTab === "private" ? isPrivateFetching : mainFetching) && ( - + )} setStatusFilter(value as AgentStatusFilter)} > - + @@ -471,7 +471,7 @@ export function AgentGallery() { setSort(v as AgentSort)}> @@ -506,7 +506,7 @@ export function AgentGallery() { setPublisher(null)} - className="inline-flex items-center gap-1 rounded-full bg-blue-50 px-3 py-1 text-xs font-medium text-blue-700 transition-colors hover:bg-blue-100 dark:bg-blue-500/15 dark:text-blue-300" + className="bg-primary/5 text-primary hover:bg-primary/10 inline-flex items-center gap-1 rounded-full px-3 py-1 text-xs font-medium transition-colors dark:bg-primary/15 dark:text-primary" title="点击清除发布者筛选" > {publisher.label} @@ -514,7 +514,7 @@ export function AgentGallery() { )} - + 共 {activeTotal} 个智能体 @@ -523,7 +523,7 @@ export function AgentGallery() { {renderListBody({ list: mainAgents, @@ -536,7 +536,7 @@ export function AgentGallery() { ))} - + {renderListBody({ list: mainAgents, total: mainTotal, @@ -549,7 +549,7 @@ export function AgentGallery() { {isAdmin && ( - + {renderListBody({ list: mainAgents, total: mainTotal, @@ -564,7 +564,7 @@ export function AgentGallery() { {privateFeatureAvailable && ( {renderPrivateTab()} diff --git a/frontend-web/src/components/workspace/cm-chat-sidebar.tsx b/frontend-web/src/components/workspace/cm-chat-sidebar.tsx index 6aa80501..1656736a 100644 --- a/frontend-web/src/components/workspace/cm-chat-sidebar.tsx +++ b/frontend-web/src/components/workspace/cm-chat-sidebar.tsx @@ -18,6 +18,7 @@ import type { MouseEvent } from "react"; import { useNavigate } from "react-router-dom"; import { cn } from "@/lib/utils"; +import { useTheme } from "@/contexts/ThemeContext"; import { AgentBrandIcon } from "./agents/agent-brand-icon"; import { RecentChatList } from "./recent-chat-list"; @@ -63,6 +64,8 @@ export function CmChatSidebar({ }) { const pathname = usePathname(); const navigate = useNavigate(); + const { theme } = useTheme(); + const brandLogoSrc = theme === "light" ? "/cm-assistant-logo-red.png" : "/cm-assistant-logo.png"; const handleNewChat = (event: MouseEvent) => { if (event.metaKey || event.ctrlKey || event.shiftKey || event.button === 1) return; @@ -78,10 +81,10 @@ export function CmChatSidebar({ onClick={onCollapse} aria-label="展开侧边栏" title="展开侧边栏" - className="mx-auto mt-2 flex size-11 shrink-0 items-center justify-center rounded-xl transition-colors hover:bg-slate-100 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-blue-500/40 dark:hover:bg-slate-900" + className="mx-auto mt-2 flex size-11 shrink-0 items-center justify-center rounded-xl transition-colors hover:bg-slate-100 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary/40 dark:hover:bg-slate-900" > @@ -105,9 +108,9 @@ export function CmChatSidebar({ aria-label={item.label} title={item.label} className={cn( - "flex size-10 items-center justify-center rounded-xl transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-blue-500/40", + "flex size-10 items-center justify-center rounded-xl transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary/40", active - ? "bg-[#edf4ff] text-[#165ee8] dark:bg-blue-500/15 dark:text-blue-300" + ? "bg-primary/10 text-primary dark:bg-primary/15 dark:text-primary" : "text-slate-600 hover:bg-slate-100 hover:text-slate-950 dark:text-slate-300 dark:hover:bg-slate-900 dark:hover:text-white", )} > @@ -127,7 +130,7 @@ export function CmChatSidebar({ href="/page/workspace/settings" aria-label="设置和更多" title="设置和更多" - className="flex size-10 items-center justify-center rounded-xl text-slate-600 transition-colors hover:bg-slate-100 hover:text-slate-950 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-blue-500/40 dark:text-slate-300 dark:hover:bg-slate-900 dark:hover:text-white" + className="flex size-10 items-center justify-center rounded-xl text-slate-600 transition-colors hover:bg-slate-100 hover:text-slate-950 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary/40 dark:text-slate-300 dark:hover:bg-slate-900 dark:hover:text-white" > @@ -138,27 +141,27 @@ export function CmChatSidebar({ return ( - - + + - CM 助手 + CM 助手 - + {NAV_ITEMS.map((item) => { const active = item.href === NEW_CHAT_PATH @@ -174,9 +177,9 @@ export function CmChatSidebar({ onClick={item.href === NEW_CHAT_PATH ? handleNewChat : undefined} aria-current={active ? "page" : undefined} className={cn( - "flex h-11 items-center gap-3 rounded-xl px-4 text-[14px] font-medium transition-colors", + "flex h-10 items-center gap-3 rounded-xl px-3 text-[14px] font-medium transition-colors", active - ? "bg-[#edf4ff] text-[#165ee8] dark:bg-blue-500/15 dark:text-blue-300" + ? "bg-primary/10 text-primary dark:bg-primary/15 dark:text-primary" : "text-slate-600 hover:bg-slate-100 hover:text-slate-950 dark:text-slate-300 dark:hover:bg-slate-900 dark:hover:text-white", )} > diff --git a/frontend-web/src/components/workspace/custom-prompt-menu.tsx b/frontend-web/src/components/workspace/custom-prompt-menu.tsx index 8d168b48..66c00845 100644 --- a/frontend-web/src/components/workspace/custom-prompt-menu.tsx +++ b/frontend-web/src/components/workspace/custom-prompt-menu.tsx @@ -119,7 +119,7 @@ export function CustomPromptBar({ title={prompt.content} className={cn( "border-border/60 bg-muted/40 text-foreground/80 hover:bg-muted hover:text-foreground hover:border-border max-w-[200px] shrink-0 truncate rounded-full border px-3 py-1 text-xs font-medium shadow-sm transition-colors", - compact && "border-slate-200 bg-white px-3.5 py-1.5 text-[12px] text-slate-600 shadow-none hover:border-blue-200 hover:bg-blue-50/60 hover:text-blue-700", + compact && "border-slate-200 bg-white px-3.5 py-1.5 text-[12px] text-slate-600 shadow-none hover:border-primary/25 hover:bg-primary/5 hover:text-primary", "dark:border-slate-600/70 dark:bg-slate-700/40 dark:text-slate-200 dark:hover:bg-slate-700", )} > @@ -151,7 +151,7 @@ export function CustomPromptBar({ className={cn( "text-muted-foreground hover:text-foreground hover:bg-muted/70 h-7 w-7 shrink-0 rounded-full", compact && - "h-9 w-9 rounded-xl border border-slate-200 bg-white text-slate-600 shadow-[0_6px_18px_-14px_rgba(15,23,42,0.4)] hover:border-blue-200 hover:bg-blue-50 hover:text-blue-700 dark:border-slate-700 dark:bg-slate-900", + "h-9 w-9 rounded-xl border border-slate-200 bg-white text-slate-600 shadow-[0_6px_18px_-14px_rgba(15,23,42,0.4)] hover:border-primary/25 hover:bg-primary/5 hover:text-primary dark:border-slate-700 dark:bg-slate-900", )} title="管理常用提示词" aria-label="管理常用提示词" diff --git a/frontend-web/src/components/workspace/input-box.tsx b/frontend-web/src/components/workspace/input-box.tsx index e63eea63..6e5c64c0 100644 --- a/frontend-web/src/components/workspace/input-box.tsx +++ b/frontend-web/src/components/workspace/input-box.tsx @@ -637,7 +637,7 @@ export function InputBox({ title="调整提示词顺序" aria-label="调整提示词顺序" onClick={() => setCompactPromptOrder((current) => current + 1)} - className="flex size-7 items-center justify-center rounded-full text-slate-400 transition-colors hover:bg-slate-100 hover:text-blue-600 focus-visible:outline-none focus-visible:ring-4 focus-visible:ring-blue-500/15 dark:hover:bg-slate-800 dark:hover:text-blue-300" + className="flex size-7 items-center justify-center rounded-full text-slate-400 transition-colors hover:bg-slate-100 hover:text-primary focus-visible:outline-none focus-visible:ring-4 focus-visible:ring-primary/15 dark:hover:bg-slate-800 dark:hover:text-primary" > @@ -650,7 +650,7 @@ export function InputBox({ type="button" data-quick-prompt={item.id} title={item.content} - className="inline-flex min-h-11 max-w-full items-center justify-center rounded-xl border border-[#e5e7eb] bg-white px-4 py-2 text-center text-[14px] font-normal leading-5 text-slate-700 shadow-[0_2px_6px_rgba(15,23,42,0.025)] transition-[background-color,border-color,box-shadow] hover:border-slate-300 hover:bg-slate-50 hover:text-slate-900 hover:shadow-[0_4px_10px_rgba(15,23,42,0.04)] focus-visible:outline-none focus-visible:ring-4 focus-visible:ring-blue-500/15 dark:border-slate-700 dark:bg-slate-900 dark:text-slate-200 dark:hover:border-slate-600 dark:hover:bg-slate-800" + className="inline-flex min-h-11 max-w-full items-center justify-center rounded-xl border border-[#e5e7eb] bg-white px-4 py-2 text-center text-[14px] font-normal leading-5 text-slate-700 shadow-[0_2px_6px_rgba(15,23,42,0.025)] transition-[background-color,border-color,box-shadow] hover:border-primary/25 hover:bg-primary/5 hover:text-slate-900 hover:shadow-[0_4px_10px_rgba(15,23,42,0.04)] focus-visible:outline-none focus-visible:ring-4 focus-visible:ring-primary/15 dark:border-slate-700 dark:bg-slate-900 dark:text-slate-200 dark:hover:border-primary/35 dark:hover:bg-primary/10" onClick={() => { textInput.setInput(item.content); requestAnimationFrame(() => @@ -748,7 +748,7 @@ export function InputBox({ : "*:data-[slot='input-group']:rounded-3xl", // 紧凑落地态(CM 参考图):白卡 + 细边 + 轻阴影 + 主色聚焦环(无旋转彩边) compact && isNewThread - ? "overflow-hidden rounded-[24px] border border-slate-200/90 bg-white shadow-[0_18px_50px_-32px_rgba(37,99,235,0.6),0_8px_24px_-20px_rgba(15,23,42,0.35)] transition-all duration-300 focus-within:border-blue-300 focus-within:ring-4 focus-within:ring-blue-500/10 dark:border-slate-700 dark:bg-slate-800" + ? "overflow-hidden rounded-[24px] border border-slate-200/90 bg-white shadow-[0_18px_50px_-32px_rgba(15,23,42,0.22),0_8px_24px_-20px_rgba(15,23,42,0.35)] transition-all duration-300 focus-within:border-primary/35 focus-within:ring-4 focus-within:ring-primary/10 dark:border-slate-700 dark:bg-slate-800" : // landing 态:透明,让外层 gemini-gradient-border 提供白卡 + 旋转描边 // 对话态:常规白卡 + 中性边框 + 轻阴影 isNewThread @@ -844,7 +844,7 @@ export function InputBox({ "gap-1.5! rounded-full px-3! shadow-none", compact && "rounded-lg px-2.5! py-1! text-[11px] font-medium", memoryInjectionEnabled - ? "border border-blue-300 bg-blue-50 text-blue-700 ring-1 ring-blue-300/35 hover:bg-blue-100 dark:border-cyan-300/70 dark:bg-cyan-400/20 dark:text-cyan-50 dark:ring-cyan-300/35 dark:shadow-[0_0_0_1px_rgba(34,211,238,0.18),0_0_18px_rgba(34,211,238,0.18)] dark:hover:bg-cyan-400/25" + ? "border border-primary/35 bg-primary/5 text-primary ring-1 ring-primary/20 hover:bg-primary/10 dark:border-primary/45 dark:bg-primary/15 dark:text-primary dark:ring-primary/25 dark:hover:bg-primary/20" : compact ? "border border-transparent bg-muted/40 text-muted-foreground hover:bg-muted hover:text-foreground" : "border-0 bg-transparent text-muted-foreground hover:bg-muted/70 hover:text-foreground dark:text-slate-400 dark:hover:bg-slate-700/70 dark:hover:text-slate-100", @@ -852,7 +852,7 @@ export function InputBox({ onClick={handleMemoryInjectionToggle} > {memoryInjectionEnabled && ( - + )} 记忆 @@ -1303,9 +1303,9 @@ export function InputBox({ {isCard ? ( - + setDetailOpen(true)} @@ -1075,10 +1075,10 @@ export function SkillRow({ ) : ( - - - setDetailOpen(true)}> - + + + setDetailOpen(true)}> + @@ -1097,7 +1097,7 @@ export function SkillRow({ }} className={cn( "mt-2 flex max-w-full items-center gap-1 text-xs text-slate-400", - skill.owner_user_id && onPublisherClick && "cursor-pointer hover:text-blue-600 hover:underline", + skill.owner_user_id && onPublisherClick && "cursor-pointer hover:text-primary hover:underline", )} > diff --git a/frontend-web/src/components/workspace/user-identity-badge.tsx b/frontend-web/src/components/workspace/user-identity-badge.tsx index a3101b56..b68241c5 100644 --- a/frontend-web/src/components/workspace/user-identity-badge.tsx +++ b/frontend-web/src/components/workspace/user-identity-badge.tsx @@ -45,7 +45,7 @@ export function UserIdentityBadge({ className={cn( "bg-muted text-muted-foreground relative flex size-7 shrink-0 items-center justify-center rounded-full", variant === "topbar" && - "size-8 bg-gradient-to-br from-blue-500 to-indigo-600 text-white shadow-[0_4px_12px_-6px_rgba(37,99,235,0.8)] ring-1 ring-blue-500/10 dark:from-blue-400 dark:to-indigo-500", + "bg-primary text-primary-foreground size-8 shadow-[0_4px_12px_-6px_rgba(15,23,42,0.26)] ring-1 ring-primary/15", )} > diff --git a/frontend-web/src/pages/WorkspaceRoutes.tsx b/frontend-web/src/pages/WorkspaceRoutes.tsx index be415952..c338f5df 100644 --- a/frontend-web/src/pages/WorkspaceRoutes.tsx +++ b/frontend-web/src/pages/WorkspaceRoutes.tsx @@ -136,7 +136,9 @@ export function WorkspaceLayout({ { '--page-sidebar-width': pageSidebarWidth, '--header-height': headerHidden ? '0rem' : '4rem', - '--sidebar-width': '18.5rem', + // Shared by red, blue, and dark workbench themes. Keep this in sync + // with the Sidebar primitive instead of giving one route a wider rail. + '--sidebar-width': '14rem', } as CSSProperties } key={String(compactWorkspaceSidebar)} diff --git a/frontend-web/src/styles/globals.css b/frontend-web/src/styles/globals.css index ce84a433..306e464a 100644 --- a/frontend-web/src/styles/globals.css +++ b/frontend-web/src/styles/globals.css @@ -1290,9 +1290,9 @@ html.lightblue .tiptap-writing-editor .ProseMirror ::selection { * agent surfaces on classic rgba()/hex colours instead. * -------------------------------------------------------------------------- */ .agent-avatar { - background-color: #edf4ff; - color: #1f5fdb; - box-shadow: inset 0 0 0 1px rgba(59, 130, 246, 0.16); + background-color: #fff1f0; + color: #be2323; + box-shadow: inset 0 0 0 1px rgba(190, 35, 35, 0.16); } .dark .agent-avatar {
{t.agents.emptyTitle}
+
{description}
创建并管理专属的 AI 智能体,助力更高效的工作与决策。