From a5b6f31585cebb717e7f62967602a48c89b60210 Mon Sep 17 00:00:00 2001 From: Yuneng Jiang Date: Tue, 25 Aug 2026 21:24:07 -0700 Subject: [PATCH 1/3] refactor(ui): install the shadcn alert primitive components/shared/Alert.tsx was base-vega's own alert.tsx copied in by hand, carrying the same four exports and the same class strings, so npx shadcn add could never reach it and it would drift from every upstream fix silently. It also still wrapped each part in forwardRef, which React 19 no longer needs. Install the primitive into components/ui/ where the CLI can update it, and reduce the shared file to a wrapper that adds the four status variants (info, success, warning, error) the dashboard actually uses on top of upstream's default and destructive. Rendered output is unchanged: every variant produces byte-identical classes, role and data-variant, so all 45 call sites look the same. --- ui/litellm-dashboard/eslint-suppressions.json | 5 ++ .../src/components/shared/Alert.tsx | 85 +++++-------------- .../src/components/ui/alert.tsx | 56 ++++++++++++ 3 files changed, 82 insertions(+), 64 deletions(-) create mode 100644 ui/litellm-dashboard/src/components/ui/alert.tsx diff --git a/ui/litellm-dashboard/eslint-suppressions.json b/ui/litellm-dashboard/eslint-suppressions.json index a647eedc354..e22812df465 100644 --- a/ui/litellm-dashboard/eslint-suppressions.json +++ b/ui/litellm-dashboard/eslint-suppressions.json @@ -2133,6 +2133,11 @@ "count": 1 } }, + "src/components/ui/alert.tsx": { + "local/filename-pascal-case": { + "count": 1 + } + }, "src/components/ui/avatar.tsx": { "local/filename-pascal-case": { "count": 1 diff --git a/ui/litellm-dashboard/src/components/shared/Alert.tsx b/ui/litellm-dashboard/src/components/shared/Alert.tsx index 490fcba6ef5..1a895c204de 100644 --- a/ui/litellm-dashboard/src/components/shared/Alert.tsx +++ b/ui/litellm-dashboard/src/components/shared/Alert.tsx @@ -1,75 +1,32 @@ -import * as React from "react"; - -import { cva, type VariantProps } from "class-variance-authority"; +import type * as React from "react"; +import { Alert as AlertPrimitive, AlertAction, AlertDescription, AlertTitle } from "@/components/ui/alert"; import { cn } from "@/lib/cva.config"; -const alertVariants = cva( - "group/alert relative grid w-full gap-0.5 rounded-lg border px-4 py-3 text-left text-sm has-data-[slot=alert-action]:relative has-data-[slot=alert-action]:pr-18 has-[>svg]:grid-cols-[auto_1fr] has-[>svg]:gap-x-2.5 *:[svg]:row-span-2 *:[svg]:translate-y-0.5 *:[svg]:text-current *:[svg:not([class*='size-'])]:size-4", - { - variants: { - variant: { - default: "bg-card text-card-foreground", - destructive: - "bg-card text-destructive *:data-[slot=alert-description]:text-destructive/90 *:[svg]:text-current", - info: "border-info/20 bg-info/5 text-info *:[svg]:text-current", - success: "border-success/20 bg-success/5 text-success *:[svg]:text-current", - warning: "border-warning/20 bg-warning/5 text-warning *:[svg]:text-current", - error: - "border-destructive/20 bg-destructive/10 text-destructive *:data-[slot=alert-description]:text-destructive/90 *:[svg]:text-destructive", - }, - }, - defaultVariants: { - variant: "default", - }, - }, -); +const STATUS_VARIANT_CLASSES = { + info: "border-info/20 bg-info/5 text-info *:[svg]:text-current", + success: "border-success/20 bg-success/5 text-success *:[svg]:text-current", + warning: "border-warning/20 bg-warning/5 text-warning *:[svg]:text-current", + error: + "border-destructive/20 bg-destructive/10 text-destructive *:data-[slot=alert-description]:text-destructive/90 *:[svg]:text-destructive", +} as const; -type AlertProps = React.ComponentProps<"div"> & VariantProps; +type StatusVariant = keyof typeof STATUS_VARIANT_CLASSES; +type AlertVariant = NonNullable["variant"]> | StatusVariant; -const Alert = React.forwardRef(({ className, variant = "default", ...props }, ref) => ( -
, "variant"> & { + variant?: AlertVariant; +}; + +const isStatusVariant = (variant: AlertVariant): variant is StatusVariant => variant in STATUS_VARIANT_CLASSES; + +const Alert = ({ variant = "default", className, ...props }: AlertProps) => ( + -)); -Alert.displayName = "Alert"; - -const AlertTitle = React.forwardRef>(({ className, ...props }, ref) => ( -
svg]/alert:col-start-2 [&_a]:underline [&_a]:underline-offset-3 [&_a]:hover:text-foreground", - className, - )} - {...props} - /> -)); -AlertTitle.displayName = "AlertTitle"; - -const AlertDescription = React.forwardRef>( - ({ className, ...props }, ref) => ( -
- ), ); -AlertDescription.displayName = "AlertDescription"; - -const AlertAction = React.forwardRef>(({ className, ...props }, ref) => ( -
-)); -AlertAction.displayName = "AlertAction"; export { Alert, AlertTitle, AlertDescription, AlertAction }; diff --git a/ui/litellm-dashboard/src/components/ui/alert.tsx b/ui/litellm-dashboard/src/components/ui/alert.tsx new file mode 100644 index 00000000000..01d86e0961a --- /dev/null +++ b/ui/litellm-dashboard/src/components/ui/alert.tsx @@ -0,0 +1,56 @@ +import * as React from "react"; +import { cva, type VariantProps } from "class-variance-authority"; + +import { cn } from "@/lib/cva.config"; + +const alertVariants = cva( + "group/alert relative grid w-full gap-0.5 rounded-lg border px-4 py-3 text-left text-sm has-data-[slot=alert-action]:relative has-data-[slot=alert-action]:pr-18 has-[>svg]:grid-cols-[auto_1fr] has-[>svg]:gap-x-2.5 *:[svg]:row-span-2 *:[svg]:translate-y-0.5 *:[svg]:text-current *:[svg:not([class*='size-'])]:size-4", + { + variants: { + variant: { + default: "bg-card text-card-foreground", + destructive: + "bg-card text-destructive *:data-[slot=alert-description]:text-destructive/90 *:[svg]:text-current", + }, + }, + defaultVariants: { + variant: "default", + }, + }, +); + +function Alert({ className, variant, ...props }: React.ComponentProps<"div"> & VariantProps) { + return
; +} + +function AlertTitle({ className, ...props }: React.ComponentProps<"div">) { + return ( +
svg]/alert:col-start-2 [&_a]:underline [&_a]:underline-offset-3 [&_a]:hover:text-foreground", + className, + )} + {...props} + /> + ); +} + +function AlertDescription({ className, ...props }: React.ComponentProps<"div">) { + return ( +
+ ); +} + +function AlertAction({ className, ...props }: React.ComponentProps<"div">) { + return
; +} + +export { Alert, AlertTitle, AlertDescription, AlertAction }; From d2aea2d4e7eff9aaa720a6d6b5ec3884daf94052 Mon Sep 17 00:00:00 2001 From: Yuneng Jiang Date: Tue, 25 Aug 2026 22:56:59 -0700 Subject: [PATCH 2/3] refactor(ui): re-pull label, textarea, separator and skeleton from the registry These four primitives still wrapped their body in React.forwardRef, which the dashboard has not needed since it moved to React 19: a function component receives ref as an ordinary prop and the existing {...props} spread already hands it to the DOM node. Re-pulling each from base-vega drops the wrapper and its displayName. These four were picked because the ref plumbing is their only divergence from current upstream, so the class strings, data-slot values and exports are untouched and nothing renders differently. The other seven primitives that still carry forwardRef have also drifted on their class strings, so re-pulling them would ship a visual change alongside the cleanup and they are left alone here. Textarea is the one with real ref call sites, roughly seventeen of them through react-hook-form's field.ref, and ref-forwarding.test.tsx did not cover it. Add that case next to the Label, Separator and Skeleton ones already there. --- ui/litellm-dashboard/src/components/ui/label.tsx | 10 ++++------ .../src/components/ui/ref-forwarding.test.tsx | 7 +++++++ ui/litellm-dashboard/src/components/ui/separator.tsx | 11 ++++------- ui/litellm-dashboard/src/components/ui/skeleton.tsx | 11 +++-------- ui/litellm-dashboard/src/components/ui/textarea.tsx | 10 ++++------ 5 files changed, 22 insertions(+), 27 deletions(-) diff --git a/ui/litellm-dashboard/src/components/ui/label.tsx b/ui/litellm-dashboard/src/components/ui/label.tsx index 1ac4eed0d4e..ded2dfc1a7b 100644 --- a/ui/litellm-dashboard/src/components/ui/label.tsx +++ b/ui/litellm-dashboard/src/components/ui/label.tsx @@ -4,10 +4,9 @@ import * as React from "react"; import { cn } from "@/lib/cva.config"; -const Label = React.forwardRef>( - ({ className, ...props }, ref) => ( +function Label({ className, ...props }: React.ComponentProps<"label">) { + return (