/** * @license * Copyright 2026 Google LLC * SPDX-License-Identifier: Apache-2.0 */ import { REFERENCE_CONTENT_START, REFERENCE_CONTENT_END, } from '@google/gemini-cli-core'; export const formatBytes = (bytes: number): string => { const gb = bytes / (1024 * 1024 * 1024); if (bytes < 1024 * 1024) { return `${(bytes / 1024).toFixed(1)} KB`; } if (bytes < 1024 * 1024 * 1024) { return `${(bytes / (1024 * 1024)).toFixed(1)} MB`; } return `${gb.toFixed(2)} GB`; }; /** * Formats a duration in milliseconds into a concise, human-readable string (e.g., "1h 5s"). * It omits any time units that are zero. * @param milliseconds The duration in milliseconds. * @returns A formatted string representing the duration. */ export const formatDuration = (milliseconds: number): string => { if (milliseconds <= 0) { return '0s'; } if (milliseconds < 1000) { return `${Math.round(milliseconds)}ms`; } const totalSeconds = milliseconds / 1000; if (totalSeconds < 60) { return `${totalSeconds.toFixed(1)}s`; } const hours = Math.floor(totalSeconds / 3600); const minutes = Math.floor((totalSeconds % 3600) / 60); const seconds = Math.floor(totalSeconds % 60); const parts: string[] = []; if (hours > 0) { parts.push(`${hours}h`); } if (minutes > 0) { parts.push(`${minutes}m`); } if (seconds > 0) { parts.push(`${seconds}s`); } // If all parts are zero (e.g., exactly 1 hour), return the largest unit. if (parts.length === 0) { if (hours > 0) return `${hours}h`; if (minutes > 0) return `${minutes}m`; return `${seconds}s`; } return parts.join(' '); }; export const formatTimeAgo = (date: string | number | Date): string => { const past = new Date(date); if (isNaN(past.getTime())) { return 'invalid date'; } const now = new Date(); const diffMs = now.getTime() - past.getTime(); if (diffMs < 60000) { return 'just now'; } return `${formatDuration(diffMs)} ago`; }; /** * Removes content bounded by reference content markers from the given text. * The markers are "${REFERENCE_CONTENT_START}" and "${REFERENCE_CONTENT_END}". * * @param text The input text containing potential reference blocks. * @returns The text with reference blocks removed and trimmed. */ export function stripReferenceContent(text: string): string { // Match optional newline, the start marker, content (non-greedy), and the end marker const pattern = new RegExp( `\\n?${REFERENCE_CONTENT_START}[\\s\\S]*?${REFERENCE_CONTENT_END}`, 'g', ); return text.replace(pattern, '').trim(); } export const formatResetTime = ( resetTime: string | undefined, format: 'terse' | 'column' | 'full' = 'full', ): string => { if (!resetTime) return ''; const resetDate = new Date(resetTime); if (isNaN(resetDate.getTime())) return ''; const diff = resetDate.getTime() - Date.now(); if (diff <= 0) return ''; const totalMinutes = Math.ceil(diff / (1000 * 60)); const hours = Math.floor(totalMinutes / 60); const minutes = totalMinutes % 60; const isTerse = format === 'terse'; const isColumn = format === 'column'; if (isTerse || isColumn) { const hoursStr = hours > 0 ? `${hours}h` : ''; const minutesStr = minutes > 0 ? `${minutes}m` : ''; const duration = hoursStr && minutesStr ? `${hoursStr} ${minutesStr}` : hoursStr || minutesStr; if (isColumn) { const timeStr = new Intl.DateTimeFormat('en-US', { hour: 'numeric', minute: 'numeric', }).format(resetDate); return duration ? `${timeStr} (${duration})` : timeStr; } return duration; } let duration = ''; if (hours > 0) { duration = `${hours} hour${hours > 1 ? 's' : ''}`; if (minutes > 0) { duration += ` ${minutes} minute${minutes > 1 ? 's' : ''}`; } } else { duration = `${minutes} minute${minutes > 1 ? 's' : ''}`; } const timeStr = new Intl.DateTimeFormat('en-US', { hour: 'numeric', minute: 'numeric', timeZoneName: 'short', }).format(resetDate); return `${duration} at ${timeStr}`; };