Commit 468ea511 authored by edy's avatar edy

feat(ui): 左侧导航栏移植Windows客户端透明风格UI,新增搜索过滤功能

- 侧边栏背景/导航按钮/专家分类/专家条目/会话卡片改为透明若隐若现风格
- 新增侧边栏搜索框,支持过滤导航项、专家列表、会话列表
- 去掉品牌名千匠问天四字,仅保留logo图标
- 清理.sidebar-brand-name死代码和重复的.sidebar-filter-empty样式
- 修复sidebarKnowledgeSource测试用例以适配新代码结构
Co-Authored-By: 's avatarClaude Opus 4.8 <noreply@anthropic.com>
parent 0d0a22ea
Pipeline #18503 failed
import type { ReactNode } from "react" import { useMemo, useState, type ReactNode } from "react"
import type { ProjectSessionSummary } from "@qjclaw/shared-types" import type { ProjectSessionSummary } from "@qjclaw/shared-types"
import { Sidebar } from "./Sidebar" import { Sidebar } from "./Sidebar"
import { ExpertTree, type ExpertCategoryId, type ExpertVisualKey, type SidebarExpertEntry } from "./ExpertTree" import { ExpertTree, type ExpertCategoryId, type ExpertVisualKey, type SidebarExpertEntry } from "./ExpertTree"
...@@ -69,6 +69,44 @@ export function AppSidebar({ ...@@ -69,6 +69,44 @@ export function AppSidebar({
onOpenSession, onOpenSession,
onCloseSession onCloseSession
}: AppSidebarProps) { }: AppSidebarProps) {
const [sidebarSearchQuery, setSidebarSearchQuery] = useState("")
const normalizedSidebarSearchQuery = sidebarSearchQuery.trim().toLocaleLowerCase()
const isSearchingSidebar = normalizedSidebarSearchQuery.length > 0
const matchesSidebarSearch = (value: string) => value.toLocaleLowerCase().includes(normalizedSidebarSearchQuery)
const filteredSessions = useMemo(
() => {
if (!isSearchingSidebar) {
return sessions
}
if (matchesSidebarSearch(sidebarSessionLabel)) {
return sessions
}
return sessions.filter((session, index) => {
const title = sidebarSessionTitles[session.id] ?? formatSessionTitle(session.title, index)
return matchesSidebarSearch(title)
})
},
[formatSessionTitle, isSearchingSidebar, normalizedSidebarSearchQuery, sessions, sidebarSessionLabel, sidebarSessionTitles]
)
const headerContent = (
<label className="sidebar-search app-no-drag">
<span className="sidebar-search-icon" aria-hidden="true">
<svg viewBox="0 0 20 20" focusable="false">
<path d="m14.2 14.2 2.8 2.8M8.9 15.1a6.2 6.2 0 1 1 0-12.4 6.2 6.2 0 0 1 0 12.4Z" />
</svg>
</span>
<input
type="search"
value={sidebarSearchQuery}
placeholder="搜索"
aria-label="搜索侧栏"
onChange={(event) => setSidebarSearchQuery(event.target.value)}
/>
</label>
)
const topContent = ( const topContent = (
<> <>
<nav className="nav-list" aria-label="主导航"> <nav className="nav-list" aria-label="主导航">
...@@ -78,7 +116,7 @@ export function AppSidebar({ ...@@ -78,7 +116,7 @@ export function AppSidebar({
{ id: "knowledge" as const, label: ui.knowledge }, { id: "knowledge" as const, label: ui.knowledge },
{ id: "automation" as const, label: "自动化任务" }, { id: "automation" as const, label: "自动化任务" },
{ id: "settings" as const, label: ui.settings } { id: "settings" as const, label: ui.settings }
].map((item) => ( ].filter((item) => !isSearchingSidebar || matchesSidebarSearch(item.label)).map((item) => (
<button <button
key={item.id} key={item.id}
type="button" type="button"
...@@ -93,9 +131,7 @@ export function AppSidebar({ ...@@ -93,9 +131,7 @@ export function AppSidebar({
</button> </button>
))} ))}
</nav> </nav>
<div className="conversation-sidebar-action"> {!showBindEntry ? <div className="conversation-sidebar-action">{sidebarNewSessionAction}</div> : null}
{!showBindEntry ? sidebarNewSessionAction : null}
</div>
</> </>
) )
...@@ -107,6 +143,7 @@ export function AppSidebar({ ...@@ -107,6 +143,7 @@ export function AppSidebar({
viewMode={viewMode} viewMode={viewMode}
prompt={prompt} prompt={prompt}
activeProjectId={activeProjectId} activeProjectId={activeProjectId}
searchQuery={normalizedSidebarSearchQuery}
onToggleCategory={onToggleCategory} onToggleCategory={onToggleCategory}
onExpertSelect={onExpertSelect} onExpertSelect={onExpertSelect}
renderCategoryIcon={renderCategoryIcon} renderCategoryIcon={renderCategoryIcon}
...@@ -115,7 +152,8 @@ export function AppSidebar({ ...@@ -115,7 +152,8 @@ export function AppSidebar({
<SessionList <SessionList
visible={!showBindEntry} visible={!showBindEntry}
label={sidebarSessionLabel} label={sidebarSessionLabel}
sessions={sessions} sessions={filteredSessions}
totalSessionCount={sessions.length}
activeSessionId={activeSessionId} activeSessionId={activeSessionId}
sessionTitles={sidebarSessionTitles} sessionTitles={sidebarSessionTitles}
projectActionPending={projectActionPending} projectActionPending={projectActionPending}
...@@ -123,6 +161,7 @@ export function AppSidebar({ ...@@ -123,6 +161,7 @@ export function AppSidebar({
activeStreamSessionId={activeStreamSessionId} activeStreamSessionId={activeStreamSessionId}
closeLabel={ui.closeSession} closeLabel={ui.closeSession}
formatSessionTitle={formatSessionTitle} formatSessionTitle={formatSessionTitle}
isFiltering={isSearchingSidebar}
onOpenSession={onOpenSession} onOpenSession={onOpenSession}
onCloseSession={onCloseSession} onCloseSession={onCloseSession}
/> />
...@@ -133,6 +172,7 @@ export function AppSidebar({ ...@@ -133,6 +172,7 @@ export function AppSidebar({
<Sidebar <Sidebar
topContent={topContent} topContent={topContent}
bottomContent={bottomContent} bottomContent={bottomContent}
headerContent={headerContent}
isCollapsed={isCollapsed} isCollapsed={isCollapsed}
onToggleCollapsed={onToggleCollapsed} onToggleCollapsed={onToggleCollapsed}
/> />
......
...@@ -107,6 +107,7 @@ interface ExpertTreeProps { ...@@ -107,6 +107,7 @@ interface ExpertTreeProps {
viewMode: "chat" | "experts" | "tasks" | "automation" | "plugins" | "settings" | "knowledge" viewMode: "chat" | "experts" | "tasks" | "automation" | "plugins" | "settings" | "knowledge"
prompt: string prompt: string
activeProjectId?: string activeProjectId?: string
searchQuery?: string
onToggleCategory(categoryId: string): void onToggleCategory(categoryId: string): void
onExpertSelect(entry: SidebarExpertEntry): void onExpertSelect(entry: SidebarExpertEntry): void
renderCategoryIcon(categoryId: ExpertCategoryId): ReactNode renderCategoryIcon(categoryId: ExpertCategoryId): ReactNode
...@@ -119,11 +120,40 @@ export function ExpertTree({ ...@@ -119,11 +120,40 @@ export function ExpertTree({
viewMode, viewMode,
prompt, prompt,
activeProjectId, activeProjectId,
searchQuery = "",
onToggleCategory, onToggleCategory,
onExpertSelect, onExpertSelect,
renderCategoryIcon, renderCategoryIcon,
renderExpertIcon renderExpertIcon
}: ExpertTreeProps) { }: ExpertTreeProps) {
const normalizedSearchQuery = searchQuery.trim().toLocaleLowerCase()
const isSearching = normalizedSearchQuery.length > 0
const matchesSearch = (value: string) => value.toLocaleLowerCase().includes(normalizedSearchQuery)
const sectionMatches = isSearching && matchesSearch("数字员工")
const visibleCategories = CATEGORY_CONFIG.map((category) => {
const categoryEntries = entries.filter((entry) => expertMatchesCategory(entry, category.id))
const categoryMatches = sectionMatches || (isSearching && matchesSearch(category.name))
const categoryExperts = isSearching && !categoryMatches
? categoryEntries.filter((entry) => matchesSearch(entry.displayName) || matchesSearch(entry.definition.id))
: categoryEntries
const hasMatchingExperts = categoryExperts.length > 0
if (isSearching && !categoryMatches && !hasMatchingExperts) {
return null
}
return {
category,
categoryExperts,
hasExperts: categoryEntries.length > 0,
isExpanded: isSearching ? hasMatchingExperts : expandedCategories[category.id] || false
}
}).filter((categoryModel): categoryModel is NonNullable<typeof categoryModel> => categoryModel !== null)
if (isSearching && visibleCategories.length === 0) {
return null
}
return ( return (
<section className="sidebar-section compact sidebar-experts-entry"> <section className="sidebar-section compact sidebar-experts-entry">
<div className="sidebar-section-head sidebar-digital-workers-title"> <div className="sidebar-section-head sidebar-digital-workers-title">
...@@ -134,11 +164,8 @@ export function ExpertTree({ ...@@ -134,11 +164,8 @@ export function ExpertTree({
</div> </div>
<div className="sidebar-expert-scroll"> <div className="sidebar-expert-scroll">
<div className="expert-category-list"> <div className="expert-category-list">
{CATEGORY_CONFIG.map((category) => { {visibleCategories.map(({ category, categoryExperts, hasExperts, isExpanded }) => {
const categoryExperts = entries.filter((entry) => expertMatchesCategory(entry, category.id))
const isExpanded = expandedCategories[category.id] || false
const categoryPanelId = `expert-tree-${category.id}` const categoryPanelId = `expert-tree-${category.id}`
const hasExperts = categoryExperts.length > 0
return ( return (
<div key={category.id} className={"expert-category-item expert-tree-category" + (isExpanded && hasExperts ? " expanded" : "")}> <div key={category.id} className={"expert-category-item expert-tree-category" + (isExpanded && hasExperts ? " expanded" : "")}>
......
...@@ -6,6 +6,7 @@ interface SessionListProps { ...@@ -6,6 +6,7 @@ interface SessionListProps {
visible: boolean visible: boolean
label: string label: string
sessions: ProjectSessionSummary[] sessions: ProjectSessionSummary[]
totalSessionCount?: number
activeSessionId: string activeSessionId: string
sessionTitles: Record<string, string> sessionTitles: Record<string, string>
projectActionPending: boolean projectActionPending: boolean
...@@ -13,6 +14,7 @@ interface SessionListProps { ...@@ -13,6 +14,7 @@ interface SessionListProps {
activeStreamSessionId?: string activeStreamSessionId?: string
closeLabel: string closeLabel: string
formatSessionTitle(title: string, index: number): string formatSessionTitle(title: string, index: number): string
isFiltering?: boolean
onOpenSession(session: ProjectSessionSummary): void onOpenSession(session: ProjectSessionSummary): void
onCloseSession(sessionId: string): void onCloseSession(sessionId: string): void
} }
...@@ -21,6 +23,7 @@ export function SessionList({ ...@@ -21,6 +23,7 @@ export function SessionList({
visible, visible,
label, label,
sessions, sessions,
totalSessionCount,
activeSessionId, activeSessionId,
sessionTitles, sessionTitles,
projectActionPending, projectActionPending,
...@@ -28,6 +31,7 @@ export function SessionList({ ...@@ -28,6 +31,7 @@ export function SessionList({
activeStreamSessionId, activeStreamSessionId,
closeLabel, closeLabel,
formatSessionTitle, formatSessionTitle,
isFiltering = false,
onOpenSession, onOpenSession,
onCloseSession onCloseSession
}: SessionListProps) { }: SessionListProps) {
...@@ -35,6 +39,8 @@ export function SessionList({ ...@@ -35,6 +39,8 @@ export function SessionList({
return null return null
} }
const effectiveTotal = totalSessionCount ?? sessions.length
return ( return (
<section className="sidebar-section sidebar-section-fill compact sidebar-session-section"> <section className="sidebar-section sidebar-section-fill compact sidebar-session-section">
<div className="sidebar-section-head sidebar-section-head-subtle"> <div className="sidebar-section-head sidebar-section-head-subtle">
...@@ -44,33 +50,37 @@ export function SessionList({ ...@@ -44,33 +50,37 @@ export function SessionList({
</div> </div>
</div> </div>
<div className="sidebar-session-list"> <div className="sidebar-session-list">
{sessions.map((session, index) => ( {sessions.length > 0 ? (
<div key={session.id} className={"sidebar-session-card" + (activeSessionId === session.id ? " active" : "")}> sessions.map((session, index) => (
<button <div key={session.id} className={"sidebar-session-card" + (activeSessionId === session.id ? " active" : "")}>
type="button"
className="sidebar-session-main app-no-drag"
aria-current={activeSessionId === session.id ? "true" : undefined}
disabled={projectActionPending}
onClick={() => onOpenSession(session)}
>
<strong>{sessionTitles[session.id] ?? formatSessionTitle(session.title, index)}{session.isChannelSession ? <span className="sidebar-session-tag">飞书</span> : null}</strong>
</button>
{sessions.length > 1 ? (
<button <button
type="button" type="button"
className="sidebar-session-close app-no-drag" className="sidebar-session-main app-no-drag"
aria-label={closeLabel} aria-current={activeSessionId === session.id ? "true" : undefined}
title={closeLabel} disabled={projectActionPending}
disabled={projectActionPending || (sendPhase !== "idle" && activeStreamSessionId === session.id)} onClick={() => onOpenSession(session)}
onClick={() => onCloseSession(session.id)}
> >
<svg viewBox="0 0 16 16" aria-hidden="true" focusable="false"> <strong>{sessionTitles[session.id] ?? formatSessionTitle(session.title, index)}{session.isChannelSession ? <span className="sidebar-session-tag">飞书</span> : null}</strong>
<path d="M4.25 4.25 11.75 11.75M11.75 4.25 4.25 11.75" />
</svg>
</button> </button>
) : null} {effectiveTotal > 1 ? (
</div> <button
))} type="button"
className="sidebar-session-close app-no-drag"
aria-label={closeLabel}
title={closeLabel}
disabled={projectActionPending || (sendPhase !== "idle" && activeStreamSessionId === session.id)}
onClick={() => onCloseSession(session.id)}
>
<svg viewBox="0 0 16 16" aria-hidden="true" focusable="false">
<path d="M4.25 4.25 11.75 11.75M11.75 4.25 4.25 11.75" />
</svg>
</button>
) : null}
</div>
))
) : isFiltering ? (
<div className="sidebar-filter-empty">没有匹配会话</div>
) : null}
</div> </div>
</section> </section>
) )
......
...@@ -4,16 +4,23 @@ import brandIcon from "../../assets/brand-icon.png" ...@@ -4,16 +4,23 @@ import brandIcon from "../../assets/brand-icon.png"
interface SidebarProps { interface SidebarProps {
topContent: ReactNode topContent: ReactNode
bottomContent: ReactNode bottomContent: ReactNode
headerContent?: ReactNode
isCollapsed: boolean isCollapsed: boolean
onToggleCollapsed(): void onToggleCollapsed(): void
} }
export function Sidebar({ topContent, bottomContent, isCollapsed, onToggleCollapsed }: SidebarProps) { export function Sidebar({ topContent, bottomContent, headerContent, isCollapsed, onToggleCollapsed }: SidebarProps) {
return ( return (
<aside className="sidebar conversation-sidebar-layout app-drag-region"> <aside className="sidebar conversation-sidebar-layout app-drag-region">
<div className="sidebar-brand"> <div className="sidebar-brand">
<img src={brandIcon} alt="" className="sidebar-brand-logo" /> {!isCollapsed ? (
<strong className="sidebar-brand-name">千匠问天</strong> <img src={brandIcon} alt="" className="sidebar-brand-logo" />
) : null}
{!isCollapsed && headerContent ? (
<div className="sidebar-header-content">
{headerContent}
</div>
) : null}
<button <button
type="button" type="button"
className="sidebar-collapse-button app-no-drag" className="sidebar-collapse-button app-no-drag"
......
...@@ -15,6 +15,10 @@ ...@@ -15,6 +15,10 @@
grid-template-columns: 64px minmax(0, 1fr); grid-template-columns: 64px minmax(0, 1fr);
} }
.shell.sidebar-collapsed {
grid-template-columns: 64px minmax(0, 1fr);
}
.skip-link { .skip-link {
position: fixed; position: fixed;
top: 10px; top: 10px;
...@@ -48,6 +52,19 @@ ...@@ -48,6 +52,19 @@
box-shadow: inset -1px 0 0 rgba(148, 163, 184, 0.22); box-shadow: inset -1px 0 0 rgba(148, 163, 184, 0.22);
} }
.sidebar-filter-empty {
min-height: 42px;
display: grid;
place-items: center;
padding: 0 12px;
border-radius: 10px;
border: 1px dashed rgba(96, 165, 250, 0.28);
background: rgba(255, 255, 255, 0.52);
color: #64748b;
font-size: 13px;
line-height: 1.4;
}
.nav-list, .nav-list,
.page-stack, .page-stack,
.catalog-list, .catalog-list,
...@@ -129,7 +146,7 @@ ...@@ -129,7 +146,7 @@
overflow-y: auto; overflow-y: auto;
overflow-x: hidden; overflow-x: hidden;
scrollbar-width: thin; scrollbar-width: thin;
scrollbar-color: rgba(96, 165, 250, 0.34) transparent; scrollbar-color: rgba(94, 164, 216, 0.44) transparent;
} }
.sidebar-expert-scroll::-webkit-scrollbar { .sidebar-expert-scroll::-webkit-scrollbar {
...@@ -138,16 +155,41 @@ ...@@ -138,16 +155,41 @@
.sidebar-expert-scroll::-webkit-scrollbar-track { .sidebar-expert-scroll::-webkit-scrollbar-track {
background: transparent; background: transparent;
border-radius: 2px;
} }
.sidebar-expert-scroll::-webkit-scrollbar-thumb { .sidebar-expert-scroll::-webkit-scrollbar-thumb {
background: rgba(96, 165, 250, 0.34); background: rgba(94, 164, 216, 0.44);
border-radius: 2px; border-radius: 999px;
transition: background 0.2s ease; }
.sidebar-expert-scroll:hover::-webkit-scrollbar-thumb,
.sidebar-expert-scroll:focus-within::-webkit-scrollbar-thumb {
background: rgba(37, 99, 235, 0.42);
}
.sidebar-session-list {
min-height: 0;
overflow-y: auto;
overflow-x: hidden;
scrollbar-width: thin;
scrollbar-color: rgba(94, 164, 216, 0.44) transparent;
}
.sidebar-session-list::-webkit-scrollbar {
width: 4px;
}
.sidebar-session-list::-webkit-scrollbar-track {
background: transparent;
}
.sidebar-session-list::-webkit-scrollbar-thumb {
background: rgba(94, 164, 216, 0.44);
border-radius: 999px;
} }
.sidebar-expert-scroll::-webkit-scrollbar-thumb:hover { .sidebar-session-list:hover::-webkit-scrollbar-thumb,
.sidebar-session-list:focus-within::-webkit-scrollbar-thumb {
background: rgba(37, 99, 235, 0.42); background: rgba(37, 99, 235, 0.42);
} }
......
...@@ -56,10 +56,12 @@ ...@@ -56,10 +56,12 @@
position: relative; position: relative;
height: 100vh; height: 100vh;
overflow: hidden; overflow: hidden;
background: linear-gradient(180deg, #eaf5ff 0%, #d5eaff 48%, #bddbfa 100%); background:
border-right: 1px solid rgba(37, 99, 235, 0.34); linear-gradient(180deg, rgba(255, 255, 255, 0.36), rgba(255, 255, 255, 0)),
linear-gradient(180deg, #dbeafe 0%, #c7ddf8 100%);
border-right: 1px solid rgba(37, 99, 235, 0.14);
border-bottom: 0; border-bottom: 0;
box-shadow: inset -1px 0 0 rgba(255, 255, 255, 0.66); box-shadow: none;
} }
.shell.openclaw-theme > .sidebar::before, .shell.openclaw-theme > .sidebar::before,
...@@ -71,11 +73,17 @@ ...@@ -71,11 +73,17 @@
.shell.openclaw-theme > .sidebar::before { .shell.openclaw-theme > .sidebar::before {
inset: 0; inset: 0;
background: linear-gradient(145deg, rgba(255, 255, 255, 0.44) 0%, rgba(255, 255, 255, 0) 34%, rgba(37, 99, 235, 0.1) 100%); background: linear-gradient(145deg, rgba(255, 255, 255, 0.18) 0%, rgba(255, 255, 255, 0) 32%, rgba(37, 99, 235, 0.06) 100%);
} }
.shell.openclaw-theme > .sidebar::after { .shell.openclaw-theme > .sidebar::after {
content: none; top: -14%;
left: -18%;
width: 72%;
height: 34%;
border-radius: 999px;
background: radial-gradient(circle, rgba(255, 255, 255, 0.5) 0%, rgba(255, 255, 255, 0) 72%);
opacity: 0.5;
} }
.shell.openclaw-theme .conversation-sidebar-layout { .shell.openclaw-theme .conversation-sidebar-layout {
...@@ -88,11 +96,12 @@ ...@@ -88,11 +96,12 @@
.shell.openclaw-theme .sidebar-brand { .shell.openclaw-theme .sidebar-brand {
position: relative; position: relative;
z-index: 1; z-index: 1;
display: grid; min-width: 0;
grid-template-columns: 32px minmax(0, 1fr) 28px; min-height: 42px;
display: flex;
align-items: center; align-items: center;
gap: 10px; gap: 8px;
min-height: 36px; justify-content: flex-end;
} }
.shell.openclaw-theme .sidebar-brand-logo { .shell.openclaw-theme .sidebar-brand-logo {
...@@ -102,40 +111,100 @@ ...@@ -102,40 +111,100 @@
object-fit: contain; object-fit: contain;
} }
.shell.openclaw-theme .sidebar-brand-name { .shell.openclaw-theme .sidebar-header-content {
min-width: 0; min-width: 0;
overflow: hidden; flex: 1 1 auto;
}
.shell.openclaw-theme .sidebar-search {
position: relative;
min-width: 0;
height: 34px;
display: flex;
align-items: center;
color: #41657f;
}
.shell.openclaw-theme .sidebar-search input {
width: 100%;
height: 34px;
min-width: 0;
padding: 0 32px 0 34px;
border-radius: 999px;
border: 1px solid transparent;
background: rgba(255, 255, 255, 0.4);
color: #12355f; color: #12355f;
font-size: 15px; font: inherit;
font-weight: 800; font-size: 13px;
line-height: 1.3; outline: none;
text-overflow: ellipsis; box-shadow: none;
white-space: nowrap; transition: background 160ms ease, border-color 160ms ease, box-shadow 160ms ease;
}
.shell.openclaw-theme .sidebar-search input::placeholder {
color: #6b87a0;
}
.shell.openclaw-theme .sidebar-search input:focus {
border-color: rgba(37, 99, 235, 0.4);
background: rgba(255, 255, 255, 0.8);
box-shadow: 0 0 0 3px rgba(37, 99, 235, 0.08);
}
.shell.openclaw-theme .sidebar-search-icon {
position: absolute;
z-index: 1;
display: grid;
place-items: center;
color: currentColor;
left: 11px;
width: 16px;
height: 16px;
pointer-events: none;
}
.shell.openclaw-theme .sidebar-search-icon svg {
width: 100%;
height: 100%;
display: block;
fill: none;
stroke: currentColor;
stroke-width: 1.8;
stroke-linecap: round;
stroke-linejoin: round;
} }
.shell.openclaw-theme .sidebar-collapse-button { .shell.openclaw-theme .sidebar-collapse-button {
width: 28px; width: 30px;
height: 28px; height: 30px;
padding: 0; min-width: 30px;
display: inline-grid; display: grid;
place-items: center; place-items: center;
border: 1px solid rgba(96, 165, 250, 0.28); padding: 0;
border-radius: 8px; border-radius: 9px;
background: rgba(255, 255, 255, 0.74); border: 1px solid transparent;
color: #2563eb; background: transparent;
color: #1d4ed8;
box-shadow: none; box-shadow: none;
transition: border-color 180ms ease, background 180ms ease, color 180ms ease; transition: background 160ms ease, border-color 160ms ease, color 160ms ease, box-shadow 160ms ease;
} }
.shell.openclaw-theme .sidebar-collapse-button:hover:not(:disabled) { .shell.openclaw-theme .sidebar-collapse-button:hover:not(:disabled) {
border-color: rgba(37, 99, 235, 0.48); border-color: rgba(37, 99, 235, 0.24);
background: #ffffff; background: rgba(255, 255, 255, 0.7);
color: #1d4ed8; color: #0f6eea;
box-shadow: none;
} }
.shell.openclaw-theme .sidebar-collapse-button svg { .shell.openclaw-theme .sidebar-collapse-button svg {
width: 18px; width: 18px;
height: 18px; height: 18px;
display: block;
fill: none;
stroke: currentColor;
stroke-width: 2;
stroke-linecap: round;
stroke-linejoin: round;
} }
.shell.openclaw-theme .sidebar-top { .shell.openclaw-theme .sidebar-top {
...@@ -154,14 +223,14 @@ ...@@ -154,14 +223,14 @@
min-height: 42px; min-height: 42px;
padding: 0 12px 0 14px; padding: 0 12px 0 14px;
border-radius: 14px; border-radius: 14px;
border: 1px solid rgba(96, 165, 250, 0.18); border: 1px solid transparent;
background: rgba(255, 255, 255, 0.36); background: transparent;
color: #2e516c; color: #2e516c;
justify-content: flex-start; justify-content: flex-start;
box-shadow: none; box-shadow: none;
backdrop-filter: none; backdrop-filter: none;
-webkit-backdrop-filter: none; -webkit-backdrop-filter: none;
transition: border-color 180ms ease, background 180ms ease, box-shadow 180ms ease, color 180ms ease; transition: border-color 180ms ease, background 180ms ease, box-shadow 180ms ease, color 180ms ease, transform 180ms ease;
} }
.shell.openclaw-theme .nav-item-icon svg { .shell.openclaw-theme .nav-item-icon svg {
...@@ -174,8 +243,8 @@ ...@@ -174,8 +243,8 @@
} }
.shell.openclaw-theme .nav-item:hover:not(.active) { .shell.openclaw-theme .nav-item:hover:not(.active) {
background: rgba(255, 255, 255, 0.62); background: rgba(255, 255, 255, 0.54);
border-color: rgba(96, 165, 250, 0.42); border-color: rgba(96, 165, 250, 0.22);
color: var(--revamp-blue-strong); color: var(--revamp-blue-strong);
box-shadow: none; box-shadow: none;
} }
...@@ -185,8 +254,8 @@ ...@@ -185,8 +254,8 @@
border-color: var(--revamp-blue-border); border-color: var(--revamp-blue-border);
color: var(--revamp-blue-strong); color: var(--revamp-blue-strong);
box-shadow: box-shadow:
inset 4px 0 0 var(--revamp-blue), inset 3px 0 0 var(--revamp-blue),
var(--revamp-shadow-soft); 0 4px 12px rgba(37, 99, 235, 0.08);
} }
.shell.openclaw-theme .nav-item.active::before { .shell.openclaw-theme .nav-item.active::before {
...@@ -200,22 +269,22 @@ ...@@ -200,22 +269,22 @@
.shell.openclaw-theme .sidebar-new-session.conversation-new-session { .shell.openclaw-theme .sidebar-new-session.conversation-new-session {
width: 100%; width: 100%;
min-height: 42px; min-height: 44px;
justify-content: center; justify-content: center;
padding: 0 12px; padding: 0 16px;
border-radius: 14px; border-radius: 14px;
border-color: rgba(29, 78, 216, 0.18); border-color: transparent;
background: linear-gradient(135deg, #2563eb, #0f6eea); background: linear-gradient(135deg, #2563eb, #0f6eea);
color: #ffffff; color: #ffffff;
box-shadow: var(--revamp-shadow-soft); box-shadow: 0 4px 12px rgba(37, 99, 235, 0.16);
transition: border-color 180ms ease, background 180ms ease, box-shadow 180ms ease, color 180ms ease; transition: border-color 180ms ease, background 180ms ease, box-shadow 180ms ease, color 180ms ease, transform 180ms ease;
} }
.shell.openclaw-theme .sidebar-new-session.conversation-new-session:hover:not(:disabled) { .shell.openclaw-theme .sidebar-new-session.conversation-new-session:hover:not(:disabled) {
background: linear-gradient(135deg, #1d4ed8, #075bd8); background: linear-gradient(135deg, #1d4ed8, #075bd8);
border-color: rgba(29, 78, 216, 0.32); border-color: transparent;
color: #ffffff; color: #ffffff;
box-shadow: var(--revamp-shadow); box-shadow: 0 6px 14px rgba(37, 99, 235, 0.2);
} }
.shell.openclaw-theme .conversation-new-session-plus { .shell.openclaw-theme .conversation-new-session-plus {
...@@ -246,108 +315,100 @@ ...@@ -246,108 +315,100 @@
padding-right: 0; padding-right: 0;
} }
.shell.openclaw-theme .conversation-new-session,
.shell.openclaw-theme .sidebar-experts-entry,
.shell.openclaw-theme .sidebar-session-section,
.shell.openclaw-theme .expert-category-item,
.shell.openclaw-theme .sidebar-session-card {
border-color: var(--revamp-border);
background: rgba(255, 255, 255, 0.62);
box-shadow: none;
}
.shell.openclaw-theme .sidebar-experts-entry {
min-height: 0;
display: grid;
grid-template-rows: auto minmax(0, 1fr);
gap: 8px;
overflow: hidden;
padding: 12px;
border-radius: 12px;
background: rgba(255, 255, 255, 0.82);
border-color: rgba(59, 130, 246, 0.16);
box-shadow: none;
}
.shell.openclaw-theme .sidebar-session-section {
min-height: 0;
display: grid;
grid-template-rows: auto minmax(0, 1fr);
overflow: hidden;
padding: 12px;
border-radius: 12px;
background: rgba(255, 255, 255, 0.82);
border-color: rgba(59, 130, 246, 0.16);
box-shadow: none;
}
.shell.openclaw-theme.sidebar-collapsed .conversation-sidebar-layout { .shell.openclaw-theme.sidebar-collapsed .conversation-sidebar-layout {
grid-template-rows: auto 1fr; grid-template-rows: auto 1fr;
gap: 14px; justify-items: center;
gap: 12px;
padding: 16px 10px; padding: 16px 10px;
} }
.shell.openclaw-theme.sidebar-collapsed .sidebar-brand { .shell.openclaw-theme.sidebar-collapsed .sidebar-brand {
grid-template-columns: 44px; width: 44px;
justify-items: center;
justify-content: center; justify-content: center;
gap: 0;
min-height: 44px;
} }
.shell.openclaw-theme.sidebar-collapsed .sidebar-header-content,
.shell.openclaw-theme.sidebar-collapsed .sidebar-brand-logo, .shell.openclaw-theme.sidebar-collapsed .sidebar-brand-logo,
.shell.openclaw-theme.sidebar-collapsed .sidebar-brand-name,
.shell.openclaw-theme.sidebar-collapsed .nav-item-label,
.shell.openclaw-theme.sidebar-collapsed .conversation-sidebar-action, .shell.openclaw-theme.sidebar-collapsed .conversation-sidebar-action,
.shell.openclaw-theme.sidebar-collapsed .sidebar-bottom { .shell.openclaw-theme.sidebar-collapsed .sidebar-bottom {
display: none; display: none;
} }
.shell.openclaw-theme.sidebar-collapsed .sidebar-collapse-button { .shell.openclaw-theme.sidebar-collapsed .sidebar-collapse-button {
width: 44px; width: 38px;
height: 44px; height: 38px;
border-radius: 10px; min-width: 38px;
} }
.shell.openclaw-theme.sidebar-collapsed .sidebar-top { .shell.openclaw-theme.sidebar-collapsed .sidebar-top {
min-height: 0; width: 44px;
align-content: start; align-content: start;
gap: 12px; justify-items: center;
} }
.shell.openclaw-theme.sidebar-collapsed .nav-list { .shell.openclaw-theme.sidebar-collapsed .nav-list {
width: 44px;
justify-items: center; justify-items: center;
align-content: start;
gap: 8px;
} }
.shell.openclaw-theme.sidebar-collapsed .nav-item { .shell.openclaw-theme.sidebar-collapsed .nav-item {
width: 44px; width: 44px;
min-width: 44px;
height: 44px;
min-height: 44px; min-height: 44px;
justify-content: center; justify-content: center;
gap: 0;
padding: 0; padding: 0;
} }
.shell.openclaw-theme.sidebar-collapsed .nav-item.active { .shell.openclaw-theme.sidebar-collapsed .nav-item.active {
box-shadow: box-shadow: 0 8px 18px rgba(37, 99, 235, 0.12);
inset 0 -3px 0 var(--revamp-blue),
var(--revamp-shadow-soft);
} }
.shell.openclaw-theme.sidebar-collapsed .nav-item-icon { .shell.openclaw-theme.sidebar-collapsed .nav-item-label {
display: inline-grid; display: none;
place-items: center;
}
.shell.openclaw-theme.sidebar-collapsed .nav-item-icon svg {
width: 20px;
height: 20px;
}
.shell.openclaw-theme .sidebar-experts-entry,
.shell.openclaw-theme .sidebar-session-section,
.shell.openclaw-theme .expert-category-item,
.shell.openclaw-theme .sidebar-session-card {
border-color: rgba(59, 130, 246, 0.22);
background: rgba(255, 255, 255, 0.78);
box-shadow: none;
}
.shell.openclaw-theme .sidebar-experts-entry {
min-height: 0;
display: grid;
grid-template-rows: auto minmax(0, 1fr);
gap: 8px;
overflow: hidden;
padding: 12px;
border-radius: 16px;
background: rgba(255, 255, 255, 0.82);
border-color: rgba(59, 130, 246, 0.32);
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.78);
}
.shell.openclaw-theme .sidebar-session-section {
min-height: 0;
display: grid;
grid-template-rows: auto minmax(0, 1fr);
overflow: hidden;
padding: 12px;
border-radius: 16px;
background: rgba(255, 255, 255, 0.82);
border-color: rgba(59, 130, 246, 0.32);
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.78);
} }
.shell.openclaw-theme .sidebar-digital-workers-title { .shell.openclaw-theme .sidebar-digital-workers-title {
min-height: 28px; min-height: 28px;
align-items: center; align-items: center;
padding-bottom: 8px; padding-bottom: 8px;
border-bottom: 1px solid rgba(59, 130, 246, 0.16); border-bottom: 1px solid rgba(59, 130, 246, 0.08);
letter-spacing: 0; letter-spacing: 0;
} }
...@@ -371,7 +432,7 @@ ...@@ -371,7 +432,7 @@
min-height: 28px; min-height: 28px;
align-items: center; align-items: center;
padding-bottom: 8px; padding-bottom: 8px;
border-bottom: 1px solid rgba(59, 130, 246, 0.16); border-bottom: 1px solid rgba(59, 130, 246, 0.08);
} }
.shell.openclaw-theme .sidebar-section-label { .shell.openclaw-theme .sidebar-section-label {
...@@ -400,8 +461,6 @@ ...@@ -400,8 +461,6 @@
min-height: 0; min-height: 0;
overflow-y: auto; overflow-y: auto;
overflow-x: hidden; overflow-x: hidden;
scrollbar-width: thin;
scrollbar-color: rgba(94, 164, 216, 0.44) transparent;
} }
.shell.openclaw-theme .sidebar-expert-scroll { .shell.openclaw-theme .sidebar-expert-scroll {
...@@ -410,40 +469,17 @@ ...@@ -410,40 +469,17 @@
.shell.openclaw-theme .sidebar-session-list { .shell.openclaw-theme .sidebar-session-list {
display: grid; display: grid;
gap: 8px; gap: 10px;
align-content: start; align-content: start;
padding-right: 2px; padding-right: 2px;
} }
.shell.openclaw-theme .sidebar-expert-scroll::-webkit-scrollbar,
.shell.openclaw-theme .sidebar-session-list::-webkit-scrollbar {
width: 4px;
}
.shell.openclaw-theme .sidebar-expert-scroll::-webkit-scrollbar-track,
.shell.openclaw-theme .sidebar-session-list::-webkit-scrollbar-track {
background: transparent;
}
.shell.openclaw-theme .sidebar-expert-scroll::-webkit-scrollbar-thumb,
.shell.openclaw-theme .sidebar-session-list::-webkit-scrollbar-thumb {
border-radius: 999px;
background: rgba(94, 164, 216, 0.44);
}
.shell.openclaw-theme .sidebar-expert-scroll:hover::-webkit-scrollbar-thumb,
.shell.openclaw-theme .sidebar-expert-scroll:focus-within::-webkit-scrollbar-thumb,
.shell.openclaw-theme .sidebar-session-list:hover::-webkit-scrollbar-thumb,
.shell.openclaw-theme .sidebar-session-list:focus-within::-webkit-scrollbar-thumb {
background: rgba(37, 99, 235, 0.42);
}
.shell.openclaw-theme .expert-category-item { .shell.openclaw-theme .expert-category-item {
position: relative; position: relative;
overflow: hidden; overflow: hidden;
border-radius: 14px; border-radius: 10px;
border-color: rgba(59, 130, 246, 0.2); border-color: transparent;
background: #ffffff; background: transparent;
box-shadow: none; box-shadow: none;
transition: border-color 180ms ease, box-shadow 180ms ease, background 180ms ease, color 180ms ease; transition: border-color 180ms ease, box-shadow 180ms ease, background 180ms ease, color 180ms ease;
} }
...@@ -459,8 +495,8 @@ ...@@ -459,8 +495,8 @@
} }
.shell.openclaw-theme .expert-category-item:hover { .shell.openclaw-theme .expert-category-item:hover {
border-color: rgba(37, 99, 235, 0.44); border-color: rgba(37, 99, 235, 0.2);
box-shadow: var(--revamp-shadow-soft); box-shadow: none;
} }
.shell.openclaw-theme .expert-category-item:hover::before { .shell.openclaw-theme .expert-category-item:hover::before {
...@@ -470,7 +506,7 @@ ...@@ -470,7 +506,7 @@
.shell.openclaw-theme .expert-category-item.expanded { .shell.openclaw-theme .expert-category-item.expanded {
border-color: rgba(37, 99, 235, 0.52); border-color: rgba(37, 99, 235, 0.52);
background: #f8fbff; background: #f8fbff;
box-shadow: var(--revamp-shadow-soft); box-shadow: 0 8px 16px rgba(37, 99, 235, 0.08);
} }
.shell.openclaw-theme .expert-category-item.expanded::before { .shell.openclaw-theme .expert-category-item.expanded::before {
...@@ -497,7 +533,7 @@ ...@@ -497,7 +533,7 @@
height: 24px; height: 24px;
border-radius: 8px; border-radius: 8px;
background: rgba(37, 99, 235, 0.1); background: rgba(37, 99, 235, 0.1);
box-shadow: inset 0 0 0 1px rgba(37, 99, 235, 0.14); box-shadow: none;
} }
.shell.openclaw-theme .expert-category-name { .shell.openclaw-theme .expert-category-name {
...@@ -523,23 +559,23 @@ ...@@ -523,23 +559,23 @@
} }
.shell.openclaw-theme .expert-category-experts { .shell.openclaw-theme .expert-category-experts {
gap: 5px; gap: 8px;
padding: 7px 7px 8px 10px; padding: 7px 7px 8px 10px;
} }
.shell.openclaw-theme .expert-category-expert-item { .shell.openclaw-theme .expert-category-expert-item {
min-height: 34px; min-height: 34px;
padding: 6px 9px 6px 12px; padding: 6px 9px 6px 12px;
border-radius: 12px; border-radius: 8px;
border-color: rgba(59, 130, 246, 0.14); border-color: transparent;
background: rgba(255, 255, 255, 0.74); background: transparent;
color: #31516f; color: #31516f;
transition: background 160ms ease, border-color 160ms ease, color 160ms ease, box-shadow 160ms ease; transition: background 160ms ease, border-color 160ms ease, color 160ms ease, box-shadow 160ms ease;
} }
.shell.openclaw-theme .expert-category-expert-item:hover { .shell.openclaw-theme .expert-category-expert-item:hover {
background: rgba(255, 255, 255, 0.9); background: rgba(255, 255, 255, 0.7);
border-color: rgba(37, 99, 235, 0.34); border-color: transparent;
color: #104eae; color: #104eae;
box-shadow: none; box-shadow: none;
} }
...@@ -550,15 +586,27 @@ ...@@ -550,15 +586,27 @@
border-color: var(--revamp-blue-border); border-color: var(--revamp-blue-border);
color: var(--revamp-blue-strong); color: var(--revamp-blue-strong);
box-shadow: box-shadow:
inset 4px 0 0 rgba(37, 99, 235, 0.86), inset 3px 0 0 rgba(37, 99, 235, 0.86),
var(--revamp-shadow-soft); 0 4px 12px rgba(37, 99, 235, 0.06);
} }
.shell.openclaw-theme .expert-category-expert-item.active { .shell.openclaw-theme .expert-category-expert-item.active {
background: #ffffff; background: linear-gradient(135deg, rgba(255, 255, 255, 0.96), rgba(224, 237, 255, 0.92));
box-shadow: box-shadow:
inset 3px 0 0 var(--revamp-cyber-blue), inset 2px 0 0 var(--revamp-cyber-blue),
var(--revamp-shadow-soft); 0 2px 8px rgba(37, 99, 235, 0.06);
}
.shell.openclaw-theme .expert-category-expert-icon {
color: currentColor;
}
.shell.openclaw-theme .expert-category-expert-name {
color: currentColor;
font-size: 13px;
font-weight: 600;
line-height: 1.45;
letter-spacing: 0;
} }
.shell.openclaw-theme .sidebar-session-card { .shell.openclaw-theme .sidebar-session-card {
...@@ -566,29 +614,29 @@ ...@@ -566,29 +614,29 @@
min-height: 50px; min-height: 50px;
padding: 3px; padding: 3px;
border-radius: 14px; border-radius: 14px;
border-color: rgba(59, 130, 246, 0.2); border-color: transparent;
background: #ffffff; background: transparent;
box-shadow: none; box-shadow: none;
} }
.shell.openclaw-theme .sidebar-session-card:hover { .shell.openclaw-theme .sidebar-session-card:hover {
border-color: rgba(37, 99, 235, 0.42); border-color: rgba(37, 99, 235, 0.2);
background: #f8fbff; background: rgba(255, 255, 255, 0.7);
box-shadow: var(--revamp-shadow-soft); box-shadow: none;
} }
.shell.openclaw-theme .sidebar-session-card.active { .shell.openclaw-theme .sidebar-session-card.active {
border-color: rgba(37, 99, 235, 0.58); border-color: rgba(37, 99, 235, 0.42);
background: #ffffff; background: #ffffff;
box-shadow: box-shadow:
inset 4px 0 0 var(--revamp-blue), inset 3px 0 0 var(--revamp-blue),
var(--revamp-shadow-soft); 0 4px 12px rgba(37, 99, 235, 0.06);
} }
.shell.openclaw-theme .sidebar-session-main { .shell.openclaw-theme .sidebar-session-main {
min-height: 42px; min-height: 42px;
padding: 0 8px 0 14px; padding: 0 8px 0 14px;
border-radius: 12px; border-radius: 8px;
} }
.shell.openclaw-theme .sidebar-session-main strong { .shell.openclaw-theme .sidebar-session-main strong {
...@@ -603,19 +651,16 @@ ...@@ -603,19 +651,16 @@
color: #64748b; color: #64748b;
opacity: 0.72; opacity: 0.72;
background: #f1f5f9; background: #f1f5f9;
box-shadow: inset 0 0 0 1px rgba(148, 163, 184, 0.26); box-shadow: none;
}
.shell.openclaw-theme .expert-category-expert-icon {
color: currentColor;
} }
.shell.openclaw-theme .expert-category-expert-name { .shell.openclaw-theme .sidebar-session-close svg {
color: currentColor; width: 12px;
font-size: 13px; height: 12px;
font-weight: 600; display: block;
line-height: 1.45; stroke: currentColor;
letter-spacing: 0; stroke-width: 1.8;
stroke-linecap: round;
} }
@keyframes expertPanelReveal { @keyframes expertPanelReveal {
......
...@@ -30,24 +30,23 @@ test("app shell wires a collapsible sidebar state into the sidebar components", ...@@ -30,24 +30,23 @@ test("app shell wires a collapsible sidebar state into the sidebar components",
}) })
test("sidebar nav omits plugins and keeps the new conversation action visible when expanded", () => { test("sidebar nav omits plugins and keeps the new conversation action visible when expanded", () => {
const navItems = appSidebarSource.match(/\{\s*id:\s*"chat"[\s\S]*?\]\.map/)?.[0] ?? "" const navItems = appSidebarSource.match(/\{\s*id:\s*"chat"[\s\S]*?\]\.(filter[\s\S]*?\)\.)?map/)?.[0] ?? ""
assert.match(navItems, /id:\s*"chat"/) assert.match(navItems, /id:\s*"chat"/)
assert.match(navItems, /id:\s*"tasks"/) assert.match(navItems, /id:\s*"tasks"/)
assert.match(navItems, /id:\s*"knowledge"/) assert.match(navItems, /id:\s*"knowledge"/)
assert.match(navItems, /id:\s*"settings"/) assert.match(navItems, /id:\s*"settings"/)
assert.doesNotMatch(navItems, /id:\s*"plugins"/) assert.doesNotMatch(navItems, /id:\s*"plugins"/)
assert.match(appSidebarSource, /<div className="conversation-sidebar-action">\s*\{!showBindEntry \? sidebarNewSessionAction : null\}\s*<\/div>/) assert.match(appSidebarSource, /\{!showBindEntry \? <div className="conversation-sidebar-action">\{sidebarNewSessionAction\}<\/div> : null\}/)
}) })
test("sidebar renders the brand row and hides non-icon content in collapsed mode", () => { test("sidebar renders the brand row and hides non-icon content in collapsed mode", () => {
assert.match(sidebarSource, /brandIcon/) assert.match(sidebarSource, /brandIcon/)
assert.match(sidebarSource, /sidebar-brand/) assert.match(sidebarSource, /sidebar-brand/)
assert.match(sidebarSource, /千匠问天/)
assert.match(sidebarSource, /sidebar-collapse-button/) assert.match(sidebarSource, /sidebar-collapse-button/)
assert.match(themeStylesSource, /\.shell\.openclaw-theme\.sidebar-collapsed\s*\{[\s\S]*?grid-template-columns:\s*64px minmax\(0, 1fr\);/m) assert.match(shellStylesSource, /\.shell\.sidebar-collapsed\s*\{[\s\S]*?grid-template-columns:\s*64px minmax\(0, 1fr\);/m)
assert.match(themeStylesSource, /\.shell\.openclaw-theme\.sidebar-collapsed \.sidebar-brand-logo,\s*\n\.shell\.openclaw-theme\.sidebar-collapsed \.sidebar-brand-name/) assert.match(themeStylesSource, /\.shell\.openclaw-theme\.sidebar-collapsed \.sidebar-header-content,\s*\n\.shell\.openclaw-theme\.sidebar-collapsed \.sidebar-brand-logo/)
assert.match(themeStylesSource, /\.shell\.openclaw-theme\.sidebar-collapsed \.sidebar-brand\s*\{[\s\S]*?grid-template-columns:\s*44px;/m) assert.match(themeStylesSource, /\.shell\.openclaw-theme\.sidebar-collapsed \.sidebar-brand\s*\{[\s\S]*?width:\s*44px;[\s\S]*?justify-content:\s*center;/m)
assert.match(themeStylesSource, /\.shell\.openclaw-theme\.sidebar-collapsed \.sidebar-collapse-button\s*\{[\s\S]*?width:\s*44px;[\s\S]*?height:\s*44px;/m) assert.match(themeStylesSource, /\.shell\.openclaw-theme\.sidebar-collapsed \.sidebar-collapse-button\s*\{[\s\S]*?width:\s*38px;[\s\S]*?height:\s*38px;/m)
assert.match(themeStylesSource, /\.shell\.openclaw-theme\.sidebar-collapsed \.sidebar-top\s*\{[\s\S]*?align-content:\s*start;/m) assert.match(themeStylesSource, /\.shell\.openclaw-theme\.sidebar-collapsed \.sidebar-top\s*\{[\s\S]*?align-content:\s*start;/m)
assert.match(themeStylesSource, /\.shell\.openclaw-theme\.sidebar-collapsed \.nav-item-label/) assert.match(themeStylesSource, /\.shell\.openclaw-theme\.sidebar-collapsed \.nav-item-label/)
assert.match(themeStylesSource, /\.shell\.openclaw-theme\.sidebar-collapsed \.sidebar-bottom/) assert.match(themeStylesSource, /\.shell\.openclaw-theme\.sidebar-collapsed \.sidebar-bottom/)
...@@ -61,8 +60,8 @@ test("sidebar keeps brand, navigation, and lower content in fixed grid rows", () ...@@ -61,8 +60,8 @@ test("sidebar keeps brand, navigation, and lower content in fixed grid rows", ()
}) })
test("new conversation action matches the sidebar navigation control size", () => { test("new conversation action matches the sidebar navigation control size", () => {
assert.match(cssBlock(themeStylesSource, ".shell.openclaw-theme .nav-item"), /min-height:\s*42px;[\s\S]*?border-radius:\s*10px;/) assert.match(cssBlock(themeStylesSource, ".shell.openclaw-theme .nav-item"), /min-height:\s*42px;[\s\S]*?border-radius:\s*14px;/)
assert.match(cssBlock(themeStylesSource, ".shell.openclaw-theme .sidebar-new-session.conversation-new-session"), /width:\s*100%;[\s\S]*?min-height:\s*42px;[\s\S]*?justify-content:\s*center;[\s\S]*?border-radius:\s*10px;/) assert.match(cssBlock(themeStylesSource, ".shell.openclaw-theme .sidebar-new-session.conversation-new-session"), /width:\s*100%;[\s\S]*?min-height:\s*44px;[\s\S]*?justify-content:\s*center;[\s\S]*?border-radius:\s*14px;/)
}) })
test("knowledge navigation uses a book icon instead of the old document icon", () => { test("knowledge navigation uses a book icon instead of the old document icon", () => {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment