open-swe/ui/src/lib/utils.ts
Johannes du Plessis 73b3ba7930
feat: collapse finished agent work into a Worked for X dropdown (#1509)
Once an agent turn finishes, its reasoning/tool/exploration steps collapse
behind a single "Worked for …" toggle so the chat shows only the final reply,
mirroring Devin's transcript. Work stays expanded live and auto-collapses on
completion; the final reply text/cards remain visible.

Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>
2026-06-12 10:08:12 -07:00

62 lines
No EOL
2.3 KiB
TypeScript

import { clsx } from "clsx"
import { twMerge } from "tailwind-merge"
import type { ClassValue } from "clsx";
/**
* Merge class names into a single string.
* @param inputs - The class names to merge.
* @returns The merged class name string.
* @example
* cn("text-red-500", "bg-blue-500") // "text-red-500 bg-blue-500"
* cn("text-red-500", "bg-blue-500", "text-2xl") // "text-red-500 bg-blue-500 text-2xl"
*/
export function cn(...inputs: Array<ClassValue>) {
return twMerge(clsx(inputs))
}
/**
* Format an elapsed duration in milliseconds as a compact string (e.g. "5s", "3m 20s").
*/
export function formatElapsed(ms: number): string {
const secs = Math.max(1, Math.ceil(ms / 1000));
return secs < 60 ? `${secs}s` : `${Math.floor(secs / 60)}m ${secs % 60}s`;
}
/**
* Intl.RelativeTimeFormat instance for formatting relative times.
*/
const rtf = new Intl.RelativeTimeFormat("en", { numeric: "auto" });
/**
* Format a timestamp as a relative time string.
* @param ts - The timestamp to format.
* @returns The relative time string.
*
* @example
* formatRelativeTime(Date.now()) // "just now"
* formatRelativeTime(Date.now() - 1000) // "1 second ago"
* formatRelativeTime(Date.now() - 60 * 1000) // "1 minute ago"
* formatRelativeTime(Date.now() - 60 * 60 * 1000) // "1 hour ago"
* formatRelativeTime(Date.now() - 24 * 60 * 60 * 1000) // "1 day ago"
* formatRelativeTime(Date.now() - 7 * 24 * 60 * 60 * 1000) // "1 week ago"
*/
export function formatRelativeTime(ts: number): string {
const diffMs = ts - Date.now(); // negative for past
const absMs = Math.abs(diffMs);
if (absMs < 60_000) return rtf.format(Math.round(diffMs / 1000), "second");
if (absMs < 60 * 60_000) return rtf.format(Math.round(diffMs / 60_000), "minute");
if (absMs < 24 * 60 * 60_000) {
return rtf.format(Math.round(diffMs / (60 * 60_000)), "hour");
}
if (absMs < 7 * 24 * 60 * 60_000) {
return rtf.format(Math.round(diffMs / (24 * 60 * 60_000)), "day");
}
if (absMs < 30 * 24 * 60 * 60_000) {
return rtf.format(Math.round(diffMs / (7 * 24 * 60 * 60_000)), "week");
}
if (absMs < 365 * 24 * 60 * 60_000) {
return rtf.format(Math.round(diffMs / (30 * 24 * 60 * 60_000)), "month");
}
return rtf.format(Math.round(diffMs / (365 * 24 * 60 * 60_000)), "year");
}