mirror of
https://github.com/wu736139669/hapi.git
synced 2026-08-05 06:24:37 +00:00
fix(web): drop Idle session-list badge (keep working/pending) (#1366)
* fix(web): drop Idle session-list badge (keep working/pending) Quiet active rows already read as the default via full opacity vs faded archived; labeling Idle was badge inflation. Pin-in-progress now only surfaces working/pending so the section does not advertise lack-of-state. Fixes #1362 Co-authored-by: Cursor <cursoragent@cursor.com> * fix(web): include deliveryMode on abort send-error restore Unblocks web typecheck: RawSendError requires deliveryMode, and the abort-restore path was omitting it (already red on upstream/main CI). Co-authored-by: Cursor <cursoragent@cursor.com> --------- Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -460,6 +460,58 @@ describe('SessionList collapse behavior', () => {
|
||||
expect(getProjectPanel().getAttribute('data-open')).toBeNull()
|
||||
})
|
||||
|
||||
it('does not label quiet active sessions as Idle', () => {
|
||||
const sessions = [
|
||||
makeSession({
|
||||
id: 'session-quiet',
|
||||
active: true,
|
||||
updatedAt: 100,
|
||||
metadata: { path: '/work/hapi', name: 'Quiet task', flavor: 'codex' },
|
||||
}),
|
||||
]
|
||||
render(renderSessionList(sessions, null))
|
||||
|
||||
expect(screen.getByRole('button', { name: /Quiet task/ })).toBeInTheDocument()
|
||||
expect(screen.queryByText('Idle')).toBeNull()
|
||||
expect(screen.queryByTitle('Idle')).toBeNull()
|
||||
})
|
||||
|
||||
it('keeps quiet active sessions in directory groups when pin-in-progress is on', () => {
|
||||
localStorage.setItem('hapi-pin-in-progress-sessions', 'true')
|
||||
const sessions = [
|
||||
makeSession({
|
||||
id: 'session-running',
|
||||
active: true,
|
||||
thinking: true,
|
||||
updatedAt: 100,
|
||||
metadata: { path: '/work/hapi', name: 'Running task', flavor: 'codex' },
|
||||
}),
|
||||
makeSession({
|
||||
id: 'session-quiet',
|
||||
active: true,
|
||||
updatedAt: 90,
|
||||
metadata: { path: '/work/hapi', name: 'Quiet task', flavor: 'codex' },
|
||||
}),
|
||||
makeSession({
|
||||
id: 'session-pending',
|
||||
active: true,
|
||||
pendingRequestsCount: 1,
|
||||
updatedAt: 80,
|
||||
metadata: { path: '/work/other', name: 'Pending task', flavor: 'codex' },
|
||||
}),
|
||||
]
|
||||
render(renderSessionList(sessions, null))
|
||||
|
||||
expect(screen.getByTitle('In progress')).toBeInTheDocument()
|
||||
expect(screen.getByText(/Running \(1\)/)).toBeInTheDocument()
|
||||
expect(screen.getByText(/pending \(1\)/)).toBeInTheDocument()
|
||||
expect(screen.queryByText(/Idle \(/)).toBeNull()
|
||||
// Quiet active stays under its project directory, not an Idle pin bucket.
|
||||
expect(screen.getByTitle('/work/hapi')).toBeInTheDocument()
|
||||
expect(screen.getByRole('button', { name: /Quiet task/ })).toBeInTheDocument()
|
||||
expect(getProjectPanel().getAttribute('data-open')).toBe('true')
|
||||
})
|
||||
|
||||
it('auto-expands the path again when the selected session changes', async () => {
|
||||
const sessions = [
|
||||
makeSession({
|
||||
|
||||
@@ -45,9 +45,18 @@ type SessionGroup = {
|
||||
const RUNNING_BUCKETS = [
|
||||
{ key: 'working', labelKey: 'session.item.running', colorClass: 'text-[var(--app-badge-success-text)]', pulse: true },
|
||||
{ key: 'pending', labelKey: 'session.item.pending', colorClass: 'text-[var(--app-badge-warning-text)]', pulse: true },
|
||||
{ key: 'idle', labelKey: 'session.item.idle', colorClass: 'text-[var(--app-hint)]', pulse: false },
|
||||
] as const
|
||||
|
||||
/** Active sessions that warrant the optional pinned In progress section. Quiet actives stay in directory groups. */
|
||||
function isPinnedInProgressSession(session: SessionSummary): boolean {
|
||||
if (!session.active) {
|
||||
return false
|
||||
}
|
||||
return session.thinking
|
||||
|| (session.backgroundTaskCount ?? 0) > 0
|
||||
|| (session.pendingRequestsCount ?? 0) > 0
|
||||
}
|
||||
|
||||
export type SessionTimeRange = {
|
||||
start: number | null
|
||||
end: number | null
|
||||
@@ -1099,10 +1108,9 @@ export function SessionList(props: {
|
||||
[visibleSessions, activeMachineFilter]
|
||||
)
|
||||
const runningSessions = useMemo(() => {
|
||||
const buckets: Record<'working' | 'pending' | 'idle', SessionSummary[]> = {
|
||||
const buckets: Record<'working' | 'pending', SessionSummary[]> = {
|
||||
working: [],
|
||||
pending: [],
|
||||
idle: []
|
||||
}
|
||||
if (!pinInProgressSessions) {
|
||||
return buckets
|
||||
@@ -1115,9 +1123,8 @@ export function SessionList(props: {
|
||||
buckets.working.push(session)
|
||||
} else if ((session.pendingRequestsCount ?? 0) > 0) {
|
||||
buckets.pending.push(session)
|
||||
} else {
|
||||
buckets.idle.push(session)
|
||||
}
|
||||
// Quiet active sessions stay in directory groups (no Idle pin bucket).
|
||||
}
|
||||
const byRecent = (a: SessionSummary, b: SessionSummary) => b.updatedAt - a.updatedAt
|
||||
for (const key of Object.keys(buckets) as Array<keyof typeof buckets>) {
|
||||
@@ -1127,11 +1134,10 @@ export function SessionList(props: {
|
||||
}, [machineFilteredSessions, pinInProgressSessions])
|
||||
const runningSessionTotal = runningSessions.working.length
|
||||
+ runningSessions.pending.length
|
||||
+ runningSessions.idle.length
|
||||
const groups = useMemo(
|
||||
() => groupSessionsByDirectory(
|
||||
pinInProgressSessions
|
||||
? machineFilteredSessions.filter((session) => !session.active)
|
||||
? machineFilteredSessions.filter((session) => !isPinnedInProgressSession(session))
|
||||
: machineFilteredSessions
|
||||
),
|
||||
[machineFilteredSessions, pinInProgressSessions]
|
||||
|
||||
@@ -205,16 +205,6 @@ export function SessionRowSummary(props: {
|
||||
<span className="text-[11px] font-medium leading-none">{t('session.item.pending')}</span>
|
||||
) : null}
|
||||
</span>
|
||||
) : s.active ? (
|
||||
<span
|
||||
className="inline-flex shrink-0 items-center gap-1 text-[var(--app-hint)]"
|
||||
title={t('session.item.idle')}
|
||||
>
|
||||
<span className="h-1.5 w-1.5 rounded-full bg-current" aria-hidden="true" />
|
||||
{!inRunningSection ? (
|
||||
<span className="text-[11px] font-medium leading-none">{t('session.item.idle')}</span>
|
||||
) : null}
|
||||
</span>
|
||||
) : attention && nestedTooltips && attentionId ? (
|
||||
<SessionAttentionIndicator
|
||||
attention={attention}
|
||||
|
||||
@@ -145,7 +145,6 @@ export default {
|
||||
'session.item.pending': 'pending',
|
||||
'session.item.thinking': 'thinking',
|
||||
'session.item.running': 'Running',
|
||||
'session.item.idle': 'Idle',
|
||||
'session.item.permission': 'Permission required',
|
||||
'session.item.needsInput': 'Needs input',
|
||||
'session.item.background': 'Background tasks running',
|
||||
@@ -735,7 +734,7 @@ export default {
|
||||
'settings.display.activeSessionsOnly': 'Active sessions only',
|
||||
'settings.display.activeSessionsOnly.desc': 'Hide inactive sessions in the sidebar. The session you have open stays visible.',
|
||||
'settings.display.pinInProgressSessions': 'Pin in-progress sessions',
|
||||
'settings.display.pinInProgressSessions.desc': 'Move active sessions into a pinned In progress section at the top of the sidebar. Off keeps them inside their project directories.',
|
||||
'settings.display.pinInProgressSessions.desc': 'Move working and pending sessions into a pinned In progress section at the top of the sidebar. Quiet active sessions stay inside their project directories. Off keeps everything in directory groups.',
|
||||
'settings.display.sessionListStatus': 'Session list status',
|
||||
'settings.display.sessionListStatus.standard': 'Standard',
|
||||
'settings.display.sessionListStatus.detailed': 'Detailed',
|
||||
|
||||
@@ -145,7 +145,6 @@ export default {
|
||||
'session.item.pending': '待处理',
|
||||
'session.item.thinking': '思考中',
|
||||
'session.item.running': '运行中',
|
||||
'session.item.idle': '空闲',
|
||||
'session.item.permission': '需要权限',
|
||||
'session.item.needsInput': '需要输入',
|
||||
'session.item.background': '后台任务运行中',
|
||||
@@ -734,7 +733,7 @@ export default {
|
||||
'settings.display.activeSessionsOnly': '仅显示活跃会话',
|
||||
'settings.display.activeSessionsOnly.desc': '在侧边栏隐藏非活跃会话;当前打开的会话仍会保留显示。',
|
||||
'settings.display.pinInProgressSessions': '置顶进行中会话',
|
||||
'settings.display.pinInProgressSessions.desc': '将活跃会话移到侧边栏顶部的「进行中」分区。关闭后仍保留在各自项目目录分组中。',
|
||||
'settings.display.pinInProgressSessions.desc': '将运行中和待处理会话移到侧边栏顶部的「进行中」分区;安静的活跃会话仍留在各自项目目录分组中。关闭后全部保留在目录分组中。',
|
||||
'settings.display.sessionListStatus': '会话列表状态',
|
||||
'settings.display.sessionListStatus.standard': '标准',
|
||||
'settings.display.sessionListStatus.detailed': '详细',
|
||||
|
||||
@@ -778,6 +778,7 @@ function SessionPage() {
|
||||
message: t('chat.sendError.aborted'),
|
||||
code: 'abort',
|
||||
scheduledAt: null,
|
||||
deliveryMode: 'steer',
|
||||
mutationStarted: true,
|
||||
restoreSuppressed: false
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user