diff --git a/src/renderer/src/components/right-sidebar/AiVaultScanIssueBanners.test.tsx b/src/renderer/src/components/right-sidebar/AiVaultScanIssueBanners.test.tsx new file mode 100644 index 00000000000..df15ba89029 --- /dev/null +++ b/src/renderer/src/components/right-sidebar/AiVaultScanIssueBanners.test.tsx @@ -0,0 +1,42 @@ +// @vitest-environment happy-dom +import { cleanup, render, screen } from '@testing-library/react' +import { afterEach, expect, it } from 'vitest' +import type { AiVaultListResult, AiVaultSession } from '../../../../shared/ai-vault-types' +import { AiVaultScanIssueBanners } from './AiVaultScanIssueBanners' + +afterEach(() => { + cleanup() +}) + +it('keeps many partial-read notices in a bounded scroll area above the session list', () => { + const notices = Array.from({ length: 40 }, (_, index) => ({ + executionHostId: 'ssh:box' as const, + agent: 'codex' as const, + kind: 'notice' as const, + path: `/home/u/.codex/sessions/rollout-${index}.jsonl`, + message: `Skipped 1 oversized transcript record over the 10.0 MiB limit (12.0 MiB at byte ${index}). The rest of the session was read.` + })) + const scanResult: AiVaultListResult = { + // oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: banners only read the session count. + sessions: [{} as AiVaultSession], + issues: [...notices, { agent: 'codex', path: '/broken.jsonl', message: 'Unreadable' }], + scannedAt: '2026-10-15T00:00:00.000Z' + } + + render() + + const region = screen.getByTestId('ai-vault-scan-notices') + expect(region.className).toContain('max-h-24') + expect(region.className).toContain('overflow-y-auto') + expect(region.children).toHaveLength(42) + expect(region.textContent).toContain('1 transcript skipped') +}) + +it('renders no notice area for a clean scan', () => { + render( + + ) + expect(screen.queryByTestId('ai-vault-scan-notices')).toBeNull() +}) diff --git a/src/renderer/src/components/right-sidebar/AiVaultScanIssueBanners.tsx b/src/renderer/src/components/right-sidebar/AiVaultScanIssueBanners.tsx index 46171da9cb8..93c76e0437b 100644 --- a/src/renderer/src/components/right-sidebar/AiVaultScanIssueBanners.tsx +++ b/src/renderer/src/components/right-sidebar/AiVaultScanIssueBanners.tsx @@ -18,6 +18,10 @@ export function AiVaultScanIssueBanners({ const blocking = blockingAiVaultScanIssue(scanResult) const skippedTranscriptCount = skippedAiVaultTranscriptCount(scanResult) + const notices = aiVaultScanNoticeIssues(scanResult) + const reasons = skippedAiVaultTranscriptReasons(scanResult) + const hasNoticeRows = notices.length > 0 || skippedTranscriptCount > 0 || reasons.length > 0 + return ( <> {blocking ? ( @@ -25,35 +29,43 @@ export function AiVaultScanIssueBanners({ {blocking.message} ) : null} - {aiVaultScanNoticeIssues(scanResult).map((issue) => ( + {hasNoticeRows ? ( + // Why bounded: one notice per partly-read session can outgrow the panel and hide the list.
- {issue.message} -
- ))} - {skippedTranscriptCount > 0 ? ( -
- {translate( - 'auto.components.right.sidebar.AiVaultPanel.transcriptsSkipped', - '{{count}} transcript skipped', - { count: skippedTranscriptCount } - )} + {notices.map((issue) => ( +
+ {issue.message} +
+ ))} + {skippedTranscriptCount > 0 ? ( +
+ {translate( + 'auto.components.right.sidebar.AiVaultPanel.transcriptsSkipped', + '{{count}} transcript skipped', + { count: skippedTranscriptCount } + )} +
+ ) : null} + {reasons.map((reason) => ( +
+ {reason} +
+ ))}
) : null} - {skippedAiVaultTranscriptReasons(scanResult).map((reason) => ( -
- {reason} -
- ))} ) }