From 9a26da90a47459d249030b6873ec99619161fdc9 Mon Sep 17 00:00:00 2001
From: Yuneng Jiang
Date: Thu, 13 Aug 2026 15:59:53 -0700
Subject: [PATCH] refactor(ui): migrate playground to shadcn
Replaces antd and Tremor with shadcn primitives across the six route-owned,
form-free playground components: the compare view and its panel, message input
and unified selector, plus the realtime playground and the agent builder.
Markup only, no behaviour change. The characterisation tests added in the
previous commit are untouched here and stay green through the swap.
Adds ui/slider.tsx via the shadcn CLI and retires the six antd
no-restricted-imports suppressions the migration made obsolete.
---
ui/litellm-dashboard/eslint-suppressions.json | 30 +-
.../components/chat_ui/AgentBuilderView.tsx | 530 +++++++++---------
.../components/chat_ui/RealtimePlayground.tsx | 77 +--
.../components/compareUI/CompareUI.tsx | 76 ++-
.../compareUI/components/ComparisonPanel.tsx | 75 ++-
.../compareUI/components/MessageInput.tsx | 33 +-
.../compareUI/components/UnifiedSelector.tsx | 66 ++-
.../src/components/ui/slider.tsx | 41 ++
8 files changed, 514 insertions(+), 414 deletions(-)
create mode 100644 ui/litellm-dashboard/src/components/ui/slider.tsx
diff --git a/ui/litellm-dashboard/eslint-suppressions.json b/ui/litellm-dashboard/eslint-suppressions.json
index d7f71a5840d..0c5bcc65702 100644
--- a/ui/litellm-dashboard/eslint-suppressions.json
+++ b/ui/litellm-dashboard/eslint-suppressions.json
@@ -1055,9 +1055,6 @@
"no-nested-ternary": {
"count": 2
},
- "no-restricted-imports": {
- "count": 1
- },
"react-hooks/set-state-in-effect": {
"count": 5
}
@@ -1098,9 +1095,6 @@
"no-nested-ternary": {
"count": 2
},
- "no-restricted-imports": {
- "count": 1
- },
"react-hooks/immutability": {
"count": 2
},
@@ -1115,9 +1109,6 @@
"no-nested-ternary": {
"count": 4
},
- "no-restricted-imports": {
- "count": 1
- },
"react-hooks/set-state-in-effect": {
"count": 1
}
@@ -1125,9 +1116,6 @@
"src/app/(dashboard)/playground/components/compareUI/components/ComparisonPanel.tsx": {
"local/no-complex-jsx-arrow": {
"count": 2
- },
- "no-restricted-imports": {
- "count": 1
}
},
"src/app/(dashboard)/playground/components/compareUI/components/MessageDisplay.tsx": {
@@ -1135,21 +1123,11 @@
"count": 1
}
},
- "src/app/(dashboard)/playground/components/compareUI/components/MessageInput.tsx": {
- "no-restricted-imports": {
- "count": 1
- }
- },
"src/app/(dashboard)/playground/components/compareUI/components/ModelSelector.tsx": {
"no-restricted-imports": {
"count": 2
}
},
- "src/app/(dashboard)/playground/components/compareUI/components/UnifiedSelector.tsx": {
- "no-restricted-imports": {
- "count": 1
- }
- },
"src/app/(dashboard)/playground/components/complianceUI/ComplianceUI.tsx": {
"local/no-complex-jsx-arrow": {
"count": 2
@@ -3582,6 +3560,14 @@
"count": 1
}
},
+ "src/components/ui/slider.tsx": {
+ "local/filename-pascal-case": {
+ "count": 1
+ },
+ "no-nested-ternary": {
+ "count": 1
+ }
+ },
"src/components/ui/switch.tsx": {
"local/filename-pascal-case": {
"count": 1
diff --git a/ui/litellm-dashboard/src/app/(dashboard)/playground/components/chat_ui/AgentBuilderView.tsx b/ui/litellm-dashboard/src/app/(dashboard)/playground/components/chat_ui/AgentBuilderView.tsx
index d4333b95c62..44995707b10 100644
--- a/ui/litellm-dashboard/src/app/(dashboard)/playground/components/chat_ui/AgentBuilderView.tsx
+++ b/ui/litellm-dashboard/src/app/(dashboard)/playground/components/chat_ui/AgentBuilderView.tsx
@@ -1,16 +1,24 @@
"use client";
-import {
- CommentOutlined,
- DeleteOutlined,
- ExperimentOutlined,
- LinkOutlined,
- PlusOutlined,
- RobotOutlined,
- SaveOutlined,
-} from "@ant-design/icons";
-import { Button, Input, Modal, Select, Spin, Tabs } from "antd";
+import { Bot, FlaskConical, Link as LinkIcon, MessageSquare, Plus, Save, Trash2 } from "lucide-react";
import React, { useCallback, useEffect, useState } from "react";
+import {
+ AlertDialog,
+ AlertDialogAction,
+ AlertDialogContent,
+ AlertDialogDescription,
+ AlertDialogFooter,
+ AlertDialogHeader,
+ AlertDialogTitle,
+} from "@/components/ui/alert-dialog";
+import { Button } from "@/components/ui/button";
+import { Input } from "@/components/ui/input";
+import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
+import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
+import { Textarea } from "@/components/ui/textarea";
+import { UiLoadingSpinner } from "@/components/ui/ui-loading-spinner";
+import { MultiSelect } from "@/components/shared/MultiSelect";
+import { useVisitedTabs } from "@/hooks/useVisitedTabs";
import CodeBlock from "@/components/CodeBlock";
import NotificationsManager from "@/components/molecules/notifications_manager";
import {
@@ -27,8 +35,6 @@ import { fetchAvailableModels, ModelGroup } from "@/components/llm_calls/fetch_m
import ComplianceUI from "../complianceUI/ComplianceUI";
import ChatUI from "./ChatUI";
-const { TextArea } = Input;
-
export interface AgentBuilderViewProps {
accessToken: string | null;
token: string | null;
@@ -45,6 +51,8 @@ export interface AgentBuilderViewProps {
const NEW_AGENT_ID = "__new__";
+type AgentTab = "configure" | "chat" | "test" | "connect";
+
function getConnectTabBaseUrl(
proxySettings: AgentBuilderViewProps["proxySettings"],
customProxyBaseUrl?: string,
@@ -116,7 +124,7 @@ function ConnectTabContent({
Create a virtual key that can only call this agent. The key will be scoped to you (user_id) and restricted to
the model {agentName}.
-