From fff40e2ed6a99ee8efa335c53b186d8a49e30be9 Mon Sep 17 00:00:00 2001 From: Krrish Dholakia Date: Sat, 4 Oct 2025 11:29:15 -0700 Subject: [PATCH] fix(datautils.ts): fix clipboard copy issue on http endpoints --- ui/litellm-dashboard/src/utils/dataUtils.ts | 86 +++++++++++++++------ 1 file changed, 61 insertions(+), 25 deletions(-) diff --git a/ui/litellm-dashboard/src/utils/dataUtils.ts b/ui/litellm-dashboard/src/utils/dataUtils.ts index 29ffd8eec0..d511ce8eb9 100644 --- a/ui/litellm-dashboard/src/utils/dataUtils.ts +++ b/ui/litellm-dashboard/src/utils/dataUtils.ts @@ -1,43 +1,79 @@ -import NotificationsManager from "@/components/molecules/notifications_manager"; -import { message } from "antd"; +import NotificationsManager from "@/components/molecules/notifications_manager" +import { message } from "antd" + +export function updateExistingKeys(target: Source, source: Object): Source { + const clonedTarget = structuredClone(target) -export function updateExistingKeys( - target: Source, - source: Object -): Source { - const clonedTarget = structuredClone(target); - for (const [key, value] of Object.entries(source)) { if (key in clonedTarget) { - (clonedTarget as any)[key] = value; + ;(clonedTarget as any)[key] = value } } - return clonedTarget; + return clonedTarget } export const formatNumberWithCommas = (value: number | null | undefined, decimals: number = 0): string => { if (value === null || value === undefined) { - return '-'; + return "-" } - return value.toLocaleString('en-US', { + return value.toLocaleString("en-US", { minimumFractionDigits: decimals, maximumFractionDigits: decimals, - }); -}; + }) +} export const copyToClipboard = async ( text: string | null | undefined, - messageText: string = "Copied to clipboard" + messageText: string = "Copied to clipboard", ): Promise => { - if (!text) return false; - try { - await navigator.clipboard.writeText(text); - NotificationsManager.success(messageText); - return true; - } catch (err) { - NotificationsManager.fromBackend("Failed to copy to clipboard"); - console.error("Failed to copy: ", err); - return false; + if (!text) return false + + // Check if clipboard API is available + if (navigator && navigator.clipboard && navigator.clipboard.writeText) { + try { + await navigator.clipboard.writeText(text) + NotificationsManager.success(messageText) + return true + } catch (err) { + console.error("Clipboard API failed: ", err) + // Fall back to legacy method + return fallbackCopyToClipboard(text, messageText) + } + } else { + // Use fallback method when clipboard API is not available + return fallbackCopyToClipboard(text, messageText) } -}; \ No newline at end of file +} + +// Fallback method using document.execCommand (deprecated but widely supported) +const fallbackCopyToClipboard = (text: string, messageText: string): boolean => { + try { + const textArea = document.createElement("textarea") + textArea.value = text + + // Make the textarea invisible + textArea.style.position = "fixed" + textArea.style.left = "-999999px" + textArea.style.top = "-999999px" + textArea.setAttribute("readonly", "") + + document.body.appendChild(textArea) + textArea.focus() + textArea.select() + + const successful = document.execCommand("copy") + document.body.removeChild(textArea) + + if (successful) { + NotificationsManager.success(messageText) + return true + } else { + throw new Error("execCommand failed") + } + } catch (err) { + NotificationsManager.fromBackend("Failed to copy to clipboard") + console.error("Failed to copy: ", err) + return false + } +}