Skip to content

Commit 8ef7958

Browse files
authored
fix: Improve timestamp alignment in build logs (#437)
### Problem Each build-log row shows a relative elapsed time (e.g. 3.6s) followed by the absolute time (e.g. 04:50:49.96 PM). The elapsed time has a variable width, which caused two visible bugs: 1. Truncation — once elapsed time hit two digits (10.2s), the extra character pushed the row past the fixed-width column and the absolute time got clipped to …. 2. Misalignment — the variable-width elapsed time shifted the absolute time to a different horizontal position on every row, so timestamps didn't line up vertically and were hard to scan. On top of that, the elapsed value itself jumped around: formatDurationCompact emitted unpadded fields (2m 8s vs 2m 27s), so the seconds column wandered between single- and double-digit widths. ### Solution Gave the timestamp a stable, fixed-width layout so the whole column lines up vertically and is easy to scan top-to-bottom. The elapsed time and the absolute time now each occupy a fixed column, durations are zero-padded so digits don't shift between rows, and the full timestamp always fits without truncation. <img width="290" height="441" alt="1" src="https://github.com/user-attachments/assets/15dc06ad-fa68-41b8-892b-eb64564cabbf" /> <img width="287" height="310" alt="2" src="https://github.com/user-attachments/assets/c63b26cb-3ae0-4789-ae0d-7053f00083a2" />
1 parent 58abb98 commit 8ef7958

3 files changed

Lines changed: 13 additions & 8 deletions

File tree

src/features/dashboard/build/logs-cells.tsx

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ import {
77
} from '@/features/dashboard/common/log-cells'
88
import { formatDurationCompact } from '@/lib/utils/formatting'
99
import CopyButtonInline from '@/ui/copy-button-inline'
10-
import { Badge, type BadgeProps } from '@/ui/primitives/badge'
1110

1211
export const LogLevel = ({ level }: { level: BuildLogModel['level'] }) => {
1312
return <LogLevelBadge level={level} />
@@ -27,10 +26,13 @@ export const Timestamp = ({
2726
return (
2827
<CopyButtonInline
2928
value={date.toISOString()}
30-
className="font-mono group prose-table-numeric truncate"
29+
truncate={false}
30+
className="font-mono group prose-table-numeric"
3131
>
32-
{formatDurationCompact(millisAfterStart, true)}{' '}
33-
<span className="group-hover:text-current transition-colors text-fg-tertiary">
32+
<span className="inline-block w-[7ch] shrink-0 text-right">
33+
{formatDurationCompact(millisAfterStart, true, true)}
34+
</span>
35+
<span className="ml-2 whitespace-nowrap group-hover:text-current transition-colors text-fg-tertiary">
3436
{format(date, 'hh:mm:ss.SS a', {
3537
locale: enUS,
3638
})}

src/features/dashboard/build/logs.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ import { useBuildLogs } from './use-build-logs'
3434
import useLogFilters from './use-log-filters'
3535

3636
// Column width are calculated as max width of the content + padding
37-
const COLUMN_WIDTHS_PX = { timestamp: 176 + 16, level: 52 + 16 } as const
37+
const COLUMN_WIDTHS_PX = { timestamp: 204 + 16, level: 52 + 16 } as const
3838
const ROW_HEIGHT_PX = 26
3939
const LIVE_STATUS_ROW_HEIGHT_PX = ROW_HEIGHT_PX + 16
4040
const VIRTUAL_OVERSCAN = 16

src/lib/utils/formatting.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -344,19 +344,22 @@ export function formatDuration(durationMs: number): string {
344344

345345
export function formatDurationCompact(
346346
ms: number,
347-
showDecimalSeconds = false
347+
showDecimalSeconds = false,
348+
padTrailingField = false
348349
): string {
349350
const seconds = Math.floor(ms / 1000)
350351
const minutes = Math.floor(seconds / 60)
351352
const hours = Math.floor(minutes / 60)
353+
const pad = (n: number) =>
354+
padTrailingField ? n.toString().padStart(2, '0') : `${n}`
352355

353356
if (hours > 0) {
354357
const remainingMinutes = minutes % 60
355-
return `${hours}h ${remainingMinutes}m`
358+
return `${hours}h ${pad(remainingMinutes)}m`
356359
}
357360
if (minutes > 0) {
358361
const remainingSeconds = seconds % 60
359-
return `${minutes}m ${remainingSeconds}s`
362+
return `${minutes}m ${pad(remainingSeconds)}s`
360363
}
361364
return showDecimalSeconds
362365
? `${seconds}.${Math.floor((ms % 1000) / 100)}s`

0 commit comments

Comments
 (0)