From 512692dd36e222758fcbd5e656bb6ea7dae56618 Mon Sep 17 00:00:00 2001 From: "devin-ai-integration[bot]" <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Fri, 9 Oct 2026 23:30:22 -0700 Subject: [PATCH] feat(ui): rename the Playground System One tab to Decisions and give the JSON editor VS Code light and dark themes (#45729) * fix(ui): render the System One JSON editor with the stock One Dark theme Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> * feat(ui): rename the Playground System One tab to Decisions and use the VS Code Dark+ JSON theme Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> * fix(ui): keep the JSON editor header and issue list on the light surface Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> * chore(ui): drop the root block scanner test and the unused band styles Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> * feat(ui): follow the dashboard theme in the JSON editor and drop the breakdown accents Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> * fix(ui): round the Decisions score pill to two decimals Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> * fix(ui): strip the Prism block backgrounds so the JSON editor theme swap stops the React style warning Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> --------- Co-authored-by: kerry Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> --- .../components/systemOneUI/JsonEditor.tsx | 110 ++++++++++-------- .../systemOneUI/QuestionBreakdown.tsx | 9 +- .../systemOneUI/ResponseView.test.tsx | 14 +++ .../components/systemOneUI/ResponseView.tsx | 4 +- .../systemOneUI/lib/rootBlocks.test.ts | 53 --------- .../components/systemOneUI/lib/rootBlocks.ts | 65 ----------- .../app/(dashboard)/playground/page.test.tsx | 4 +- .../src/app/(dashboard)/playground/page.tsx | 2 +- 8 files changed, 85 insertions(+), 176 deletions(-) delete mode 100644 ui/litellm-dashboard/src/app/(dashboard)/playground/components/systemOneUI/lib/rootBlocks.test.ts delete mode 100644 ui/litellm-dashboard/src/app/(dashboard)/playground/components/systemOneUI/lib/rootBlocks.ts diff --git a/ui/litellm-dashboard/src/app/(dashboard)/playground/components/systemOneUI/JsonEditor.tsx b/ui/litellm-dashboard/src/app/(dashboard)/playground/components/systemOneUI/JsonEditor.tsx index fce617b4c47..cc7bebb0bb8 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/playground/components/systemOneUI/JsonEditor.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/playground/components/systemOneUI/JsonEditor.tsx @@ -1,11 +1,12 @@ import { Badge } from "@/components/ui/badge"; import { cn } from "@/lib/cva.config"; import { CircleAlert, CircleCheck, TriangleAlert } from "lucide-react"; -import { useId, useMemo, useRef } from "react"; +import { useTheme } from "next-themes"; +import { useId, useRef } from "react"; import { createElement, PrismLight as SyntaxHighlighter } from "react-syntax-highlighter"; import type { SyntaxHighlighterProps } from "react-syntax-highlighter"; import json from "react-syntax-highlighter/dist/esm/languages/prism/json"; -import { findRootBlocks, ROOT_BLOCK_STYLES, type RootBlock } from "./lib/rootBlocks"; +import { vs, vscDarkPlus } from "react-syntax-highlighter/dist/esm/styles/prism"; import type { SystemOnePayloadValidation } from "./lib/validatePayload"; SyntaxHighlighter.registerLanguage("json", json); @@ -14,16 +15,44 @@ const EDITOR_TEXT = "m-0 whitespace-pre-wrap wrap-anywhere py-3 font-mono text-x const GUTTER_WIDTH = "w-11"; type LineRendererProps = Parameters>[0]; const CONTENT_INSET = "pl-14 pr-3"; -const CODE_TAG_PROPS = { className: "language-json", style: { whiteSpace: "pre-wrap" } } as const; +type SyntaxTheme = NonNullable; +const THEME_BLOCKS = ['pre[class*="language-"]', 'code[class*="language-"]'] as const; -const TOKEN_COLORS = [ - "[&_.token.property]:text-sky-700 dark:[&_.token.property]:text-sky-300", - "[&_.token.string]:text-emerald-700 dark:[&_.token.string]:text-emerald-300", - "[&_.token.number]:text-amber-700 dark:[&_.token.number]:text-amber-300", - "[&_.token.boolean]:text-violet-700 dark:[&_.token.boolean]:text-violet-300", - "[&_.token.null]:text-violet-700 dark:[&_.token.null]:text-violet-300", - "[&_.token.punctuation]:text-muted-foreground [&_.token.operator]:text-muted-foreground", -].join(" "); +function withoutBlockBackground(theme: SyntaxTheme): SyntaxTheme { + const stripped = THEME_BLOCKS.map((block) => { + const { background: _background, backgroundColor: _backgroundColor, ...rest } = theme[block] ?? {}; + return [block, rest] as const; + }); + return { ...theme, ...Object.fromEntries(stripped) }; +} + +const VS_CODE_DARK_PLUS = withoutBlockBackground(vscDarkPlus); +const VS_CODE_LIGHT_PLUS = { + ...withoutBlockBackground(vs), + property: { color: "#0451a5" }, + string: { color: "#a31515" }, + number: { color: "#098658" }, + boolean: { color: "#0000ff" }, + punctuation: { color: "#000000" }, + operator: { color: "#000000" }, +} as const; +const INHERIT_TEXT = { + fontFamily: "inherit", + fontSize: "inherit", + lineHeight: "inherit", + whiteSpace: "pre-wrap", + overflowWrap: "anywhere", + textShadow: "none", +} as const; +const CODE_TAG_PROPS = { className: "language-json", style: INHERIT_TEXT } as const; +const TRANSPARENT_PRE = { + ...INHERIT_TEXT, + background: "transparent", + border: 0, + margin: 0, + padding: 0, + overflow: "visible", +} as const; interface JsonEditorProps { value: string; @@ -69,39 +98,24 @@ export function IssueList({ id, validation }: { id: string; validation: SystemOn ); } -function renderLines(rootBlocks: readonly RootBlock[]) { - return function LineRows({ rows, stylesheet, useInlineStyles }: LineRendererProps) { - return rows.map((row, line) => { - const lineElement = { node: row, stylesheet, useInlineStyles, key: line }; - const block = rootBlocks.find(({ startLine, endLine }) => line >= startLine && line <= endLine); - return ( -
- - {line + 1} - - - {createElement(lineElement)} - -
- ); - }); - }; +function LineRows({ rows, stylesheet, useInlineStyles }: LineRendererProps) { + return rows.map((row, line) => { + const lineElement = { node: row, stylesheet, useInlineStyles, key: line }; + return ( +
+ + {line + 1} + + {createElement(lineElement)} +
+ ); + }); } export default function JsonEditor({ value, onChange, validation }: JsonEditorProps) { const issuesId = useId(); const highlightRef = useRef(null); - const renderer = useMemo(() => renderLines(findRootBlocks(value)), [value]); + const syntaxTheme = useTheme().resolvedTheme === "dark" ? VS_CODE_DARK_PLUS : VS_CODE_LIGHT_PLUS; const lineCount = value.split("\n").length; const hasErrors = !validation.isValid; @@ -121,20 +135,22 @@ export default function JsonEditor({ value, onChange, validation }: JsonEditorPr {lineCount} {lineCount === 1 ? "line" : "lines"} -
-