ui: refactor session sidebar styles and widen history drawer

main
sp mac bookpro 2605 2026-06-06 14:46:07 +08:00
parent b333600245
commit 1c2c7bd06a
3 changed files with 208 additions and 76 deletions

View File

@ -67,6 +67,19 @@ export default function SessionSidebar({ agentId, activeSessionId, onChange, ref
};
const list = tab === 'active' ? active : archived;
const styleVars = {
['--ss-text' as any]: c.text,
['--ss-text-dim' as any]: c.textDim,
['--ss-text-active' as any]: c.textActive,
['--ss-bg-active' as any]: c.bgActive,
['--ss-border-active' as any]: c.borderActive,
['--ss-bg-hover' as any]: c.bgHover,
['--ss-bg-search' as any]: c.bgSearch,
['--ss-input-bg' as any]: c.inputBg,
['--ss-border' as any]: c.border,
['--ss-danger' as any]: c.dangerText,
['--ss-primary' as any]: c.primaryBtn ?? 'var(--color-brand)'
} as any;
// 防抖搜索
useEffect(() => {
@ -165,16 +178,9 @@ export default function SessionSidebar({ agentId, activeSessionId, onChange, ref
<List.Item
key={s.id}
onClick={() => (editing ? null : onChange(s.id))}
style={{
cursor: 'pointer',
padding: '8px 10px',
background: isActive ? c.bgActive : c.bgTransparent,
borderRadius: 6,
border: isActive ? `1px solid ${c.borderActive}` : '1px solid transparent',
marginBottom: 4
}}
className={`session-sidebar-item${isActive ? ' active' : ''}`}
>
<div style={{ flex: 1, minWidth: 0 }}>
<div className="session-sidebar-item-main">
{editing ? (
<Input
size="small"
@ -186,35 +192,17 @@ export default function SessionSidebar({ agentId, activeSessionId, onChange, ref
/>
) : (
<>
<div
style={{
fontSize: 13,
fontWeight: isActive ? 600 : 500,
color: isActive ? c.textActive : c.text,
overflow: 'hidden',
textOverflow: 'ellipsis',
whiteSpace: 'nowrap'
}}
>
💬 {s.title}
<div className={`session-sidebar-item-title${isActive ? ' active' : ''}`}>
<MessageOutlined /> {s.title}
</div>
<div
style={{
fontSize: 11,
color: c.textDim,
overflow: 'hidden',
textOverflow: 'ellipsis',
whiteSpace: 'nowrap',
marginTop: 2
}}
>
<div className="session-sidebar-item-subtitle">
{s.lastPreview || '无消息'} · {dayjs(s.lastAt || s.updatedAt).format('MM-DD HH:mm')}
</div>
</>
)}
</div>
{!editing && (
<div style={{ display: 'flex', gap: 2, marginLeft: 6 }} onClick={(e) => e.stopPropagation()}>
<div className="session-sidebar-item-actions" onClick={(e) => e.stopPropagation()}>
<Tooltip title="重命名">
<Button
type="text"
@ -266,10 +254,10 @@ export default function SessionSidebar({ agentId, activeSessionId, onChange, ref
const inSearchMode = searchQ.trim().length > 0;
return (
<div style={{ display: 'flex', flexDirection: 'column', height: '100%' }}>
<div className="session-sidebar" style={styleVars}>
{showNewButton && (
<Button type="primary" onClick={handleNew} style={{ marginBottom: 8, background: c.primaryBtn }} block>
<Button type="primary" onClick={handleNew} className="session-sidebar-new" block>
</Button>
)}
@ -278,14 +266,13 @@ export default function SessionSidebar({ agentId, activeSessionId, onChange, ref
allowClear
value={searchQ}
onChange={(e) => setSearchQ(e.target.value)}
style={{ marginBottom: 8 }}
styles={isDark ? { input: { background: c.inputBg, borderColor: c.border, color: c.text } } : undefined}
className="session-sidebar-search"
/>
{inSearchMode ? (
<div style={{ flex: 1, overflow: 'auto' }}>
<div className="session-sidebar-scroll">
{searching ? (
<div style={{ padding: 16, textAlign: 'center' }}>
<div className="session-sidebar-loading">
<Spin size="small" />
</div>
) : searchHits.length === 0 && searchSessionTitles.length === 0 ? (
@ -293,25 +280,17 @@ export default function SessionSidebar({ agentId, activeSessionId, onChange, ref
) : (
<>
{searchSessionTitles.length > 0 && (
<div style={{ marginBottom: 8 }}>
<div style={{ fontSize: 11, color: c.textDim, padding: '4px 8px' }}>
<div className="session-sidebar-search-section">
<div className="session-sidebar-search-section-title">
📁 ({searchSessionTitles.length})
</div>
{searchSessionTitles.map((s) => (
<div
key={s.id}
onClick={() => onChange(s.id)}
style={{
cursor: 'pointer',
padding: '6px 10px',
borderRadius: 6,
marginBottom: 2,
background: activeSessionId === s.id ? c.bgActive : c.bgSearch,
fontSize: 12,
color: c.text
}}
className={`session-sidebar-search-hit${activeSessionId === s.id ? ' active' : ''}`}
>
💬 {s.title}
<MessageOutlined /> {s.title}
{s.archived && <Tag style={{ marginLeft: 6 }}></Tag>}
</div>
))}
@ -319,7 +298,7 @@ export default function SessionSidebar({ agentId, activeSessionId, onChange, ref
)}
{searchHits.length > 0 && (
<div>
<div style={{ fontSize: 11, color: c.textDim, padding: '4px 8px' }}>
<div className="session-sidebar-search-section-title">
🔎 ({searchHits.length})
</div>
{searchHits.map((h) => (
@ -328,22 +307,15 @@ export default function SessionSidebar({ agentId, activeSessionId, onChange, ref
onClick={() =>
onChange(h.sessionId, { highlightMessageId: h.id })
}
style={{
cursor: 'pointer',
padding: 8,
borderRadius: 6,
marginBottom: 4,
background: c.bgSearch,
borderLeft: `3px solid ${h.role === 'user' ? '#6366f1' : '#10b981'}`
}}
className={`session-sidebar-search-msg-hit ${h.role === 'user' ? 'role-user' : 'role-assistant'}`}
>
<div style={{ fontSize: 11, color: c.textDim, marginBottom: 2 }}>
<div className="session-sidebar-search-msg-meta">
{h.role === 'user' ? '我' : 'AI'} · {h.sessionTitle}
{h.sessionArchived && <Tag style={{ marginLeft: 4 }}></Tag>}
<span style={{ marginLeft: 6 }}>{dayjs(h.createdAt).format('MM-DD HH:mm')}</span>
</div>
<div
style={{ fontSize: 12, color: c.text, lineHeight: 1.4 }}
className="session-sidebar-search-msg-snippet"
dangerouslySetInnerHTML={{ __html: h.snippet }}
/>
</div>
@ -364,7 +336,7 @@ export default function SessionSidebar({ agentId, activeSessionId, onChange, ref
{
value: 'active',
label: (
<span style={{ display: 'inline-flex', alignItems: 'center', gap: 6 }}>
<span className="session-sidebar-seg-label">
<MessageOutlined /> ({active.length})
</span>
)
@ -372,13 +344,13 @@ export default function SessionSidebar({ agentId, activeSessionId, onChange, ref
{
value: 'archived',
label: (
<span style={{ display: 'inline-flex', alignItems: 'center', gap: 6 }}>
<span className="session-sidebar-seg-label">
<InboxOutlined /> ({archived.length})
</span>
)
}
]}
style={{ marginBottom: 8 }}
className="session-sidebar-seg"
/>
{list.length === 0 ? (
<Empty description={tab === 'active' ? '无活跃会话' : '无归档会话'} image={Empty.PRESENTED_IMAGE_SIMPLE} />
@ -386,22 +358,12 @@ export default function SessionSidebar({ agentId, activeSessionId, onChange, ref
<List
size="small"
dataSource={list}
style={{ flex: 1, overflow: 'auto' }}
className="session-sidebar-list"
renderItem={renderSession}
/>
)}
</>
)}
{/* 顶部全局样式 mark 标签高亮 */}
<style>{`
mark {
background: #fef3c7;
color: #b45309;
padding: 0 2px;
border-radius: 2px;
}
`}</style>
</div>
);
}

View File

@ -144,7 +144,7 @@ export default function ChatDrawers(props: {
<Drawer
title="聊天记录"
placement="right"
width={320}
width={420}
onClose={() => setHistoryDrawerOpen(false)}
open={historyDrawerOpen}
styles={{ body: { padding: 0 } }}

View File

@ -870,6 +870,176 @@ body {
border-radius: 2px;
}
.session-sidebar {
width: 420px;
height: 100%;
display: flex;
flex-direction: column;
padding: 0 4px;
box-sizing: border-box;
}
.session-sidebar-new {
margin-bottom: 8px;
background: var(--ss-primary);
border: none;
}
.session-sidebar-search {
margin-bottom: 8px;
}
.session-sidebar .ant-input-affix-wrapper,
.session-sidebar .ant-input-affix-wrapper:hover,
.session-sidebar .ant-input-affix-wrapper-focused {
background: var(--ss-input-bg);
border-color: var(--ss-border);
}
.session-sidebar .ant-input {
color: var(--ss-text);
}
.session-sidebar-scroll {
flex: 1;
overflow: auto;
}
.session-sidebar-loading {
padding: 16px;
text-align: center;
}
.session-sidebar-search-section {
margin-bottom: 8px;
}
.session-sidebar-search-section-title {
font-size: 11px;
color: var(--ss-text-dim);
padding: 4px 8px;
}
.session-sidebar-search-hit {
cursor: pointer;
padding: 6px 10px;
border-radius: 6px;
margin-bottom: 2px;
background: var(--ss-bg-search);
font-size: 12px;
color: var(--ss-text);
}
.session-sidebar-search-hit.active {
background: var(--ss-bg-active);
}
.session-sidebar-search-msg-hit {
cursor: pointer;
padding: 8px;
border-radius: 6px;
margin-bottom: 4px;
background: var(--ss-bg-search);
border-left: 3px solid transparent;
}
.session-sidebar-search-msg-hit.role-user {
border-left-color: #6366f1;
}
.session-sidebar-search-msg-hit.role-assistant {
border-left-color: #10b981;
}
.session-sidebar-search-msg-meta {
font-size: 11px;
color: var(--ss-text-dim);
margin-bottom: 2px;
}
.session-sidebar-search-msg-snippet {
font-size: 12px;
color: var(--ss-text);
line-height: 1.4;
}
.session-sidebar-seg {
margin-bottom: 8px;
}
.session-sidebar-seg-label {
display: inline-flex;
align-items: center;
gap: 6px;
}
.session-sidebar-list {
flex: 1;
overflow: auto;
}
.session-sidebar-item {
cursor: pointer;
padding: 8px 10px;
background: transparent;
border-radius: 6px;
border: 1px solid transparent;
margin-bottom: 4px;
}
.session-sidebar-item:hover {
background: var(--ss-bg-hover);
}
.session-sidebar-item.active {
background: var(--ss-bg-active);
border-color: var(--ss-border-active);
}
.session-sidebar-item-main {
flex: 1;
min-width: 0;
}
.session-sidebar-item-title {
font-size: 13px;
font-weight: 500;
color: var(--ss-text);
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
display: inline-flex;
align-items: center;
gap: 6px;
}
.session-sidebar-item-title.active {
font-weight: 600;
color: var(--ss-text-active);
}
.session-sidebar-item-subtitle {
font-size: 11px;
color: var(--ss-text-dim);
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
margin-top: 2px;
}
.session-sidebar-item-actions {
display: flex;
gap: 2px;
margin-left: 6px;
}
.session-sidebar mark {
background: #fef3c7;
color: #b45309;
padding: 0 2px;
border-radius: 2px;
}
.chat-disclaimer {
text-align: center;
font-size: 11px;