mirror of
https://github.com/BerriAI/litellm.git
synced 2026-10-04 02:31:27 +00:00
feat(ui): AntDLoadingSpinner + view_logs small files to shadcn
- AntDLoadingSpinner: antd Spin + @ant-design LoadingOutlined \u2192 lucide Loader2 with animate-spin. Name kept for back-compat with many call sites. - JsonViewer: antd Typography.Text \u2192 plain span with semantic tokens. The external react-json-view-lite renderer is unchanged. - TruncatedValue: antd Typography.Text copyable + antd Tooltip \u2192 shadcn Tooltip + inline lucide Copy/Check button. Preserves the max-width + mono font style. - TokenFlow: antd Typography.Text \u2192 plain span. Gates: TS \u2713 | Build \u2713. Co-authored-by: yuneng-jiang <yuneng-berri@users.noreply.github.com>
This commit is contained in:
parent
948c80da15
commit
b1f9b8bf09
4 changed files with 106 additions and 43 deletions
|
|
@ -1,12 +1,26 @@
|
|||
import { Spin } from "antd";
|
||||
import { LoadingOutlined } from "@ant-design/icons";
|
||||
import { Loader2 } from "lucide-react";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
interface AntDLoadingSpinnerProps {
|
||||
size?: "small" | "default" | "large";
|
||||
fontSize?: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Back-compat spinner. The name is preserved for a growing number of
|
||||
* call sites; the implementation is now a lucide Loader2 sized via
|
||||
* Tailwind.
|
||||
*/
|
||||
export function AntDLoadingSpinner({ size, fontSize }: AntDLoadingSpinnerProps) {
|
||||
const indicator = <LoadingOutlined style={fontSize ? { fontSize } : undefined} spin />;
|
||||
return <Spin indicator={indicator} size={size} />;
|
||||
const sizeClass = cn(
|
||||
size === "small" && "h-3 w-3",
|
||||
size === "large" && "h-6 w-6",
|
||||
!size || size === "default" ? "h-4 w-4" : "",
|
||||
);
|
||||
return (
|
||||
<Loader2
|
||||
className={cn("animate-spin text-primary", sizeClass)}
|
||||
style={fontSize ? { fontSize } : undefined}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,21 +1,22 @@
|
|||
import { Typography } from "antd";
|
||||
import { JsonView, defaultStyles } from "react-json-view-lite";
|
||||
import "react-json-view-lite/dist/index.css";
|
||||
import { JSON_MAX_HEIGHT, COLOR_BG_LIGHT, SPACING_LARGE } from "./constants";
|
||||
|
||||
const { Text } = Typography;
|
||||
import {
|
||||
JSON_MAX_HEIGHT,
|
||||
COLOR_BG_LIGHT,
|
||||
SPACING_LARGE,
|
||||
} from "./constants";
|
||||
|
||||
interface JsonViewerProps {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
data: any;
|
||||
mode: "formatted";
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays JSON data in formatted tree view.
|
||||
* Uses an interactive tree component for easy navigation.
|
||||
*/
|
||||
export function JsonViewer({ data }: JsonViewerProps) {
|
||||
if (!data) return <Text type="secondary">No data</Text>;
|
||||
if (!data) return <span className="text-muted-foreground">No data</span>;
|
||||
|
||||
return (
|
||||
<div
|
||||
|
|
|
|||
|
|
@ -1,7 +1,3 @@
|
|||
import { Typography } from "antd";
|
||||
|
||||
const { Text } = Typography;
|
||||
|
||||
interface TokenFlowProps {
|
||||
prompt?: number;
|
||||
completion?: number;
|
||||
|
|
@ -10,13 +6,16 @@ interface TokenFlowProps {
|
|||
|
||||
/**
|
||||
* Displays token usage in LiteLLM format: "12 (9 prompt tokens + 3 completion tokens)"
|
||||
* Shows total with breakdown of prompt and completion tokens.
|
||||
*/
|
||||
export function TokenFlow({ prompt = 0, completion = 0, total = 0 }: TokenFlowProps) {
|
||||
export function TokenFlow({
|
||||
prompt = 0,
|
||||
completion = 0,
|
||||
total = 0,
|
||||
}: TokenFlowProps) {
|
||||
return (
|
||||
<Text>
|
||||
{total.toLocaleString()} ({prompt.toLocaleString()} prompt tokens + {completion.toLocaleString()} completion
|
||||
tokens)
|
||||
</Text>
|
||||
<span>
|
||||
{total.toLocaleString()} ({prompt.toLocaleString()} prompt tokens +{" "}
|
||||
{completion.toLocaleString()} completion tokens)
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,16 @@
|
|||
import { Typography, Tooltip } from "antd";
|
||||
import { DEFAULT_MAX_WIDTH, FONT_FAMILY_MONO, FONT_SIZE_SMALL } from "./constants";
|
||||
|
||||
const { Text } = Typography;
|
||||
import React, { useState } from "react";
|
||||
import {
|
||||
Tooltip,
|
||||
TooltipContent,
|
||||
TooltipProvider,
|
||||
TooltipTrigger,
|
||||
} from "@/components/ui/tooltip";
|
||||
import { Check, Copy } from "lucide-react";
|
||||
import {
|
||||
DEFAULT_MAX_WIDTH,
|
||||
FONT_FAMILY_MONO,
|
||||
FONT_SIZE_SMALL,
|
||||
} from "./constants";
|
||||
|
||||
interface TruncatedValueProps {
|
||||
value?: string;
|
||||
|
|
@ -9,27 +18,67 @@ interface TruncatedValueProps {
|
|||
}
|
||||
|
||||
/**
|
||||
* Displays a truncated value with tooltip and copy functionality.
|
||||
* Useful for displaying long IDs, URLs, or other text that may overflow.
|
||||
* Displays a truncated value with hover tooltip and inline copy button.
|
||||
*/
|
||||
export function TruncatedValue({ value, maxWidth = DEFAULT_MAX_WIDTH }: TruncatedValueProps) {
|
||||
if (!value) return <Text type="secondary">-</Text>;
|
||||
export function TruncatedValue({
|
||||
value,
|
||||
maxWidth = DEFAULT_MAX_WIDTH,
|
||||
}: TruncatedValueProps) {
|
||||
const [copied, setCopied] = useState(false);
|
||||
if (!value) return <span className="text-muted-foreground">-</span>;
|
||||
|
||||
const handleCopy = async (e: React.MouseEvent) => {
|
||||
e.stopPropagation();
|
||||
try {
|
||||
await navigator.clipboard.writeText(value);
|
||||
setCopied(true);
|
||||
setTimeout(() => setCopied(false), 1500);
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
} catch (_err) {
|
||||
/* noop */
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Tooltip title={value}>
|
||||
<Text
|
||||
copyable={{ text: value, tooltips: ["Copy", "Copied!"] }}
|
||||
style={{
|
||||
maxWidth,
|
||||
display: "inline-block",
|
||||
verticalAlign: "bottom",
|
||||
fontFamily: FONT_FAMILY_MONO,
|
||||
fontSize: FONT_SIZE_SMALL,
|
||||
}}
|
||||
ellipsis
|
||||
>
|
||||
{value}
|
||||
</Text>
|
||||
</Tooltip>
|
||||
<span className="inline-flex items-center gap-1 align-bottom">
|
||||
<TooltipProvider>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<span
|
||||
className="inline-block truncate"
|
||||
style={{
|
||||
maxWidth,
|
||||
fontFamily: FONT_FAMILY_MONO,
|
||||
fontSize: FONT_SIZE_SMALL,
|
||||
}}
|
||||
>
|
||||
{value}
|
||||
</span>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent className="max-w-md break-all">
|
||||
{value}
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
</TooltipProvider>
|
||||
<TooltipProvider>
|
||||
<Tooltip open={copied || undefined}>
|
||||
<TooltipTrigger asChild>
|
||||
<button
|
||||
type="button"
|
||||
onClick={handleCopy}
|
||||
className="text-muted-foreground hover:text-foreground"
|
||||
aria-label="Copy"
|
||||
>
|
||||
{copied ? (
|
||||
<Check className="h-3 w-3 text-emerald-500" />
|
||||
) : (
|
||||
<Copy className="h-3 w-3" />
|
||||
)}
|
||||
</button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>{copied ? "Copied!" : "Copy"}</TooltipContent>
|
||||
</Tooltip>
|
||||
</TooltipProvider>
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue