feat(console): 侧栏项目按优先级显示三类计数徽标
左侧项目状态新增数字提示,按优先级展示: 1. 需人工处理数(紫,attention) 2. agent 运行数(青,executing) 3. 被阻塞数(琥珀,blocked) 仅非零显示,固定「人工 > 运行 > 阻塞」排列;折叠态仍单状态点, 任一计数 > 0 时放大。后端 projectSummary 已就绪,仅透出 blocked 并渲染。i18n 五语言补齐 projAgentsTip / projBlockedTip。 tsk_0VF1URfdoe3d Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -48,6 +48,17 @@ function projStateMeta(state, t) {
|
||||
})[state] || { color: 'var(--muted)', pulse: false, label: t.projIdle };
|
||||
}
|
||||
|
||||
// 项目计数徽标:按优先级展示「需人工 / 运行中 / 被阻塞」,仅非零显示
|
||||
function CountBadge({ value, color, title }) {
|
||||
return (
|
||||
<span title={title} style={{
|
||||
fontFamily: 'var(--mono)', fontSize: 9.5, fontWeight: 700, lineHeight: '15px',
|
||||
minWidth: 15, height: 15, textAlign: 'center', padding: '0 4px',
|
||||
color: 'var(--bg-deep)', background: color, borderRadius: 8,
|
||||
}}>{value}</span>
|
||||
);
|
||||
}
|
||||
|
||||
function ProjStatusDot({ meta, size = 7 }) {
|
||||
return (
|
||||
<span style={{
|
||||
@@ -509,7 +520,10 @@ function Sidebar({ projects, currentId, onSelect, onNewProject, collapsed, onTog
|
||||
const active = p.id === currentId;
|
||||
const meta = projStateMeta(p.state, t);
|
||||
return (
|
||||
<li key={p.id} onClick={() => onSelect(p.id)} title={p.name + ' · ' + meta.label + (p.pending ? ' · ' + t.projPendingTip.replace('{n}', p.pending) : '')}
|
||||
<li key={p.id} onClick={() => onSelect(p.id)} title={p.name + ' · ' + meta.label
|
||||
+ (p.pending ? ' · ' + t.projPendingTip.replace('{n}', p.pending) : '')
|
||||
+ (p.agents ? ' · ' + t.projAgentsTip.replace('{n}', p.agents) : '')
|
||||
+ (p.blocked ? ' · ' + t.projBlockedTip.replace('{n}', p.blocked) : '')}
|
||||
draggable={!collapsed}
|
||||
onDragStart={(e) => { setDragId(p.id); e.dataTransfer.effectAllowed = 'move'; }}
|
||||
onDragOver={(e) => { if (dragId) { e.preventDefault(); if (p.id !== dragId && overId !== p.id) setOverId(p.id); } }}
|
||||
@@ -536,20 +550,16 @@ function Sidebar({ projects, currentId, onSelect, onNewProject, collapsed, onTog
|
||||
<span style={{ fontWeight: 600, fontSize: 13, color: active ? 'var(--green)' : 'var(--ink)' }}>{p.name}</span>
|
||||
<span style={{ fontSize: 10.5, color: 'var(--faint)', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>{p.path}</span>
|
||||
</span>
|
||||
<span style={{ flex: 'none', display: 'flex', alignItems: 'center', gap: 6 }}>
|
||||
{p.pending > 0 ? (
|
||||
<span title={t.projPendingTip.replace('{n}', p.pending)} style={{
|
||||
fontFamily: 'var(--mono)', fontSize: 9.5, fontWeight: 700, lineHeight: '15px',
|
||||
minWidth: 15, height: 15, textAlign: 'center', padding: '0 4px',
|
||||
color: 'var(--bg-deep)', background: 'var(--violet)', borderRadius: 8,
|
||||
}}>{p.pending}</span>
|
||||
) : null}
|
||||
<span style={{ flex: 'none', display: 'flex', alignItems: 'center', gap: 5 }}>
|
||||
{p.pending > 0 ? <CountBadge value={p.pending} color="var(--violet)" title={t.projPendingTip.replace('{n}', p.pending)} /> : null}
|
||||
{p.agents > 0 ? <CountBadge value={p.agents} color="var(--cyan)" title={t.projAgentsTip.replace('{n}', p.agents)} /> : null}
|
||||
{p.blocked > 0 ? <CountBadge value={p.blocked} color="var(--amber)" title={t.projBlockedTip.replace('{n}', p.blocked)} /> : null}
|
||||
<ProjStatusDot meta={meta} />
|
||||
</span>
|
||||
</React.Fragment>
|
||||
) : (
|
||||
<span style={{ position: 'absolute', top: 7, right: 9, display: 'flex', alignItems: 'center' }}>
|
||||
<ProjStatusDot meta={meta} size={p.pending > 0 ? 8 : 6} />
|
||||
<ProjStatusDot meta={meta} size={(p.pending || p.agents || p.blocked) > 0 ? 8 : 6} />
|
||||
</span>
|
||||
)}
|
||||
</li>
|
||||
|
||||
Reference in New Issue
Block a user