From 3b986612818e4d3012e56f062b8b1141e852af62 Mon Sep 17 00:00:00 2001 From: Mohamed Boudra Date: Sun, 27 Sep 2026 12:41:40 +0200 Subject: [PATCH] refactor(app): share the compact time-ago prose formatter Move the tab tooltip's "3m" -> "3m ago" wording into utils/time so the usage card can reuse it. --- .../screens/workspace/workspace-desktop-tabs-row.tsx | 9 ++------- packages/app/src/utils/time.test.ts | 10 ++++++++++ packages/app/src/utils/time.ts | 10 ++++++++++ 3 files changed, 22 insertions(+), 7 deletions(-) diff --git a/packages/app/src/screens/workspace/workspace-desktop-tabs-row.tsx b/packages/app/src/screens/workspace/workspace-desktop-tabs-row.tsx index 286030fd86..124c76d812 100644 --- a/packages/app/src/screens/workspace/workspace-desktop-tabs-row.tsx +++ b/packages/app/src/screens/workspace/workspace-desktop-tabs-row.tsx @@ -69,6 +69,7 @@ import { RenderProfile } from "@/utils/render-profiler"; import { TrailingActionScrim } from "@/components/ui/trailing-action-scrim"; import { useKeyboardActionHandler } from "@/hooks/use-keyboard-action-handler"; import { useCompactTimeAgo } from "@/hooks/use-compact-time-ago"; +import { formatCompactTimeAgoAsProse } from "@/utils/time"; import { buildWorkspaceKeyboardHandlerId } from "@/keyboard/handler-id"; import type { KeyboardActionDefinition } from "@/keyboard/keyboard-action-dispatcher"; import { WorkspaceNewTabMenuContent } from "@/screens/workspace/workspace-new-tab-menu"; @@ -148,12 +149,6 @@ function formatAgentTooltipTitle(singleLineTitle: string): string { return `${singleLineTitle.slice(0, AGENT_TOOLTIP_TITLE_MAX_LENGTH - 1).trimEnd()}…`; } -function formatAgentTooltipActivity(compactActivity: string): string { - if (compactActivity === "now") return "just now"; - if (/^\d/.test(compactActivity)) return `${compactActivity} ago`; - return compactActivity; -} - function AgentTabTooltipBody({ serverId, agentId, @@ -169,7 +164,7 @@ function AgentTabTooltipBody({ return state.agentLastActivity.get(agentId) ?? agent?.lastActivityAt ?? null; }); const compactActivity = useCompactTimeAgo(lastActivityAt); - const activity = formatAgentTooltipActivity(compactActivity); + const activity = formatCompactTimeAgoAsProse(compactActivity); return ( diff --git a/packages/app/src/utils/time.test.ts b/packages/app/src/utils/time.test.ts index 292fffbf6d..0ea19548c4 100644 --- a/packages/app/src/utils/time.test.ts +++ b/packages/app/src/utils/time.test.ts @@ -2,6 +2,7 @@ import { describe, it, expect } from "vitest"; import { describeCompactTimeAgo, formatCompactTimeAgo, + formatCompactTimeAgoAsProse, formatDuration, formatMessageTimestamp, formatTimeAgo, @@ -63,6 +64,15 @@ describe("describeCompactTimeAgo", () => { }); }); +describe("formatCompactTimeAgoAsProse", () => { + it("puts elapsed labels back into prose and leaves dates alone", () => { + expect(formatCompactTimeAgoAsProse("now")).toBe("just now"); + expect(formatCompactTimeAgoAsProse("3m")).toBe("3m ago"); + expect(formatCompactTimeAgoAsProse("2h")).toBe("2h ago"); + expect(formatCompactTimeAgoAsProse("Jan 15")).toBe("Jan 15"); + }); +}); + describe("formatDuration", () => { it("renders sub-minute durations as whole seconds", () => { expect(formatDuration(0)).toBe("0s"); diff --git a/packages/app/src/utils/time.ts b/packages/app/src/utils/time.ts index 8fa94e8926..668998ddf4 100644 --- a/packages/app/src/utils/time.ts +++ b/packages/app/src/utils/time.ts @@ -91,6 +91,16 @@ export function formatCompactTimeAgo(date: Date, now: Date = new Date()): string return describeCompactTimeAgo(date, now).label; } +/** + * A compact label put back into a sentence, for a tooltip or a line with room for prose: + * "now" → "just now", "5m" → "5m ago", "Jan 15" stays a date. + */ +export function formatCompactTimeAgoAsProse(label: string): string { + if (label === "now") return "just now"; + if (/^\d/.test(label)) return `${label} ago`; + return label; +} + /** * How many local midnights lie between two instants: 0 for the same day, 1 for yesterday. * Counted on the calendar rather than in elapsed time, so six days and 23 hours ago on