From 290fc203aca33ff61fa1fd64f0e9e5cfff4acb78 Mon Sep 17 00:00:00 2001 From: moe-berri Date: Sat, 3 Oct 2026 19:10:25 -0700 Subject: [PATCH] feat(lens): refine onboarding motion and linked examples --- .../src/components/lens/setup/GatewayFlow.tsx | 108 ++++++++++ .../lens/setup/LensIntroduction.module.css | 111 +++++++++- .../lens/setup/LensIntroduction.tsx | 190 ++++++++---------- 3 files changed, 295 insertions(+), 114 deletions(-) create mode 100644 ui/litellm-dashboard/src/components/lens/setup/GatewayFlow.tsx diff --git a/ui/litellm-dashboard/src/components/lens/setup/GatewayFlow.tsx b/ui/litellm-dashboard/src/components/lens/setup/GatewayFlow.tsx new file mode 100644 index 00000000000..36657f0aad2 --- /dev/null +++ b/ui/litellm-dashboard/src/components/lens/setup/GatewayFlow.tsx @@ -0,0 +1,108 @@ +import { useId } from "react"; +import styles from "./LensIntroduction.module.css"; + +const streams = [ + { + id: "runs", + color: "text-sky-500 dark:text-sky-400", + path: "M -32 66 C 24 66 32 22 96 22 S 192 86 256 86 S 368 28 422 28 C 464 28 472 54 500 54 C 558 54 584 86 646 86 S 756 22 818 22 S 924 86 986 86 S 1040 54 1056 54", + }, + { + id: "steps", + color: "text-violet-500 dark:text-violet-400", + path: "M -32 30 C 40 30 58 78 120 78 S 218 24 284 24 S 390 76 444 76 C 470 76 480 54 503 54", + }, + { + id: "tools", + color: "text-amber-500 dark:text-amber-400", + path: "M -32 54 C 26 54 68 68 128 68 S 230 38 288 38 S 396 62 448 62 C 474 62 484 54 503 54", + }, +]; + +export function GatewayFlow() { + const id = useId(); + return ( +
+
+
+

Agent swarms

+

Every run, every recorded step

+
+
+

LiteLLM gateway

+

One place, your infrastructure

+
+
+

Lens

+

Findings to improve your agents

+
+
+ +
+ ); +} diff --git a/ui/litellm-dashboard/src/components/lens/setup/LensIntroduction.module.css b/ui/litellm-dashboard/src/components/lens/setup/LensIntroduction.module.css index c2940a9c1b5..8554f4b27d0 100644 --- a/ui/litellm-dashboard/src/components/lens/setup/LensIntroduction.module.css +++ b/ui/litellm-dashboard/src/components/lens/setup/LensIntroduction.module.css @@ -1,18 +1,117 @@ -.flow { - animation: gateway-flow 10s linear infinite; +.packets { + stroke-dasharray: 0 16; + animation: signal-flow 14s linear infinite; } -@keyframes gateway-flow { +.trails { + stroke-dasharray: 6 42; + stroke-dashoffset: -10; + opacity: 0.3; + animation: signal-trails 14s linear infinite; +} + +.gatewayGlow { + opacity: 0.7; + transition: opacity 300ms ease; +} + +.flowGraphic:hover .gatewayGlow { + opacity: 1; +} + +.sampleCard { + transition: + border-color 200ms ease, + box-shadow 200ms ease; +} + +.sampleCard:hover, +.sampleCard:focus-within { + border-color: color-mix(in oklab, var(--color-indigo-500) 22%, var(--border)); + box-shadow: 0 4px 20px -16px color-mix(in oklab, var(--color-indigo-500) 40%, transparent); +} + +.examples { + --evidence-tint: color-mix(in oklab, var(--color-rose-400) 6%, transparent); + --evidence-outline: color-mix(in oklab, var(--color-rose-400) 32%, transparent); +} + +.evidenceLink { + cursor: pointer; + border-radius: 4px; + outline-offset: 4px; + transition: background-color 200ms ease; +} + +.evidenceLink:focus-visible { + outline: 2px solid var(--ring); +} + +.evidenceLink:hover { + background: var(--evidence-tint); +} + +.evidenceOutput, +.evidenceFinding, +.evidenceBar { + transition: + background-color 200ms ease, + box-shadow 200ms ease; +} + +.evidenceFinding { + border-radius: 6px; +} + +.examples:is( + [data-evidence-active="true"], + :has([data-evidence-link]:hover), + :has([data-evidence-link]:focus-visible) + ) { + .evidenceOutput { + box-shadow: inset 0 0 0 1px var(--evidence-outline); + } + + .evidenceFinding { + background: var(--evidence-tint); + box-shadow: 0 0 0 7px var(--evidence-tint); + } + + .evidenceBar { + box-shadow: 0 0 10px 2px var(--evidence-outline); + } +} + +@keyframes signal-flow { from { - transform: translateX(0); + stroke-dashoffset: 0; } to { - transform: translateX(240px); + stroke-dashoffset: -144; + } +} + +@keyframes signal-trails { + from { + stroke-dashoffset: -10; + } + to { + stroke-dashoffset: -154; } } @media (prefers-reduced-motion: reduce) { - .flow { + .packets, + .trails { animation: none; } + + .gatewayGlow, + .sampleCard, + .evidenceLink, + .evidenceOutput, + .evidenceFinding, + .evidenceBar { + transition: none; + } } diff --git a/ui/litellm-dashboard/src/components/lens/setup/LensIntroduction.tsx b/ui/litellm-dashboard/src/components/lens/setup/LensIntroduction.tsx index 1dadd0141b4..20aff27ca27 100644 --- a/ui/litellm-dashboard/src/components/lens/setup/LensIntroduction.tsx +++ b/ui/litellm-dashboard/src/components/lens/setup/LensIntroduction.tsx @@ -1,77 +1,8 @@ -import { useId } from "react"; +import { useState } from "react"; import { ArrowRight, ArrowUpRight, ChevronDown, ChevronRight } from "lucide-react"; import { Button } from "@/components/ui/button"; import styles from "./LensIntroduction.module.css"; - -const swarmColors = ["fill-violet-400", "fill-sky-400", "fill-amber-400", "fill-rose-400", "fill-indigo-400"]; - -function flowDot(index: number) { - const column = (index % 124) - 24; - const row = Math.floor(index / 124); - const phase = (column + 24) % 24; - const wave = 2.5 + 2.6 * Math.sin(((phase + 5) / 24) * Math.PI * 2); - if (Math.abs(row - wave) > 0.9) return null; - return { - x: column * 10 + 5, - y: row * 10 + 5, - color: swarmColors[(phase + row) % swarmColors.length], - }; -} - -const flowDots = Array.from({ length: 744 }, (_, index) => flowDot(index)).filter((dot) => dot !== null); - -function GatewayFlow() { - const patternId = useId(); - const beforeGateway = `${patternId}-before`; - const afterGateway = `${patternId}-after`; - return ( -
-
-
-

Agent swarms

-

Every run, every recorded step

-
-
-

LiteLLM gateway

-

One place, your infrastructure

-
-
-

Lens

-

Findings to improve your agents

-
-
- -
- ); -} +import { GatewayFlow } from "./GatewayFlow"; const traceSteps = [ { name: "research_agent", start: 0, duration: 14.2, failed: false }, @@ -84,9 +15,14 @@ const traceSteps = [ const sampleAnswer = "The column store is 40% faster than the row store for your workload."; -function TraceExample() { +type EvidenceProps = { highlighted: boolean; onHighlight: () => void }; + +function TraceExample({ highlighted, onHighlight }: EvidenceProps) { return ( -
+

Tracing @@ -94,38 +30,59 @@ function TraceExample() {

Sample trace · 14.2s

    - {traceSteps.map((item, index) => ( -
  1. - 0 ? "pl-2" : ""}`}> - {index === 0 ? ( -
  2. - ))} +
-
+

run_benchmark · output

Error: benchmark runner unavailable (503)

-
+

answer · output

“{sampleAnswer}”

@@ -134,9 +91,12 @@ function TraceExample() { ); } -function FindingExamples() { +function FindingExamples({ highlighted, onHighlight }: EvidenceProps) { return ( -
+

Lens findings @@ -144,10 +104,19 @@ function FindingExamples() {

500 sample runs reviewed

-
-

-

);