Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 31 additions & 0 deletions packages/framework-dashboard/components/EventList.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -271,3 +271,34 @@ describe('EventList inline browser rows (#1455 item 6b)', () => {
expect(screen.getByText('browser').className).toContain('text-primary')
})
})

// The background wash (#1508): a tint across the whole line for the rows the eye hunts for —
// the reader's own turns, failures, the clean landing — while the bulk of the log stays plain.
describe('EventList row wash (#1508)', () => {
test("the reader's own turn gets the blue wash", () => {
const { container } = render(
<EventList events={[{ kind: 'driver', event: { type: 'start', prompt: 'add a search box' } }]} stick={false} />,
)
expect(container.querySelector('[class*="bg-info/10"]')).toBeTruthy()
})

test('a failure gets the red wash', () => {
const { container } = render(<EventList events={[{ kind: 'end', ok: false, detail: 'exited 1' }]} stick={false} />)
expect(container.querySelector('[class*="bg-danger/10"]')).toBeTruthy()
})

test('a clean end gets the green wash; a stopped one gets none', () => {
const { container } = render(<EventList events={[{ kind: 'end', ok: true }]} stick={false} />)
expect(container.querySelector('[class*="bg-success/10"]')).toBeTruthy()
cleanup()
const { container: stopped } = render(<EventList events={[{ kind: 'end', ok: false, stopped: true }]} stick={false} />)
expect(stopped.querySelector('[class*="bg-info"], [class*="bg-danger"], [class*="bg-success"]')).toBeNull()
})

test("an agent reply stays plain canvas — the log's bulk must not shout", () => {
const { container } = render(
<EventList events={[{ kind: 'driver', event: { type: 'text', text: 'working on it' } }]} stick={false} />,
)
expect(container.querySelector('[class*="bg-info"], [class*="bg-danger"], [class*="bg-success"]')).toBeNull()
})
})
18 changes: 17 additions & 1 deletion packages/framework-dashboard/components/EventList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,20 @@ function badgeTone(e: FrameworkEvent): string {
return ''
}

/**
* The row's BACKGROUND wash (#1508), the layer above {@link badgeTone}'s markers: a tint across
* the whole line, findable from the scrollbar's distance where a coloured badge word is not.
* Only the rows the eye actually hunts for get one — the reader's own turns (the log's natural
* chapter marks), failures, and the run landing cleanly — and at a whisper of alpha, so the
* text keeps the contrast and the bulk of the log stays plain canvas.
*/
function rowWash(e: FrameworkEvent): string {
if (isFailure(e)) return 'bg-danger/10'
if (e.kind === 'driver' && e.event.type === 'start') return 'bg-info/10'
if ((e.kind === 'end' && e.ok) || e.kind === 'ready-for-merge') return 'bg-success/10'
return ''
}

/**
* Hoist the run's first prompt to the top of the log (#1170).
*
Expand Down Expand Up @@ -292,7 +306,9 @@ export function EventList({
const chunkHead = !prev || rowGroup(prev) !== rowGroup(e)
const at = receivedAt(e)
return (
<MessageScrollerItem key={i} messageId={String(i)} scrollAnchor={isTurnBoundary(e)} className="flex items-start gap-2">
// Every row carries the same -mx/px pair so a washed row's band and a plain row's
// text share the exact same columns; only the background differs.
<MessageScrollerItem key={i} messageId={String(i)} scrollAnchor={isTurnBoundary(e)} className={`-mx-1.5 flex items-start gap-2 rounded-sm px-1.5 ${rowWash(e)}`}>
{/* Fixed-width badge column so the text lines up whether or not this row repeats the badge. Wide enough for the longest common label ("system prompt") to sit on one line. */}
<span className="w-28 shrink-0">
{chunkHead && (
Expand Down
Loading