ui: refactor session sidebar styles and widen history drawer
parent
b333600245
commit
1c2c7bd06a
|
|
@ -67,6 +67,19 @@ export default function SessionSidebar({ agentId, activeSessionId, onChange, ref
|
||||||
};
|
};
|
||||||
|
|
||||||
const list = tab === 'active' ? active : archived;
|
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(() => {
|
useEffect(() => {
|
||||||
|
|
@ -165,16 +178,9 @@ export default function SessionSidebar({ agentId, activeSessionId, onChange, ref
|
||||||
<List.Item
|
<List.Item
|
||||||
key={s.id}
|
key={s.id}
|
||||||
onClick={() => (editing ? null : onChange(s.id))}
|
onClick={() => (editing ? null : onChange(s.id))}
|
||||||
style={{
|
className={`session-sidebar-item${isActive ? ' active' : ''}`}
|
||||||
cursor: 'pointer',
|
|
||||||
padding: '8px 10px',
|
|
||||||
background: isActive ? c.bgActive : c.bgTransparent,
|
|
||||||
borderRadius: 6,
|
|
||||||
border: isActive ? `1px solid ${c.borderActive}` : '1px solid transparent',
|
|
||||||
marginBottom: 4
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
<div style={{ flex: 1, minWidth: 0 }}>
|
<div className="session-sidebar-item-main">
|
||||||
{editing ? (
|
{editing ? (
|
||||||
<Input
|
<Input
|
||||||
size="small"
|
size="small"
|
||||||
|
|
@ -186,35 +192,17 @@ export default function SessionSidebar({ agentId, activeSessionId, onChange, ref
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
<div
|
<div className={`session-sidebar-item-title${isActive ? ' active' : ''}`}>
|
||||||
style={{
|
<MessageOutlined /> {s.title}
|
||||||
fontSize: 13,
|
|
||||||
fontWeight: isActive ? 600 : 500,
|
|
||||||
color: isActive ? c.textActive : c.text,
|
|
||||||
overflow: 'hidden',
|
|
||||||
textOverflow: 'ellipsis',
|
|
||||||
whiteSpace: 'nowrap'
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
💬 {s.title}
|
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div className="session-sidebar-item-subtitle">
|
||||||
style={{
|
|
||||||
fontSize: 11,
|
|
||||||
color: c.textDim,
|
|
||||||
overflow: 'hidden',
|
|
||||||
textOverflow: 'ellipsis',
|
|
||||||
whiteSpace: 'nowrap',
|
|
||||||
marginTop: 2
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{s.lastPreview || '无消息'} · {dayjs(s.lastAt || s.updatedAt).format('MM-DD HH:mm')}
|
{s.lastPreview || '无消息'} · {dayjs(s.lastAt || s.updatedAt).format('MM-DD HH:mm')}
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
{!editing && (
|
{!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="重命名">
|
<Tooltip title="重命名">
|
||||||
<Button
|
<Button
|
||||||
type="text"
|
type="text"
|
||||||
|
|
@ -266,10 +254,10 @@ export default function SessionSidebar({ agentId, activeSessionId, onChange, ref
|
||||||
const inSearchMode = searchQ.trim().length > 0;
|
const inSearchMode = searchQ.trim().length > 0;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div style={{ display: 'flex', flexDirection: 'column', height: '100%' }}>
|
<div className="session-sidebar" style={styleVars}>
|
||||||
{showNewButton && (
|
{showNewButton && (
|
||||||
<Button type="primary" onClick={handleNew} style={{ marginBottom: 8, background: c.primaryBtn }} block>
|
<Button type="primary" onClick={handleNew} className="session-sidebar-new" block>
|
||||||
➕ 新对话
|
新对话
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
|
@ -278,14 +266,13 @@ export default function SessionSidebar({ agentId, activeSessionId, onChange, ref
|
||||||
allowClear
|
allowClear
|
||||||
value={searchQ}
|
value={searchQ}
|
||||||
onChange={(e) => setSearchQ(e.target.value)}
|
onChange={(e) => setSearchQ(e.target.value)}
|
||||||
style={{ marginBottom: 8 }}
|
className="session-sidebar-search"
|
||||||
styles={isDark ? { input: { background: c.inputBg, borderColor: c.border, color: c.text } } : undefined}
|
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{inSearchMode ? (
|
{inSearchMode ? (
|
||||||
<div style={{ flex: 1, overflow: 'auto' }}>
|
<div className="session-sidebar-scroll">
|
||||||
{searching ? (
|
{searching ? (
|
||||||
<div style={{ padding: 16, textAlign: 'center' }}>
|
<div className="session-sidebar-loading">
|
||||||
<Spin size="small" />
|
<Spin size="small" />
|
||||||
</div>
|
</div>
|
||||||
) : searchHits.length === 0 && searchSessionTitles.length === 0 ? (
|
) : searchHits.length === 0 && searchSessionTitles.length === 0 ? (
|
||||||
|
|
@ -293,25 +280,17 @@ export default function SessionSidebar({ agentId, activeSessionId, onChange, ref
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
{searchSessionTitles.length > 0 && (
|
{searchSessionTitles.length > 0 && (
|
||||||
<div style={{ marginBottom: 8 }}>
|
<div className="session-sidebar-search-section">
|
||||||
<div style={{ fontSize: 11, color: c.textDim, padding: '4px 8px' }}>
|
<div className="session-sidebar-search-section-title">
|
||||||
📁 会话标题命中 ({searchSessionTitles.length})
|
📁 会话标题命中 ({searchSessionTitles.length})
|
||||||
</div>
|
</div>
|
||||||
{searchSessionTitles.map((s) => (
|
{searchSessionTitles.map((s) => (
|
||||||
<div
|
<div
|
||||||
key={s.id}
|
key={s.id}
|
||||||
onClick={() => onChange(s.id)}
|
onClick={() => onChange(s.id)}
|
||||||
style={{
|
className={`session-sidebar-search-hit${activeSessionId === s.id ? ' active' : ''}`}
|
||||||
cursor: 'pointer',
|
|
||||||
padding: '6px 10px',
|
|
||||||
borderRadius: 6,
|
|
||||||
marginBottom: 2,
|
|
||||||
background: activeSessionId === s.id ? c.bgActive : c.bgSearch,
|
|
||||||
fontSize: 12,
|
|
||||||
color: c.text
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
💬 {s.title}
|
<MessageOutlined /> {s.title}
|
||||||
{s.archived && <Tag style={{ marginLeft: 6 }}>归档</Tag>}
|
{s.archived && <Tag style={{ marginLeft: 6 }}>归档</Tag>}
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
|
|
@ -319,7 +298,7 @@ export default function SessionSidebar({ agentId, activeSessionId, onChange, ref
|
||||||
)}
|
)}
|
||||||
{searchHits.length > 0 && (
|
{searchHits.length > 0 && (
|
||||||
<div>
|
<div>
|
||||||
<div style={{ fontSize: 11, color: c.textDim, padding: '4px 8px' }}>
|
<div className="session-sidebar-search-section-title">
|
||||||
🔎 消息内容命中 ({searchHits.length})
|
🔎 消息内容命中 ({searchHits.length})
|
||||||
</div>
|
</div>
|
||||||
{searchHits.map((h) => (
|
{searchHits.map((h) => (
|
||||||
|
|
@ -328,22 +307,15 @@ export default function SessionSidebar({ agentId, activeSessionId, onChange, ref
|
||||||
onClick={() =>
|
onClick={() =>
|
||||||
onChange(h.sessionId, { highlightMessageId: h.id })
|
onChange(h.sessionId, { highlightMessageId: h.id })
|
||||||
}
|
}
|
||||||
style={{
|
className={`session-sidebar-search-msg-hit ${h.role === 'user' ? 'role-user' : 'role-assistant'}`}
|
||||||
cursor: 'pointer',
|
|
||||||
padding: 8,
|
|
||||||
borderRadius: 6,
|
|
||||||
marginBottom: 4,
|
|
||||||
background: c.bgSearch,
|
|
||||||
borderLeft: `3px solid ${h.role === 'user' ? '#6366f1' : '#10b981'}`
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
<div style={{ fontSize: 11, color: c.textDim, marginBottom: 2 }}>
|
<div className="session-sidebar-search-msg-meta">
|
||||||
{h.role === 'user' ? '我' : 'AI'} · {h.sessionTitle}
|
{h.role === 'user' ? '我' : 'AI'} · {h.sessionTitle}
|
||||||
{h.sessionArchived && <Tag style={{ marginLeft: 4 }}>归档</Tag>}
|
{h.sessionArchived && <Tag style={{ marginLeft: 4 }}>归档</Tag>}
|
||||||
<span style={{ marginLeft: 6 }}>{dayjs(h.createdAt).format('MM-DD HH:mm')}</span>
|
<span style={{ marginLeft: 6 }}>{dayjs(h.createdAt).format('MM-DD HH:mm')}</span>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
style={{ fontSize: 12, color: c.text, lineHeight: 1.4 }}
|
className="session-sidebar-search-msg-snippet"
|
||||||
dangerouslySetInnerHTML={{ __html: h.snippet }}
|
dangerouslySetInnerHTML={{ __html: h.snippet }}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -364,7 +336,7 @@ export default function SessionSidebar({ agentId, activeSessionId, onChange, ref
|
||||||
{
|
{
|
||||||
value: 'active',
|
value: 'active',
|
||||||
label: (
|
label: (
|
||||||
<span style={{ display: 'inline-flex', alignItems: 'center', gap: 6 }}>
|
<span className="session-sidebar-seg-label">
|
||||||
<MessageOutlined /> 活跃 ({active.length})
|
<MessageOutlined /> 活跃 ({active.length})
|
||||||
</span>
|
</span>
|
||||||
)
|
)
|
||||||
|
|
@ -372,13 +344,13 @@ export default function SessionSidebar({ agentId, activeSessionId, onChange, ref
|
||||||
{
|
{
|
||||||
value: 'archived',
|
value: 'archived',
|
||||||
label: (
|
label: (
|
||||||
<span style={{ display: 'inline-flex', alignItems: 'center', gap: 6 }}>
|
<span className="session-sidebar-seg-label">
|
||||||
<InboxOutlined /> 归档 ({archived.length})
|
<InboxOutlined /> 归档 ({archived.length})
|
||||||
</span>
|
</span>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
]}
|
]}
|
||||||
style={{ marginBottom: 8 }}
|
className="session-sidebar-seg"
|
||||||
/>
|
/>
|
||||||
{list.length === 0 ? (
|
{list.length === 0 ? (
|
||||||
<Empty description={tab === 'active' ? '无活跃会话' : '无归档会话'} image={Empty.PRESENTED_IMAGE_SIMPLE} />
|
<Empty description={tab === 'active' ? '无活跃会话' : '无归档会话'} image={Empty.PRESENTED_IMAGE_SIMPLE} />
|
||||||
|
|
@ -386,22 +358,12 @@ export default function SessionSidebar({ agentId, activeSessionId, onChange, ref
|
||||||
<List
|
<List
|
||||||
size="small"
|
size="small"
|
||||||
dataSource={list}
|
dataSource={list}
|
||||||
style={{ flex: 1, overflow: 'auto' }}
|
className="session-sidebar-list"
|
||||||
renderItem={renderSession}
|
renderItem={renderSession}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* 顶部全局样式 mark 标签高亮 */}
|
|
||||||
<style>{`
|
|
||||||
mark {
|
|
||||||
background: #fef3c7;
|
|
||||||
color: #b45309;
|
|
||||||
padding: 0 2px;
|
|
||||||
border-radius: 2px;
|
|
||||||
}
|
|
||||||
`}</style>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -144,7 +144,7 @@ export default function ChatDrawers(props: {
|
||||||
<Drawer
|
<Drawer
|
||||||
title="聊天记录"
|
title="聊天记录"
|
||||||
placement="right"
|
placement="right"
|
||||||
width={320}
|
width={420}
|
||||||
onClose={() => setHistoryDrawerOpen(false)}
|
onClose={() => setHistoryDrawerOpen(false)}
|
||||||
open={historyDrawerOpen}
|
open={historyDrawerOpen}
|
||||||
styles={{ body: { padding: 0 } }}
|
styles={{ body: { padding: 0 } }}
|
||||||
|
|
|
||||||
170
src/styles.css
170
src/styles.css
|
|
@ -870,6 +870,176 @@ body {
|
||||||
border-radius: 2px;
|
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 {
|
.chat-disclaimer {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
font-size: 11px;
|
font-size: 11px;
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue