Prevent memory activity from clashing with desktop themes

Memory tool activity inherited the global warm brand treatment, which made the new desktop memory surface feel detached from white and dark themes. The component now uses memory-specific tokens with a quieter teal family, and the sidebar settings dock is made opaque so scrolling sessions cannot show through it.

Constraint: Desktop memory activity must remain readable across light, white, and dark themes without reusing the classic warm brand surface
Rejected: Keep using color-mix with --color-brand | preserves the original warm tint mismatch in white theme
Confidence: high
Scope-risk: narrow
Directive: Keep memory activity colors on memory-specific tokens instead of generic brand tokens
Tested: cd desktop && bun run test src/components/chat/MessageList.test.tsx --run
Tested: cd desktop && bun run test src/components/layout/Sidebar.test.tsx --run
Tested: cd desktop && bun run lint
Tested: cd desktop && bun run build
Tested: Browser smoke screenshots for light, white, and dark memory activity previews
This commit is contained in:
程序员阿江(Relakkes) 2026-05-14 23:13:02 +08:00
parent e070c4a0d0
commit 667b86303f
5 changed files with 42 additions and 5 deletions

View File

@ -264,6 +264,9 @@ describe('MessageList nested tool calls', () => {
expect(screen.getByText('Saved 1 memory item(s)')).toBeTruthy()
expect(screen.getByText('preferences.md')).toBeTruthy()
expect(screen.getByText('Tool details')).toBeTruthy()
const memoryCardClassName = screen.getByTestId('memory-tool-activity-card').className
expect(memoryCardClassName).toContain('border-[var(--color-memory-border)]')
expect(memoryCardClassName).toContain('bg-[var(--color-memory-surface)]')
})
it('promotes memory file reads into collapsible memory references', () => {

View File

@ -195,7 +195,10 @@ function MemoryToolActivityGroup({
return (
<div className="mb-2">
<div className="overflow-hidden rounded-lg border border-[color-mix(in_srgb,var(--color-brand)_22%,var(--color-border))] bg-[color-mix(in_srgb,var(--color-brand)_6%,var(--color-surface-container-lowest))]">
<div
data-testid="memory-tool-activity-card"
className="overflow-hidden rounded-lg border border-[var(--color-memory-border)] bg-[var(--color-memory-surface)]"
>
<button
type="button"
onClick={() => setExpanded((value) => !value)}
@ -206,12 +209,12 @@ function MemoryToolActivityGroup({
) : (
<ChevronRight size={15} className="shrink-0 text-[var(--color-text-tertiary)]" aria-hidden="true" />
)}
<BookMarked size={15} className="shrink-0 text-[var(--color-brand)]" aria-hidden="true" />
<BookMarked size={15} className="shrink-0 text-[var(--color-memory-accent)]" aria-hidden="true" />
<span className="min-w-0 flex-1 truncate text-[13px] font-medium text-[var(--color-text-primary)]">
{t(titleKey, { count: activity.files.length })}
</span>
{isStreaming ? (
<span className="h-1.5 w-1.5 rounded-full bg-[var(--color-brand)] animate-pulse-dot" />
<span className="h-1.5 w-1.5 rounded-full bg-[var(--color-memory-accent)] animate-pulse-dot" />
) : null}
</button>
@ -226,7 +229,7 @@ function MemoryToolActivityGroup({
onClick={() => openMemorySettings(file.path)}
className="group flex w-full items-start gap-2 rounded-md px-2 py-1.5 text-left transition-colors hover:bg-[var(--color-surface-hover)] focus:outline-none focus-visible:shadow-[var(--shadow-focus-ring)]"
>
<span className="mt-0.5 flex h-5 w-5 shrink-0 items-center justify-center rounded-sm border border-[var(--color-border)] bg-[var(--color-surface)] text-[var(--color-text-tertiary)] group-hover:text-[var(--color-brand)]">
<span className="mt-0.5 flex h-5 w-5 shrink-0 items-center justify-center rounded-sm border border-[var(--color-memory-border)] bg-[var(--color-memory-icon-bg)] text-[var(--color-text-tertiary)] group-hover:text-[var(--color-memory-accent)]">
<Settings size={12} aria-hidden="true" />
</span>
<span className="min-w-0 flex-1">

View File

@ -348,6 +348,13 @@ describe('Sidebar', () => {
expect(screen.getByTestId('sidebar-session-list-section')).toHaveClass('flex', 'flex-1', 'min-h-0', 'flex-col')
})
it('keeps the settings dock opaque above the scrolling session list', () => {
render(<Sidebar />)
expect(screen.getByTestId('sidebar-settings-dock')).toHaveClass('sidebar-settings-dock')
expect(screen.getByTestId('sidebar-settings-dock')).toHaveClass('absolute', 'bottom-0')
})
it('keeps mobile navigation focused on chat sessions', async () => {
const onRequestClose = vi.fn()
createSession.mockResolvedValue('session-mobile-new')

View File

@ -593,7 +593,10 @@ export function Sidebar({ isMobile = false, onRequestClose }: SidebarProps) {
)}
{!isMobile && (
<div className={`absolute bottom-0 left-0 right-0 border-t border-[var(--color-border)] p-3 ${expanded ? '' : 'flex justify-center'}`}>
<div
data-testid="sidebar-settings-dock"
className={`sidebar-settings-dock absolute bottom-0 left-0 right-0 border-t border-[var(--color-border)] p-3 ${expanded ? '' : 'flex justify-center'}`}
>
<NavItem
active={activeTabId === SETTINGS_TAB_ID}
collapsed={!expanded}

View File

@ -177,6 +177,10 @@
--color-surface-sidebar: var(--color-surface-container-low);
--color-surface-hover: var(--color-surface-container-high);
--color-surface-selected: var(--color-surface-container);
--color-memory-accent: #3B7068;
--color-memory-surface: color-mix(in srgb, var(--color-memory-accent) 7%, var(--color-surface-container-lowest));
--color-memory-border: color-mix(in srgb, var(--color-memory-accent) 26%, var(--color-border));
--color-memory-icon-bg: color-mix(in srgb, var(--color-memory-accent) 10%, var(--color-surface-container-lowest));
--color-model-option-selected-bg: var(--color-primary-fixed);
--color-model-option-selected-border: rgba(143, 72, 47, 0.2);
--color-activity-heat-0: var(--color-surface-container);
@ -374,6 +378,10 @@
--color-surface-sidebar: #F1F4F7;
--color-surface-hover: #F2F5F8;
--color-surface-selected: #E9EEF3;
--color-memory-accent: #0F766E;
--color-memory-surface: color-mix(in srgb, var(--color-memory-accent) 5%, #FFFFFF);
--color-memory-border: color-mix(in srgb, var(--color-memory-accent) 24%, var(--color-border));
--color-memory-icon-bg: color-mix(in srgb, var(--color-memory-accent) 8%, #FFFFFF);
--color-model-option-selected-bg: #FFF0EA;
--color-model-option-selected-border: rgba(143, 72, 47, 0.2);
--color-activity-heat-0: #EEF2F6;
@ -548,6 +556,10 @@
--color-surface-sidebar: #171615;
--color-surface-hover: var(--color-surface-container-highest);
--color-surface-selected: var(--color-surface-container);
--color-memory-accent: #7DD3C7;
--color-memory-surface: color-mix(in srgb, var(--color-memory-accent) 9%, var(--color-surface-container-lowest));
--color-memory-border: color-mix(in srgb, var(--color-memory-accent) 26%, var(--color-border));
--color-memory-icon-bg: color-mix(in srgb, var(--color-memory-accent) 12%, var(--color-surface-container-lowest));
--color-model-option-selected-bg: rgba(255, 181, 159, 0.13);
--color-model-option-selected-border: rgba(255, 181, 159, 0.34);
--color-activity-heat-0: #242322;
@ -967,10 +979,19 @@ button, input, textarea, select, a, [role="button"] {
::-webkit-scrollbar-thumb:hover { opacity: 0.6; }
.sidebar-scroll-area {
padding-bottom: 72px;
scrollbar-width: thin;
scrollbar-color: var(--color-outline-a72) transparent;
}
.sidebar-settings-dock {
z-index: 30;
background-color: var(--color-surface-sidebar);
background-image: var(--sidebar-panel-bg-image);
background-repeat: no-repeat;
background-position: left bottom;
}
.sidebar-scroll-area::-webkit-scrollbar {
width: 4px;
}