From 81e8ee7c33e863c17513090d5893084bc80b53cb Mon Sep 17 00:00:00 2001 From: Serhii Dziupin Date: Wed, 5 Aug 2026 13:43:04 +0300 Subject: [PATCH] feat(sidebar): show compact timestamp in recent activity rows MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The sidebar's recent activity list (SidebarActivitySections) rendered its session rows without an inline timestamp on web/desktop — the compact relative label only appeared in the hover tooltip and on touch runtimes. Render the existing i18n-backed formatSessionCompactDateLabel inline in the recent rows' metadata slot, alongside the goal/branch glyphs, for web/desktop too. It keeps the same hover-fade as the other metadata, so the hover-revealed row actions never overlap it, and the full date stays available in the row tooltip. No new strings: the label reuses common.relative.* keys. Fixes #2560 --- .../session/sidebar/SessionNodeItem.test.ts | 41 +++++++++++++++++++ .../session/sidebar/SessionNodeItem.tsx | 14 ++++++- 2 files changed, 54 insertions(+), 1 deletion(-) create mode 100644 packages/ui/src/components/session/sidebar/SessionNodeItem.test.ts diff --git a/packages/ui/src/components/session/sidebar/SessionNodeItem.test.ts b/packages/ui/src/components/session/sidebar/SessionNodeItem.test.ts new file mode 100644 index 00000000..dc77851c --- /dev/null +++ b/packages/ui/src/components/session/sidebar/SessionNodeItem.test.ts @@ -0,0 +1,41 @@ +import { describe, expect, test } from 'bun:test'; +import { readFileSync } from 'node:fs'; + +const source = readFileSync(new URL('./SessionNodeItem.tsx', import.meta.url), 'utf8'); + +describe('SessionNodeItem recent-activity timestamp', () => { + test('the recent activity rows render the compact timestamp in the inline metadata slot', () => { + // The right-slot guard must open for recent rows even when no activity, + // goal glyph, or branch marker is present. + const guard = source.indexOf("showActivityDuration || sessionGoalGlyph || showInlineBranchMarker || renderContext === 'recent'"); + expect(guard).toBeGreaterThan(-1); + // The recent-only block sits inside that slot… + const guardOpen = source.indexOf("{renderContext === 'recent' ? (", guard); + expect(guardOpen).toBeGreaterThan(guard); + // …and the compact label rendered there is the first one after it. + const label = source.indexOf('{sessionCompactUpdatedLabel}', guardOpen); + expect(label).toBeGreaterThan(guardOpen); + // The only later occurrence is the pre-existing row tooltip (which shows + // the full date), not a second inline render. + const tooltipLabel = source.indexOf('{sessionCompactUpdatedLabel}', label + 1); + expect(tooltipLabel).toBeGreaterThan(label); + expect(source.indexOf('title={sessionUpdatedLabel}', tooltipLabel - 80)).toBeGreaterThan(-1); + }); + + test('the timestamp shares the hover-fade of the other metadata so revealed actions never overlap it', () => { + const guard = source.indexOf("showActivityDuration || sessionGoalGlyph || showInlineBranchMarker || renderContext === 'recent'"); + // The slot content fades out while the row is hovered (hideOnHoverClass) + // and while the row menu is open — the same span that now carries the + // recent timestamp. + const hideOnHover = source.indexOf('hideOnHoverClass', guard); + expect(hideOnHover).toBeGreaterThan(guard); + expect(hideOnHover).toBeLessThan(source.indexOf("{renderContext === 'recent' ? (", guard)); + }); + + test('the compact label uses the existing i18n-backed relative time helper', () => { + // formatSessionCompactDateLabel (already used by touch runtimes and the + // row tooltip) is the source of the label — no new formatting code. + expect(source.indexOf('const sessionCompactUpdatedLabel = formatSessionCompactDateLabel(sessionTimestamp);')).toBeGreaterThan(-1); + expect(source.indexOf('{sessionCompactUpdatedLabel}')).toBeGreaterThan(-1); + }); +}); diff --git a/packages/ui/src/components/session/sidebar/SessionNodeItem.tsx b/packages/ui/src/components/session/sidebar/SessionNodeItem.tsx index 6521feba..3a502597 100644 --- a/packages/ui/src/components/session/sidebar/SessionNodeItem.tsx +++ b/packages/ui/src/components/session/sidebar/SessionNodeItem.tsx @@ -1257,7 +1257,7 @@ function SessionNodeItemComponent(props: Props): React.ReactNode { )} - ) : (showActivityDuration || sessionGoalGlyph || showInlineBranchMarker) ? ( + ) : (showActivityDuration || sessionGoalGlyph || showInlineBranchMarker || renderContext === 'recent') ? (
) : null} + {/* The recent activity list shows its compact + timestamp inline (touch runtimes already get + it through the alwaysShowActions branch); + it shares the slot with the goal/branch + metadata and hides on hover exactly like + them, so the revealed row actions never + overlap it. */} + {renderContext === 'recent' ? ( + + {sessionCompactUpdatedLabel} + + ) : null} )}