fix(ui): reserve stage column on every row for consistent alignment

PR-Agent flagged that trace child rows rendered an extra stage-hint
cell while the header grid template only had 7 columns. Result: every
expanded child shifted module/message/latency/request rightward, and
the stage chip overlapped its neighbour in the trace timeline.

Add a dedicated 72px "Stage" column to the header grid template and
to every row variant. Standalone leaves and trace heads render an
empty span at the same width; trace children with stage hints render
the chip in that slot. Column edges now match across all row types.

Refs #1138, #1151
This commit is contained in:
Tam Nhu Tran
2026-04-30 16:09:50 -04:00
parent 04ddc33518
commit 40cc62a67f
3 changed files with 18 additions and 4 deletions
+7 -3
View File
@@ -24,10 +24,13 @@ export interface LogsEntryListProps {
density?: RowDensity;
}
// Leading 16px slot reserves space for the trace-expand chevron so that
// trace rows and standalone leaf rows align under the same column edges.
// Column layout shared by the header and every row variant:
// chevron(16) · time(88) · level(64) · stage(72) · module(140) · message(flex) · latency(72) · request(112)
// The dedicated `stage` column is rendered as an empty span on rows that
// have no stage hint so column edges align across leaf, trace head, and
// trace child rows alike.
const COLS_TEMPLATE =
'grid grid-cols-[16px_88px_64px_140px_minmax(0,1fr)_72px_112px] items-center gap-3 px-3';
'grid grid-cols-[16px_88px_64px_72px_140px_minmax(0,1fr)_72px_112px] items-center gap-3 px-3';
export function LogsEntryList({
entries,
@@ -149,6 +152,7 @@ export function LogsEntryList({
<span role="columnheader" aria-hidden="true" />
<span role="columnheader">Time</span>
<span role="columnheader">Level</span>
<span role="columnheader">Stage</span>
<span role="columnheader">Module</span>
<span role="columnheader">Message</span>
<span role="columnheader" className="text-right">
+11 -1
View File
@@ -59,6 +59,10 @@ function LogsRowImpl({
const handleSelect = () => onSelect(entry.id);
const handleKey = (e: KeyboardEvent<HTMLDivElement>) => {
// Ignore keys that bubbled from a nested interactive element (e.g. the
// copy-requestId button). Without this guard, pressing Enter on the
// copy button would also select the row and shift the detail panel.
if (e.target !== e.currentTarget) return;
if (e.key === 'Enter' || e.key === ' ') {
e.preventDefault();
handleSelect();
@@ -106,6 +110,10 @@ function LogsRowImpl({
<span role="cell" className="flex w-[64px] shrink-0 items-center">
<LogLevelBadge level={entry.level} />
</span>
{/* Always reserve the stage column so leaf, trace head, and trace
child rows all align under the same column edges in the header
grid template. Children with `stageHint` show a chip; rows
without one render an empty span at the same width. */}
{stageHint ? (
<span
role="cell"
@@ -113,7 +121,9 @@ function LogsRowImpl({
>
{stageHint}
</span>
) : null}
) : (
<span role="cell" className="w-[72px] shrink-0" aria-hidden="true" />
)}
<span
role="cell"
className="w-[140px] shrink-0 truncate text-[12px] font-medium text-foreground/80"
Binary file not shown.