feat(observability2): apply design system to trace components
Phase 3 of visual redesign - refine trace-specific visualization: - Create custom zincDarkTheme for syntax highlighting - Update trace-summary with flat zinc metric cards - Restyle code-context-section with zinc hierarchy - Apply zinc styling to function-to-optimize-section - Clean decorative elements from llm-calls-timeline - Establish clear visual hierarchy in errors-section - Update trace-search with zinc input styling - Migrate page.tsx and layout.tsx to zinc colors Requirements: TRC-01 through TRC-05
This commit is contained in:
parent
021ed80988
commit
5441ffa800
9 changed files with 355 additions and 301 deletions
|
|
@ -2,11 +2,11 @@ import { ReactNode } from "react"
|
|||
|
||||
export default function Observability2Layout({ children }: { children: ReactNode }) {
|
||||
return (
|
||||
<div className="min-h-screen bg-gray-50 dark:bg-gray-900">
|
||||
<nav className="border-b border-gray-200 dark:border-gray-700 bg-white dark:bg-gray-800">
|
||||
<div className="min-h-screen bg-zinc-50 dark:bg-zinc-950">
|
||||
<nav className="border-b border-zinc-200 dark:border-zinc-700 bg-white dark:bg-zinc-800">
|
||||
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||
<div className="flex h-14 items-center justify-center">
|
||||
<h1 className="text-lg font-semibold text-gray-900 dark:text-white">
|
||||
<h1 className="text-lg font-semibold text-zinc-900 dark:text-zinc-50">
|
||||
Observability v2
|
||||
</h1>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -311,13 +311,13 @@ function TraceContent({ traceId, traceData }: { traceId: string; traceData: Trac
|
|||
function EmptyState() {
|
||||
return (
|
||||
<div className="flex flex-col items-center justify-center py-16 text-center">
|
||||
<div className="w-16 h-16 mb-6 rounded-full bg-gray-100 dark:bg-gray-800 flex items-center justify-center">
|
||||
<Search className="h-8 w-8 text-gray-400" />
|
||||
<div className="w-16 h-16 mb-6 rounded-full bg-zinc-100 dark:bg-zinc-800 flex items-center justify-center">
|
||||
<Search className="h-8 w-8 text-zinc-400" />
|
||||
</div>
|
||||
<h3 className="text-xl font-semibold text-gray-900 dark:text-white mb-2">
|
||||
<h3 className="text-xl font-semibold text-zinc-900 dark:text-zinc-50 mb-2">
|
||||
Enter a Trace ID to Get Started
|
||||
</h3>
|
||||
<p className="text-gray-500 dark:text-gray-400 max-w-md">
|
||||
<p className="text-zinc-500 dark:text-zinc-400 max-w-md">
|
||||
Paste or type a trace ID in the search box above to view all associated LLM calls,
|
||||
generated candidates, and any errors that occurred during processing.
|
||||
</p>
|
||||
|
|
@ -331,14 +331,14 @@ function NotFoundState({ traceId }: { traceId: string }) {
|
|||
<div className="w-16 h-16 mb-6 rounded-full bg-red-100 dark:bg-red-900/30 flex items-center justify-center">
|
||||
<Search className="h-8 w-8 text-red-500" />
|
||||
</div>
|
||||
<h3 className="text-xl font-semibold text-gray-900 dark:text-white mb-2">Trace Not Found</h3>
|
||||
<p className="text-gray-500 dark:text-gray-400 max-w-md mb-4">
|
||||
<h3 className="text-xl font-semibold text-zinc-900 dark:text-zinc-50 mb-2">Trace Not Found</h3>
|
||||
<p className="text-zinc-500 dark:text-zinc-400 max-w-md mb-4">
|
||||
No data was found for the trace ID:
|
||||
</p>
|
||||
<code className="text-sm font-mono text-gray-900 dark:text-white bg-gray-100 dark:bg-gray-700 px-3 py-2 rounded">
|
||||
<code className="text-sm font-mono text-zinc-900 dark:text-zinc-50 bg-zinc-100 dark:bg-zinc-700 px-3 py-2 rounded">
|
||||
{traceId}
|
||||
</code>
|
||||
<p className="text-gray-500 dark:text-gray-400 max-w-md mt-4 text-sm">
|
||||
<p className="text-zinc-500 dark:text-zinc-400 max-w-md mt-4 text-sm">
|
||||
Please check that the trace ID is correct and try again.
|
||||
</p>
|
||||
</div>
|
||||
|
|
@ -353,8 +353,8 @@ function SearchSkeleton() {
|
|||
return (
|
||||
<div className="w-full max-w-2xl mx-auto">
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="flex-1 h-12 bg-gray-200 dark:bg-gray-700 rounded-lg animate-pulse" />
|
||||
<div className="w-24 h-12 bg-gray-200 dark:bg-gray-700 rounded-lg animate-pulse" />
|
||||
<div className="flex-1 h-12 bg-zinc-200 dark:bg-zinc-700 rounded-md animate-pulse" />
|
||||
<div className="w-24 h-12 bg-zinc-200 dark:bg-zinc-700 rounded-md animate-pulse" />
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
|
@ -364,38 +364,38 @@ function TraceContentSkeleton() {
|
|||
return (
|
||||
<div className="space-y-6">
|
||||
{/* Summary skeleton */}
|
||||
<div className="bg-white dark:bg-gray-800 rounded-lg shadow p-6 border border-gray-200 dark:border-gray-700">
|
||||
<div className="bg-white dark:bg-zinc-800 rounded-md shadow p-6 border border-zinc-200 dark:border-zinc-700">
|
||||
<div className="grid grid-cols-2 md:grid-cols-4 gap-6">
|
||||
{createSkeletonArray(4).map((_, i) => (
|
||||
<div key={i}>
|
||||
<div className="h-4 w-16 bg-gray-200 dark:bg-gray-700 rounded mb-2 animate-pulse" />
|
||||
<div className="h-8 w-24 bg-gray-200 dark:bg-gray-700 rounded animate-pulse" />
|
||||
<div className="h-4 w-16 bg-zinc-200 dark:bg-zinc-700 rounded mb-2 animate-pulse" />
|
||||
<div className="h-8 w-24 bg-zinc-200 dark:bg-zinc-700 rounded animate-pulse" />
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Timeline skeleton */}
|
||||
<div className="bg-white dark:bg-gray-800 rounded-lg shadow border border-gray-200 dark:border-gray-700">
|
||||
<div className="p-6 border-b border-gray-200 dark:border-gray-700">
|
||||
<div className="h-6 w-48 bg-gray-200 dark:bg-gray-700 rounded animate-pulse" />
|
||||
<div className="bg-white dark:bg-zinc-800 rounded-md shadow border border-zinc-200 dark:border-zinc-700">
|
||||
<div className="p-6 border-b border-zinc-200 dark:border-zinc-700">
|
||||
<div className="h-6 w-48 bg-zinc-200 dark:bg-zinc-700 rounded animate-pulse" />
|
||||
</div>
|
||||
<div className="divide-y divide-gray-200 dark:divide-gray-700">
|
||||
<div className="divide-y divide-zinc-200 dark:divide-zinc-700">
|
||||
{createSkeletonArray(3).map((_, i) => (
|
||||
<div key={i} className="p-4">
|
||||
<div className="h-6 bg-gray-200 dark:bg-gray-700 rounded animate-pulse" />
|
||||
<div className="h-6 bg-zinc-200 dark:bg-zinc-700 rounded animate-pulse" />
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Errors skeleton */}
|
||||
<div className="bg-white dark:bg-gray-800 rounded-lg shadow border border-gray-200 dark:border-gray-700">
|
||||
<div className="p-6 border-b border-gray-200 dark:border-gray-700">
|
||||
<div className="h-6 w-32 bg-gray-200 dark:bg-gray-700 rounded animate-pulse" />
|
||||
<div className="bg-white dark:bg-zinc-800 rounded-md shadow border border-zinc-200 dark:border-zinc-700">
|
||||
<div className="p-6 border-b border-zinc-200 dark:border-zinc-700">
|
||||
<div className="h-6 w-32 bg-zinc-200 dark:bg-zinc-700 rounded animate-pulse" />
|
||||
</div>
|
||||
<div className="p-6">
|
||||
<div className="h-16 bg-gray-200 dark:bg-gray-700 rounded animate-pulse" />
|
||||
<div className="h-16 bg-zinc-200 dark:bg-zinc-700 rounded animate-pulse" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
"use client"
|
||||
|
||||
import { useState, useMemo, useCallback, memo } from "react"
|
||||
import { ChevronDown, ChevronRight, Code, FileText, Hash, FolderTree } from "lucide-react"
|
||||
import { ChevronDown, Code, FileText, Hash, FolderTree } from "lucide-react"
|
||||
import { CodeHighlighter, CODE_STYLE } from "./code-highlighter"
|
||||
import { CopyButton } from "@/components/observability/copy-button"
|
||||
import { InfoIcon } from "@/components/observability/info-icon"
|
||||
|
|
@ -109,15 +109,15 @@ export const CodeContextSection = memo(function CodeContextSection({
|
|||
}
|
||||
|
||||
return (
|
||||
<div className="bg-white dark:bg-gray-800 rounded-lg shadow border border-gray-200 dark:border-gray-700">
|
||||
<div className="bg-white dark:bg-zinc-900 rounded-sm border border-zinc-200 dark:border-zinc-800">
|
||||
{/* Header - always visible */}
|
||||
<button
|
||||
onClick={() => setIsExpanded(!isExpanded)}
|
||||
className="w-full p-6 flex items-center justify-between hover:bg-gray-50 dark:hover:bg-gray-700/50 transition-colors rounded-lg"
|
||||
className="w-full p-6 flex items-center justify-between hover:bg-zinc-50 dark:hover:bg-zinc-800 transition-colors rounded-sm"
|
||||
>
|
||||
<div className="flex items-center gap-2">
|
||||
<Code className="h-5 w-5 text-gray-400" />
|
||||
<h2 className="text-xl font-bold text-gray-900 dark:text-white">Code Context</h2>
|
||||
<Code className="h-4 w-4 text-zinc-400" />
|
||||
<h2 className="text-xl font-bold text-zinc-900 dark:text-white">Code Context</h2>
|
||||
<InfoIcon
|
||||
content="The code provided to the LLM for optimization. Read-writable code can be modified, read-only dependencies are for reference only."
|
||||
side="right"
|
||||
|
|
@ -125,7 +125,7 @@ export const CodeContextSection = memo(function CodeContextSection({
|
|||
</div>
|
||||
<div className="flex items-center gap-4">
|
||||
{/* Summary metrics */}
|
||||
<div className="flex items-center gap-4 text-sm text-gray-500 dark:text-gray-400">
|
||||
<div className="flex items-center gap-4 text-sm text-zinc-500 dark:text-zinc-400">
|
||||
<span className="flex items-center gap-1">
|
||||
<Hash className="h-4 w-4" />
|
||||
{metrics.totalTokens.toLocaleString()} tokens
|
||||
|
|
@ -135,32 +135,28 @@ export const CodeContextSection = memo(function CodeContextSection({
|
|||
{metrics.totalFiles} files
|
||||
</span>
|
||||
</div>
|
||||
{isExpanded ? (
|
||||
<ChevronDown className="h-5 w-5 text-gray-400" />
|
||||
) : (
|
||||
<ChevronRight className="h-5 w-5 text-gray-400" />
|
||||
)}
|
||||
<ChevronDown className={`h-4 w-4 text-zinc-400 transition-transform duration-200 ${isExpanded ? '' : '-rotate-90'}`} />
|
||||
</div>
|
||||
</button>
|
||||
|
||||
{/* Expanded content */}
|
||||
{isExpanded && (
|
||||
<div className="px-6 pb-6 pt-0 border-t border-gray-200 dark:border-gray-700 space-y-4">
|
||||
<div className="px-6 pb-6 pt-0 border-t border-zinc-200 dark:border-zinc-800 space-y-4">
|
||||
{/* Function info */}
|
||||
{(functionName || filePath) && (
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4 py-4">
|
||||
{functionName && (
|
||||
<div className="flex flex-col">
|
||||
<span className="text-xs text-gray-500 dark:text-gray-400 mb-1">Function</span>
|
||||
<code className="text-sm font-mono text-gray-900 dark:text-white bg-gray-100 dark:bg-gray-700 px-2 py-1 rounded w-fit">
|
||||
<span className="text-xs text-zinc-500 dark:text-zinc-400 mb-1">Function</span>
|
||||
<code className="text-sm font-mono text-zinc-900 dark:text-white bg-zinc-100 dark:bg-zinc-800 px-2 py-1 rounded-sm w-fit">
|
||||
{functionName}
|
||||
</code>
|
||||
</div>
|
||||
)}
|
||||
{filePath && (
|
||||
<div className="flex flex-col">
|
||||
<span className="text-xs text-gray-500 dark:text-gray-400 mb-1">File</span>
|
||||
<code className="text-sm font-mono text-gray-900 dark:text-white bg-gray-100 dark:bg-gray-700 px-2 py-1 rounded w-fit truncate max-w-full" title={filePath}>
|
||||
<span className="text-xs text-zinc-500 dark:text-zinc-400 mb-1">File</span>
|
||||
<code className="text-sm font-mono text-zinc-900 dark:text-white bg-zinc-100 dark:bg-zinc-800 px-2 py-1 rounded-sm w-fit truncate max-w-full" title={filePath}>
|
||||
{filePath}
|
||||
</code>
|
||||
</div>
|
||||
|
|
@ -170,10 +166,10 @@ export const CodeContextSection = memo(function CodeContextSection({
|
|||
|
||||
{/* Token breakdown - only show when both types exist */}
|
||||
{rwFiles.length > 0 && roFiles.length > 0 && (
|
||||
<div className="p-4 bg-gray-50 dark:bg-gray-900/50 rounded-lg">
|
||||
<div className="p-4 bg-zinc-50 dark:bg-zinc-950 rounded-sm border border-zinc-200 dark:border-zinc-800">
|
||||
<div className="flex items-center gap-2 mb-3">
|
||||
<Hash className="h-4 w-4 text-gray-400" />
|
||||
<span className="text-sm font-medium text-gray-700 dark:text-gray-300">
|
||||
<Hash className="h-4 w-4 text-zinc-400" />
|
||||
<span className="text-sm font-medium text-zinc-700 dark:text-zinc-300">
|
||||
Token Distribution (estimated)
|
||||
</span>
|
||||
</div>
|
||||
|
|
@ -185,13 +181,13 @@ export const CodeContextSection = memo(function CodeContextSection({
|
|||
<div className="flex gap-4 text-xs">
|
||||
<div className="flex items-center gap-1.5">
|
||||
<div className="w-3 h-3 rounded-sm bg-emerald-500" />
|
||||
<span className="text-gray-600 dark:text-gray-400">
|
||||
<span className="text-zinc-600 dark:text-zinc-400">
|
||||
Read-Writable: {metrics.rwTokens.toLocaleString()} ({rwFiles.length} files)
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-1.5">
|
||||
<div className="w-3 h-3 rounded-sm bg-slate-500" />
|
||||
<span className="text-gray-600 dark:text-gray-400">
|
||||
<span className="text-zinc-600 dark:text-zinc-400">
|
||||
Read-Only: {metrics.roTokens.toLocaleString()} ({roFiles.length} files)
|
||||
</span>
|
||||
</div>
|
||||
|
|
@ -256,15 +252,15 @@ function getAccentColorClasses(accentColor: "emerald" | "slate"): { border: stri
|
|||
switch (accentColor) {
|
||||
case "emerald":
|
||||
return {
|
||||
border: "border-emerald-200 dark:border-emerald-800",
|
||||
bg: "bg-emerald-50 dark:bg-emerald-900/20",
|
||||
border: "border-zinc-200 dark:border-zinc-800",
|
||||
bg: "bg-zinc-50 dark:bg-zinc-900",
|
||||
icon: "text-emerald-500",
|
||||
}
|
||||
case "slate":
|
||||
return {
|
||||
border: "border-slate-200 dark:border-slate-700",
|
||||
bg: "bg-slate-50 dark:bg-slate-900/20",
|
||||
icon: "text-slate-500",
|
||||
border: "border-zinc-200 dark:border-zinc-800",
|
||||
bg: "bg-zinc-50 dark:bg-zinc-900",
|
||||
icon: "text-zinc-500",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -285,42 +281,38 @@ const CodeGroupSection = memo(function CodeGroupSection({
|
|||
const { border: borderColor, bg: bgColor, icon: iconColor } = getAccentColorClasses(accentColor)
|
||||
|
||||
return (
|
||||
<div className={`rounded-lg border ${borderColor} overflow-hidden`}>
|
||||
<div className={`rounded-sm border ${borderColor} overflow-hidden`}>
|
||||
<div
|
||||
role="button"
|
||||
tabIndex={0}
|
||||
onClick={onToggle}
|
||||
onKeyDown={e => { if (e.key === "Enter" || e.key === " ") onToggle() }}
|
||||
className={`w-full p-4 flex items-center justify-between hover:bg-opacity-80 transition-colors cursor-pointer ${bgColor}`}
|
||||
className={`w-full p-4 flex items-center justify-between hover:bg-zinc-100 dark:hover:bg-zinc-800 transition-colors cursor-pointer ${bgColor}`}
|
||||
>
|
||||
<div className="flex items-center gap-3">
|
||||
<FileText className={`h-4 w-4 ${iconColor}`} />
|
||||
<div className="text-left">
|
||||
<span className="text-sm font-medium text-gray-900 dark:text-white">{title}</span>
|
||||
<span className="text-xs text-gray-500 dark:text-gray-400 ml-2">{subtitle}</span>
|
||||
<span className="text-sm font-medium text-zinc-900 dark:text-white">{title}</span>
|
||||
<span className="text-xs text-zinc-500 dark:text-zinc-400 ml-2">{subtitle}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-center gap-3">
|
||||
<span className="text-xs text-gray-500 dark:text-gray-400">
|
||||
<span className="text-xs text-zinc-500 dark:text-zinc-400">
|
||||
{tokenCount.toLocaleString()} tokens · {charCount.toLocaleString()} chars · {files.length} files
|
||||
</span>
|
||||
{isExpanded ? (
|
||||
<ChevronDown className="h-4 w-4 text-gray-400" />
|
||||
) : (
|
||||
<ChevronRight className="h-4 w-4 text-gray-400" />
|
||||
)}
|
||||
<ChevronDown className={`h-4 w-4 text-zinc-400 transition-transform duration-200 ${isExpanded ? '' : '-rotate-90'}`} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{isExpanded && (
|
||||
<div className="border-t border-gray-200 dark:border-gray-700 divide-y divide-gray-200 dark:divide-gray-700">
|
||||
<div className="border-t border-zinc-200 dark:border-zinc-800 divide-y divide-zinc-200 dark:divide-zinc-800">
|
||||
{files.map((file, index) => {
|
||||
const fileKey = `${sectionKey}-${index}`
|
||||
const isFileExpanded = expandedFiles.has(fileKey)
|
||||
return (
|
||||
<div key={fileKey}>
|
||||
{/* File header */}
|
||||
<div className="px-4 py-2 flex items-center justify-between bg-gray-100 dark:bg-gray-800">
|
||||
<div className="px-4 py-2 flex items-center justify-between bg-zinc-100 dark:bg-zinc-950">
|
||||
<div
|
||||
role="button"
|
||||
tabIndex={0}
|
||||
|
|
@ -328,21 +320,17 @@ const CodeGroupSection = memo(function CodeGroupSection({
|
|||
onKeyDown={e => { if (e.key === "Enter" || e.key === " ") onToggleFile(fileKey) }}
|
||||
className="flex items-center gap-2 cursor-pointer hover:opacity-80 flex-1"
|
||||
>
|
||||
<FileText className="h-3.5 w-3.5 text-gray-400" />
|
||||
<span className="text-sm font-mono font-medium text-gray-900 dark:text-white">
|
||||
<FileText className="h-3.5 w-3.5 text-zinc-400" />
|
||||
<span className="text-sm font-mono font-medium text-zinc-900 dark:text-white">
|
||||
{file.filename}
|
||||
</span>
|
||||
<span className="text-xs text-gray-500 dark:text-gray-400" title={file.path}>
|
||||
<span className="text-xs text-zinc-500 dark:text-zinc-400" title={file.path}>
|
||||
{file.path !== file.filename && `(${file.path})`}
|
||||
</span>
|
||||
{isFileExpanded ? (
|
||||
<ChevronDown className="h-3.5 w-3.5 text-gray-400" />
|
||||
) : (
|
||||
<ChevronRight className="h-3.5 w-3.5 text-gray-400" />
|
||||
)}
|
||||
<ChevronDown className={`h-3.5 w-3.5 text-zinc-400 transition-transform duration-200 ${isFileExpanded ? '' : '-rotate-90'}`} />
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="text-xs text-gray-500 dark:text-gray-400">
|
||||
<span className="text-xs text-zinc-500 dark:text-zinc-400">
|
||||
{file.code.split("\n").length} lines
|
||||
</span>
|
||||
<CopyButton text={file.code} label={file.filename} size="sm" />
|
||||
|
|
@ -383,7 +371,7 @@ const TokenDistributionBar = memo(function TokenDistributionBar({
|
|||
const roPercent = Math.round((roTokens / totalTokens) * 100)
|
||||
|
||||
return (
|
||||
<div className="flex h-6 rounded-lg overflow-hidden border border-gray-200 dark:border-gray-600 mb-2">
|
||||
<div className="flex h-6 rounded-sm overflow-hidden border border-zinc-200 dark:border-zinc-800 mb-2">
|
||||
<div
|
||||
className="bg-emerald-500 flex items-center justify-center text-white text-xs font-semibold"
|
||||
style={{ width: `${rwPercent}%` }}
|
||||
|
|
|
|||
|
|
@ -9,17 +9,105 @@ const SyntaxHighlighter = dynamic(
|
|||
{
|
||||
ssr: false,
|
||||
loading: () => (
|
||||
<div className="animate-pulse bg-gray-800 rounded p-4 min-h-[100px]">
|
||||
<div className="h-4 bg-gray-700 rounded w-3/4 mb-2" />
|
||||
<div className="h-4 bg-gray-700 rounded w-1/2 mb-2" />
|
||||
<div className="h-4 bg-gray-700 rounded w-2/3" />
|
||||
<div className="animate-pulse bg-zinc-800 rounded p-4 min-h-[100px]">
|
||||
<div className="h-4 bg-zinc-700 rounded w-3/4 mb-2" />
|
||||
<div className="h-4 bg-zinc-700 rounded w-1/2 mb-2" />
|
||||
<div className="h-4 bg-zinc-700 rounded w-2/3" />
|
||||
</div>
|
||||
),
|
||||
}
|
||||
)
|
||||
|
||||
// Import style - this is just JSON data, relatively small
|
||||
import { oneDark } from "react-syntax-highlighter/dist/esm/styles/prism"
|
||||
// Custom zinc-based theme for syntax highlighting
|
||||
export const zincDarkTheme = {
|
||||
// Base colors
|
||||
'code[class*="language-"]': {
|
||||
color: 'rgb(250, 250, 250)', // zinc-50
|
||||
background: 'none',
|
||||
fontFamily: 'var(--font-mono)',
|
||||
fontSize: '1em',
|
||||
textAlign: 'left',
|
||||
whiteSpace: 'pre',
|
||||
wordSpacing: 'normal',
|
||||
wordBreak: 'normal',
|
||||
wordWrap: 'normal',
|
||||
lineHeight: '1.5',
|
||||
tabSize: 4,
|
||||
hyphens: 'none',
|
||||
},
|
||||
'pre[class*="language-"]': {
|
||||
color: 'rgb(250, 250, 250)', // zinc-50
|
||||
background: 'rgb(24, 24, 27)', // zinc-900
|
||||
fontFamily: 'var(--font-mono)',
|
||||
fontSize: '1em',
|
||||
textAlign: 'left',
|
||||
whiteSpace: 'pre',
|
||||
wordSpacing: 'normal',
|
||||
wordBreak: 'normal',
|
||||
wordWrap: 'normal',
|
||||
lineHeight: '1.5',
|
||||
tabSize: 4,
|
||||
hyphens: 'none',
|
||||
padding: '1em',
|
||||
margin: '0',
|
||||
overflow: 'auto',
|
||||
},
|
||||
// Comments
|
||||
comment: {
|
||||
color: 'rgb(113, 113, 122)', // zinc-500
|
||||
fontStyle: 'italic',
|
||||
},
|
||||
prolog: { color: 'rgb(113, 113, 122)' },
|
||||
doctype: { color: 'rgb(113, 113, 122)' },
|
||||
cdata: { color: 'rgb(113, 113, 122)' },
|
||||
// Keywords
|
||||
keyword: { color: 'rgb(96, 165, 250)' }, // blue-400
|
||||
'control-flow': { color: 'rgb(96, 165, 250)' },
|
||||
// Strings
|
||||
string: { color: 'rgb(134, 239, 172)' }, // green-300
|
||||
'attr-value': { color: 'rgb(134, 239, 172)' },
|
||||
// Functions
|
||||
function: { color: 'rgb(253, 224, 71)' }, // yellow-300
|
||||
'class-name': { color: 'rgb(253, 224, 71)' },
|
||||
// Numbers and boolean
|
||||
number: { color: 'rgb(251, 146, 60)' }, // orange-400
|
||||
boolean: { color: 'rgb(251, 146, 60)' },
|
||||
// Operators and punctuation
|
||||
operator: { color: 'rgb(161, 161, 170)' }, // zinc-400
|
||||
punctuation: { color: 'rgb(161, 161, 170)' },
|
||||
// Variables and properties
|
||||
variable: { color: 'rgb(250, 250, 250)' }, // zinc-50
|
||||
property: { color: 'rgb(250, 250, 250)' },
|
||||
// Tags and attributes
|
||||
tag: { color: 'rgb(96, 165, 250)' }, // blue-400
|
||||
'attr-name': { color: 'rgb(250, 250, 250)' },
|
||||
namespace: { opacity: 0.7 },
|
||||
// Other tokens
|
||||
selector: { color: 'rgb(253, 224, 71)' },
|
||||
important: {
|
||||
color: 'rgb(251, 146, 60)',
|
||||
fontWeight: 'bold',
|
||||
},
|
||||
atrule: { color: 'rgb(96, 165, 250)' },
|
||||
builtin: { color: 'rgb(253, 224, 71)' },
|
||||
entity: {
|
||||
color: 'rgb(250, 250, 250)',
|
||||
cursor: 'help',
|
||||
},
|
||||
url: {
|
||||
color: 'rgb(96, 165, 250)',
|
||||
textDecoration: 'underline',
|
||||
},
|
||||
// Inserted and deleted for diffs
|
||||
inserted: {
|
||||
color: 'rgb(134, 239, 172)',
|
||||
background: 'rgba(134, 239, 172, 0.1)',
|
||||
},
|
||||
deleted: {
|
||||
color: 'rgb(248, 113, 113)',
|
||||
background: 'rgba(248, 113, 113, 0.1)',
|
||||
},
|
||||
} as const
|
||||
|
||||
// Shared style constants to avoid recreating objects on each render
|
||||
export const CODE_STYLE = {
|
||||
|
|
@ -27,6 +115,7 @@ export const CODE_STYLE = {
|
|||
padding: "1rem",
|
||||
fontSize: "0.875rem",
|
||||
lineHeight: 1.5,
|
||||
background: 'rgb(24, 24, 27)', // zinc-900 - ensure background consistency
|
||||
} as const
|
||||
|
||||
export const CODE_STYLE_RELAXED = {
|
||||
|
|
@ -34,6 +123,7 @@ export const CODE_STYLE_RELAXED = {
|
|||
padding: "1rem",
|
||||
fontSize: "0.875rem",
|
||||
lineHeight: 1.6,
|
||||
background: 'rgb(24, 24, 27)', // zinc-900 - ensure background consistency
|
||||
} as const
|
||||
|
||||
export const CODE_STYLE_SMALL = {
|
||||
|
|
@ -41,6 +131,7 @@ export const CODE_STYLE_SMALL = {
|
|||
padding: "1rem",
|
||||
fontSize: "0.8125rem",
|
||||
lineHeight: 1.5,
|
||||
background: 'rgb(24, 24, 27)', // zinc-900 - ensure background consistency
|
||||
} as const
|
||||
|
||||
interface CodeHighlighterProps {
|
||||
|
|
@ -59,7 +150,7 @@ export const CodeHighlighter = memo(function CodeHighlighter({
|
|||
return (
|
||||
<SyntaxHighlighter
|
||||
language={language}
|
||||
style={oneDark}
|
||||
style={zincDarkTheme}
|
||||
customStyle={customStyle}
|
||||
showLineNumbers={showLineNumbers}
|
||||
>
|
||||
|
|
|
|||
|
|
@ -6,7 +6,6 @@ import {
|
|||
AlertCircle,
|
||||
AlertTriangle,
|
||||
ChevronDown,
|
||||
ChevronRight,
|
||||
} from "lucide-react"
|
||||
import { CopyButton } from "@/components/observability/copy-button"
|
||||
|
||||
|
|
@ -51,18 +50,18 @@ export const ErrorsSection = memo(function ErrorsSection({ errors }: ErrorsSecti
|
|||
}
|
||||
|
||||
return (
|
||||
<div className="bg-white dark:bg-gray-800 rounded-lg shadow border border-gray-200 dark:border-gray-700">
|
||||
<div className="p-6 border-b border-gray-200 dark:border-gray-700">
|
||||
<div className="bg-white dark:bg-zinc-900 rounded-sm border border-zinc-200 dark:border-zinc-700">
|
||||
<div className="p-6 border-b border-zinc-200 dark:border-zinc-700">
|
||||
<div className="flex items-center gap-3">
|
||||
<AlertCircle className="h-5 w-5 text-red-600 dark:text-red-400" />
|
||||
<h2 className="text-xl font-bold text-red-600 dark:text-red-400">Errors</h2>
|
||||
<span className="bg-red-100 dark:bg-red-900 text-red-800 dark:text-red-200 text-sm px-2.5 py-0.5 rounded font-semibold">
|
||||
<span className="text-red-400 border border-red-600 text-sm px-2.5 py-0.5 rounded-sm font-semibold">
|
||||
{errors.length}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="divide-y divide-gray-200 dark:divide-gray-700">
|
||||
<div className="divide-y divide-zinc-200 dark:divide-zinc-700">
|
||||
{errors.map(error => {
|
||||
const isExpanded = expandedErrors.has(error.id)
|
||||
const isTestFailure = error.error_type === "test_failure"
|
||||
|
|
@ -73,11 +72,11 @@ export const ErrorsSection = memo(function ErrorsSection({ errors }: ErrorsSecti
|
|||
|
||||
let severityColor: string
|
||||
if (error.severity === "critical") {
|
||||
severityColor = "text-red-700 dark:text-red-300 bg-red-100 dark:bg-red-900"
|
||||
severityColor = "text-red-400 border border-red-600 px-1.5 py-0.5"
|
||||
} else if (error.severity === "error") {
|
||||
severityColor = "text-orange-700 dark:text-orange-300 bg-orange-100 dark:bg-orange-900"
|
||||
severityColor = "text-orange-400 border border-orange-600 px-1.5 py-0.5"
|
||||
} else {
|
||||
severityColor = "text-yellow-700 dark:text-yellow-300 bg-yellow-100 dark:bg-yellow-900"
|
||||
severityColor = "text-yellow-400 border border-yellow-600 px-1.5 py-0.5"
|
||||
}
|
||||
|
||||
return (
|
||||
|
|
@ -88,31 +87,28 @@ export const ErrorsSection = memo(function ErrorsSection({ errors }: ErrorsSecti
|
|||
tabIndex={0}
|
||||
onClick={() => toggleError(error.id)}
|
||||
onKeyDown={e => { if (e.key === "Enter" || e.key === " ") toggleError(error.id) }}
|
||||
className="flex items-start gap-3 cursor-pointer hover:opacity-80 flex-1"
|
||||
className="flex items-start gap-3 cursor-pointer hover:opacity-80 flex-1 transition-opacity duration-150"
|
||||
>
|
||||
<SeverityIcon className="h-5 w-5 text-red-600 dark:text-red-400 mt-0.5 flex-shrink-0" />
|
||||
<div className="flex-1 min-w-0">
|
||||
<div className="flex items-center gap-2 mb-1 flex-wrap">
|
||||
<span className="font-semibold text-gray-900 dark:text-white">
|
||||
<span className="font-semibold text-zinc-900 dark:text-white">
|
||||
{error.error_type}
|
||||
</span>
|
||||
<span className={`px-2 py-0.5 text-xs font-semibold rounded ${severityColor}`}>
|
||||
<span className={`text-xs font-semibold rounded-sm ${severityColor}`}>
|
||||
{error.severity}
|
||||
</span>
|
||||
<span className="text-xs text-gray-500 dark:text-gray-400 ml-auto">
|
||||
<span className="text-xs text-zinc-500 dark:text-zinc-400 ml-auto">
|
||||
{new Date(error.created_at).toLocaleString()}
|
||||
</span>
|
||||
</div>
|
||||
<p className="text-sm text-gray-700 dark:text-gray-300 line-clamp-2">
|
||||
<p className="text-sm text-zinc-700 dark:text-zinc-300 line-clamp-2">
|
||||
{error.error_message}
|
||||
</p>
|
||||
</div>
|
||||
{(hasContext || isTestFailure) &&
|
||||
(isExpanded ? (
|
||||
<ChevronDown className="h-5 w-5 text-gray-400" />
|
||||
) : (
|
||||
<ChevronRight className="h-5 w-5 text-gray-400" />
|
||||
))}
|
||||
{(hasContext || isTestFailure) && (
|
||||
<ChevronDown className={`h-4 w-4 text-zinc-400 transition-transform duration-200 ${isExpanded ? '' : '-rotate-90'}`} />
|
||||
)}
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<CopyButton text={error.error_message} label="error message" size="sm" />
|
||||
|
|
@ -121,17 +117,17 @@ export const ErrorsSection = memo(function ErrorsSection({ errors }: ErrorsSecti
|
|||
|
||||
{isExpanded && isTestFailure && error.context && (
|
||||
<div className="px-4 pb-4 ml-8">
|
||||
<div className="p-4 bg-red-50 dark:bg-red-900/20 rounded-lg border border-red-200 dark:border-red-800">
|
||||
<h4 className="text-sm font-semibold text-red-800 dark:text-red-300 mb-3">
|
||||
<div className="p-4 bg-zinc-50 dark:bg-zinc-800 rounded-sm border border-zinc-200 dark:border-zinc-700">
|
||||
<h4 className="text-sm font-semibold text-red-600 dark:text-red-400 mb-3">
|
||||
Test Failure Details
|
||||
</h4>
|
||||
|
||||
{error.context.test_name && (
|
||||
<div className="mb-3">
|
||||
<span className="text-xs font-medium text-gray-600 dark:text-gray-400 uppercase tracking-wide">
|
||||
<span className="text-xs font-medium text-zinc-600 dark:text-zinc-400 uppercase tracking-wide">
|
||||
Test Name
|
||||
</span>
|
||||
<p className="text-sm text-gray-900 dark:text-white font-mono mt-1">
|
||||
<p className="text-sm text-zinc-900 dark:text-white font-mono mt-1">
|
||||
{error.context.test_name}
|
||||
</p>
|
||||
</div>
|
||||
|
|
@ -139,10 +135,10 @@ export const ErrorsSection = memo(function ErrorsSection({ errors }: ErrorsSecti
|
|||
|
||||
{error.context.failure_reason && (
|
||||
<div className="mb-3">
|
||||
<span className="text-xs font-medium text-gray-600 dark:text-gray-400 uppercase tracking-wide">
|
||||
<span className="text-xs font-medium text-zinc-600 dark:text-zinc-400 uppercase tracking-wide">
|
||||
Failure Reason
|
||||
</span>
|
||||
<p className="text-sm text-gray-900 dark:text-white mt-1 whitespace-pre-wrap">
|
||||
<p className="text-sm text-zinc-900 dark:text-white mt-1 whitespace-pre-wrap">
|
||||
{error.context.failure_reason}
|
||||
</p>
|
||||
</div>
|
||||
|
|
@ -150,10 +146,10 @@ export const ErrorsSection = memo(function ErrorsSection({ errors }: ErrorsSecti
|
|||
|
||||
{error.context.expected && (
|
||||
<div className="mb-3">
|
||||
<span className="text-xs font-medium text-gray-600 dark:text-gray-400 uppercase tracking-wide">
|
||||
<span className="text-xs font-medium text-zinc-600 dark:text-zinc-400 uppercase tracking-wide">
|
||||
Expected
|
||||
</span>
|
||||
<pre className="text-xs text-gray-900 dark:text-white mt-1 bg-white dark:bg-gray-800 p-2 rounded overflow-x-auto font-mono">
|
||||
<pre className="text-xs text-zinc-900 dark:text-white mt-1 bg-white dark:bg-zinc-900 p-2 rounded-sm overflow-x-auto font-mono">
|
||||
{String(error.context.expected)}
|
||||
</pre>
|
||||
</div>
|
||||
|
|
@ -161,10 +157,10 @@ export const ErrorsSection = memo(function ErrorsSection({ errors }: ErrorsSecti
|
|||
|
||||
{error.context.actual && (
|
||||
<div className="mb-3">
|
||||
<span className="text-xs font-medium text-gray-600 dark:text-gray-400 uppercase tracking-wide">
|
||||
<span className="text-xs font-medium text-zinc-600 dark:text-zinc-400 uppercase tracking-wide">
|
||||
Actual
|
||||
</span>
|
||||
<pre className="text-xs text-gray-900 dark:text-white mt-1 bg-white dark:bg-gray-800 p-2 rounded overflow-x-auto font-mono">
|
||||
<pre className="text-xs text-zinc-900 dark:text-white mt-1 bg-white dark:bg-zinc-900 p-2 rounded-sm overflow-x-auto font-mono">
|
||||
{String(error.context.actual)}
|
||||
</pre>
|
||||
</div>
|
||||
|
|
@ -172,10 +168,10 @@ export const ErrorsSection = memo(function ErrorsSection({ errors }: ErrorsSecti
|
|||
|
||||
{error.context.test_output && (
|
||||
<div>
|
||||
<span className="text-xs font-medium text-gray-600 dark:text-gray-400 uppercase tracking-wide">
|
||||
<span className="text-xs font-medium text-zinc-600 dark:text-zinc-400 uppercase tracking-wide">
|
||||
Test Output
|
||||
</span>
|
||||
<pre className="text-xs text-gray-900 dark:text-white mt-1 bg-white dark:bg-gray-800 p-2 rounded overflow-x-auto whitespace-pre-wrap font-mono max-h-48">
|
||||
<pre className="text-xs text-zinc-900 dark:text-white mt-1 bg-white dark:bg-zinc-900 p-2 rounded-sm overflow-x-auto whitespace-pre-wrap font-mono max-h-48">
|
||||
{String(error.context.test_output)}
|
||||
</pre>
|
||||
</div>
|
||||
|
|
@ -186,11 +182,11 @@ export const ErrorsSection = memo(function ErrorsSection({ errors }: ErrorsSecti
|
|||
|
||||
{isExpanded && !isTestFailure && hasContext && (
|
||||
<div className="px-4 pb-4 ml-8">
|
||||
<div className="p-4 bg-gray-50 dark:bg-gray-900 rounded-lg border border-gray-200 dark:border-gray-700">
|
||||
<span className="text-xs font-medium text-gray-600 dark:text-gray-400 uppercase tracking-wide">
|
||||
<div className="p-4 bg-zinc-50 dark:bg-zinc-900 rounded-sm border border-zinc-200 dark:border-zinc-700">
|
||||
<span className="text-xs font-medium text-zinc-600 dark:text-zinc-400 uppercase tracking-wide">
|
||||
Context
|
||||
</span>
|
||||
<pre className="text-xs text-gray-900 dark:text-white mt-2 overflow-auto whitespace-pre-wrap font-mono max-h-48">
|
||||
<pre className="text-xs text-zinc-900 dark:text-white mt-2 overflow-auto whitespace-pre-wrap font-mono max-h-48">
|
||||
{JSON.stringify(error.context, null, 2)}
|
||||
</pre>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
"use client"
|
||||
|
||||
import { memo, useMemo, useState } from "react"
|
||||
import { Code, FileText, ChevronDown, ChevronRight } from "lucide-react"
|
||||
import { Code, FileText, ChevronDown } from "lucide-react"
|
||||
import { CodeHighlighter, CODE_STYLE_RELAXED } from "./code-highlighter"
|
||||
import { CopyButton } from "@/components/observability/copy-button"
|
||||
|
||||
|
|
@ -68,9 +68,9 @@ export const FunctionToOptimizeSection = memo(function FunctionToOptimizeSection
|
|||
}
|
||||
|
||||
return (
|
||||
<div className="bg-white dark:bg-gray-800 rounded-lg shadow border border-gray-200 dark:border-gray-700 overflow-hidden">
|
||||
<div className="bg-white dark:bg-zinc-900 rounded-sm border border-zinc-200 dark:border-zinc-800 overflow-hidden">
|
||||
{/* Header */}
|
||||
<div className="w-full p-4 bg-gradient-to-r from-blue-50 to-indigo-50 dark:from-blue-900/20 dark:to-indigo-900/20 flex items-center justify-between">
|
||||
<div className="w-full p-4 bg-zinc-950 flex items-center justify-between">
|
||||
<div
|
||||
role="button"
|
||||
tabIndex={0}
|
||||
|
|
@ -78,26 +78,22 @@ export const FunctionToOptimizeSection = memo(function FunctionToOptimizeSection
|
|||
onKeyDown={e => { if (e.key === "Enter" || e.key === " ") setIsExpanded(!isExpanded) }}
|
||||
className="flex items-center gap-3 cursor-pointer hover:opacity-80 flex-1"
|
||||
>
|
||||
<div className="p-2 bg-blue-100 dark:bg-blue-900/50 rounded-lg">
|
||||
<Code className="h-5 w-5 text-blue-600 dark:text-blue-400" />
|
||||
<div className="p-2 bg-zinc-800 rounded-sm">
|
||||
<Code className="h-4 w-4 text-zinc-400" />
|
||||
</div>
|
||||
<div className="text-left">
|
||||
<h2 className="text-lg font-semibold text-gray-900 dark:text-white">
|
||||
<h2 className="text-lg font-semibold text-zinc-50">
|
||||
Function to Optimize
|
||||
</h2>
|
||||
<div className="flex items-center gap-2 mt-1">
|
||||
{functionName && (
|
||||
<code className="text-sm font-mono text-blue-600 dark:text-blue-400 bg-blue-100 dark:bg-blue-900/50 px-2 py-0.5 rounded">
|
||||
<code className="text-sm font-mono text-zinc-300 bg-zinc-800 px-2 py-0.5 rounded-sm">
|
||||
{functionName}
|
||||
</code>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
{isExpanded ? (
|
||||
<ChevronDown className="h-5 w-5 text-gray-400" />
|
||||
) : (
|
||||
<ChevronRight className="h-5 w-5 text-gray-400" />
|
||||
)}
|
||||
<ChevronDown className={`h-4 w-4 text-zinc-500 transition-transform duration-200 ${isExpanded ? '' : '-rotate-90'}`} />
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<CopyButton text={functionFile.code} label="function code" />
|
||||
|
|
@ -107,17 +103,17 @@ export const FunctionToOptimizeSection = memo(function FunctionToOptimizeSection
|
|||
{isExpanded && (
|
||||
<>
|
||||
{/* File info */}
|
||||
<div className="px-4 py-2 bg-gray-50 dark:bg-gray-900/50 border-t border-b border-gray-200 dark:border-gray-700 flex items-center gap-2">
|
||||
<FileText className="h-4 w-4 text-gray-400" />
|
||||
<span className="text-sm font-mono font-medium text-gray-700 dark:text-gray-300">
|
||||
<div className="px-4 py-2 bg-zinc-900 border-t border-b border-zinc-800 flex items-center gap-2">
|
||||
<FileText className="h-4 w-4 text-zinc-500" />
|
||||
<span className="text-sm font-mono font-medium text-zinc-300">
|
||||
{functionFile.filename}
|
||||
</span>
|
||||
{functionFile.path !== functionFile.filename && (
|
||||
<span className="text-xs text-gray-500 dark:text-gray-400">
|
||||
<span className="text-xs font-mono text-zinc-500">
|
||||
({functionFile.path})
|
||||
</span>
|
||||
)}
|
||||
<span className="text-xs text-gray-500 dark:text-gray-400 ml-auto">
|
||||
<span className="text-xs text-zinc-500 ml-auto">
|
||||
{functionFile.code.split("\n").length} lines
|
||||
</span>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@
|
|||
import { useState, useMemo, useCallback, memo, useEffect } from "react"
|
||||
import {
|
||||
ChevronDown,
|
||||
ChevronRight,
|
||||
Hash,
|
||||
FileText,
|
||||
Code,
|
||||
|
|
@ -106,38 +105,34 @@ const CodeFileDisplay = memo(function CodeFileDisplay({
|
|||
return (
|
||||
<div>
|
||||
{/* File header */}
|
||||
<div className="px-4 py-2 flex items-center justify-between bg-gray-100 dark:bg-gray-800">
|
||||
<div className="px-4 py-2 flex items-center justify-between bg-zinc-100 dark:bg-zinc-800">
|
||||
<div
|
||||
role="button"
|
||||
tabIndex={0}
|
||||
onClick={onToggle}
|
||||
onKeyDown={e => { if (e.key === "Enter" || e.key === " ") onToggle() }}
|
||||
className="flex items-center gap-2 cursor-pointer hover:opacity-80 flex-1"
|
||||
className="flex items-center gap-2 cursor-pointer hover:opacity-80 flex-1 transition-opacity duration-150"
|
||||
>
|
||||
<FileText className="h-3.5 w-3.5 text-gray-400" />
|
||||
<FileText className="h-3.5 w-3.5 text-zinc-400" />
|
||||
{parsed.filename ? (
|
||||
<>
|
||||
<span className="text-sm font-mono font-medium text-gray-900 dark:text-white">
|
||||
<span className="text-sm font-mono font-medium text-zinc-900 dark:text-white">
|
||||
{parsed.filename}
|
||||
</span>
|
||||
{parsed.path && parsed.path !== parsed.filename && (
|
||||
<span className="text-xs text-gray-500 dark:text-gray-400" title={parsed.path}>
|
||||
<span className="text-xs text-zinc-500 dark:text-zinc-400" title={parsed.path}>
|
||||
({parsed.path})
|
||||
</span>
|
||||
)}
|
||||
</>
|
||||
) : (
|
||||
<span className="text-sm font-medium text-gray-700 dark:text-gray-300">
|
||||
<span className="text-sm font-medium text-zinc-700 dark:text-zinc-300">
|
||||
{label || "Code"}
|
||||
</span>
|
||||
)}
|
||||
{isExpanded ? (
|
||||
<ChevronDown className="h-3.5 w-3.5 text-gray-400" />
|
||||
) : (
|
||||
<ChevronRight className="h-3.5 w-3.5 text-gray-400" />
|
||||
)}
|
||||
<ChevronDown className={`h-3.5 w-3.5 text-zinc-400 transition-transform duration-200 ${isExpanded ? '' : '-rotate-90'}`} />
|
||||
</div>
|
||||
<span className="text-xs text-gray-500 dark:text-gray-400">
|
||||
<span className="text-xs text-zinc-500 dark:text-zinc-400">
|
||||
{parsed.code.split("\n").length} lines
|
||||
</span>
|
||||
</div>
|
||||
|
|
@ -161,7 +156,7 @@ const DiffView = memo(function DiffView({ diff }: { diff: string }) {
|
|||
const lines = diff.split("\n")
|
||||
|
||||
return (
|
||||
<div className="font-mono text-sm bg-gray-900 overflow-x-auto">
|
||||
<div className="font-mono text-sm bg-zinc-900 overflow-x-auto">
|
||||
{lines.map((line, index) => {
|
||||
const isAddition = line.startsWith("+")
|
||||
const isDeletion = line.startsWith("-")
|
||||
|
|
@ -181,7 +176,7 @@ const DiffView = memo(function DiffView({ diff }: { diff: string }) {
|
|||
|
||||
// Determine line styling
|
||||
let bgClass = ""
|
||||
let textClass = "text-gray-300"
|
||||
let textClass = "text-zinc-300"
|
||||
let lineContent = line
|
||||
let indicator: React.ReactNode = null
|
||||
let borderClass = "border-transparent"
|
||||
|
|
@ -310,7 +305,7 @@ const CandidateCodeDisplay = memo(function CandidateCodeDisplay({
|
|||
<div>
|
||||
{/* File header with view toggle */}
|
||||
<div
|
||||
className="px-4 py-2 flex items-center justify-between bg-gray-100 dark:bg-gray-800"
|
||||
className="px-4 py-2 flex items-center justify-between bg-zinc-100 dark:bg-zinc-800"
|
||||
>
|
||||
<div
|
||||
role="button"
|
||||
|
|
@ -319,39 +314,35 @@ const CandidateCodeDisplay = memo(function CandidateCodeDisplay({
|
|||
onKeyDown={e => { if (e.key === "Enter" || e.key === " ") onToggle() }}
|
||||
className="flex items-center gap-2 cursor-pointer hover:opacity-80"
|
||||
>
|
||||
<FileText className="h-3.5 w-3.5 text-gray-400" />
|
||||
<FileText className="h-3.5 w-3.5 text-zinc-400" />
|
||||
{parsedCandidate.filename ? (
|
||||
<>
|
||||
<span className="text-sm font-mono font-medium text-gray-900 dark:text-white">
|
||||
<span className="text-sm font-mono font-medium text-zinc-900 dark:text-white">
|
||||
{parsedCandidate.filename}
|
||||
</span>
|
||||
{parsedCandidate.path && parsedCandidate.path !== parsedCandidate.filename && (
|
||||
<span className="text-xs text-gray-500 dark:text-gray-400" title={parsedCandidate.path}>
|
||||
<span className="text-xs text-zinc-500 dark:text-zinc-400" title={parsedCandidate.path}>
|
||||
({parsedCandidate.path})
|
||||
</span>
|
||||
)}
|
||||
</>
|
||||
) : (
|
||||
<span className="text-sm font-medium text-gray-700 dark:text-gray-300">
|
||||
<span className="text-sm font-medium text-zinc-700 dark:text-zinc-300">
|
||||
Optimized Code
|
||||
</span>
|
||||
)}
|
||||
{isExpanded ? (
|
||||
<ChevronDown className="h-3.5 w-3.5 text-gray-400" />
|
||||
) : (
|
||||
<ChevronRight className="h-3.5 w-3.5 text-gray-400" />
|
||||
)}
|
||||
<ChevronDown className={`h-3.5 w-3.5 text-zinc-400 transition-transform duration-200 ${isExpanded ? '' : '-rotate-90'}`} />
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
{/* View toggle */}
|
||||
{hasDiff && (
|
||||
<div className="flex items-center bg-gray-200 dark:bg-gray-700 rounded-md p-0.5">
|
||||
<div className="flex items-center bg-zinc-200 dark:bg-zinc-800 rounded-sm p-0.5">
|
||||
<button
|
||||
onClick={() => setViewMode("diff")}
|
||||
className={`px-2 py-1 text-xs font-medium rounded transition-colors flex items-center gap-1 ${
|
||||
className={`px-2 py-1 text-xs font-medium rounded transition-colors duration-150 flex items-center gap-1 ${
|
||||
viewMode === "diff"
|
||||
? "bg-white dark:bg-gray-600 text-gray-900 dark:text-white shadow-sm"
|
||||
: "text-gray-600 dark:text-gray-400 hover:text-gray-900 dark:hover:text-white"
|
||||
? "bg-white dark:bg-zinc-700 text-zinc-900 dark:text-white border border-zinc-400 dark:border-zinc-600"
|
||||
: "text-zinc-600 dark:text-zinc-400 hover:text-zinc-900 dark:hover:text-white hover:bg-zinc-100 dark:hover:bg-zinc-800"
|
||||
}`}
|
||||
>
|
||||
<GitCompare className="h-3 w-3" />
|
||||
|
|
@ -359,10 +350,10 @@ const CandidateCodeDisplay = memo(function CandidateCodeDisplay({
|
|||
</button>
|
||||
<button
|
||||
onClick={() => setViewMode("code")}
|
||||
className={`px-2 py-1 text-xs font-medium rounded transition-colors flex items-center gap-1 ${
|
||||
className={`px-2 py-1 text-xs font-medium rounded transition-colors duration-150 flex items-center gap-1 ${
|
||||
viewMode === "code"
|
||||
? "bg-white dark:bg-gray-600 text-gray-900 dark:text-white shadow-sm"
|
||||
: "text-gray-600 dark:text-gray-400 hover:text-gray-900 dark:hover:text-white"
|
||||
? "bg-white dark:bg-zinc-700 text-zinc-900 dark:text-white border border-zinc-400 dark:border-zinc-600"
|
||||
: "text-zinc-600 dark:text-zinc-400 hover:text-zinc-900 dark:hover:text-white hover:bg-zinc-100 dark:hover:bg-zinc-800"
|
||||
}`}
|
||||
>
|
||||
<Code className="h-3 w-3" />
|
||||
|
|
@ -370,7 +361,7 @@ const CandidateCodeDisplay = memo(function CandidateCodeDisplay({
|
|||
</button>
|
||||
</div>
|
||||
)}
|
||||
<span className="text-xs text-gray-500 dark:text-gray-400">
|
||||
<span className="text-xs text-zinc-500 dark:text-zinc-400">
|
||||
{parsedCandidate.code.split("\n").length} lines
|
||||
</span>
|
||||
</div>
|
||||
|
|
@ -387,14 +378,14 @@ const CandidateCodeDisplay = memo(function CandidateCodeDisplay({
|
|||
/>
|
||||
) : diffLoading ? (
|
||||
<div className="p-4 animate-pulse">
|
||||
<div className="h-4 bg-gray-700 rounded w-3/4 mb-2" />
|
||||
<div className="h-4 bg-gray-700 rounded w-1/2 mb-2" />
|
||||
<div className="h-4 bg-gray-700 rounded w-2/3" />
|
||||
<div className="h-4 bg-zinc-700 rounded w-3/4 mb-2" />
|
||||
<div className="h-4 bg-zinc-700 rounded w-1/2 mb-2" />
|
||||
<div className="h-4 bg-zinc-700 rounded w-2/3" />
|
||||
</div>
|
||||
) : unifiedDiff ? (
|
||||
<DiffView diff={unifiedDiff} />
|
||||
) : (
|
||||
<div className="p-4 text-sm text-gray-500 dark:text-gray-400">
|
||||
<div className="p-4 text-sm text-zinc-500 dark:text-zinc-400">
|
||||
No original code available for comparison
|
||||
</div>
|
||||
)}
|
||||
|
|
@ -494,7 +485,7 @@ function getModelColorClasses(modelName: string): string {
|
|||
return "bg-green-100 dark:bg-green-900 text-green-800 dark:text-green-200"
|
||||
}
|
||||
|
||||
return "bg-gray-100 dark:bg-gray-700 text-gray-800 dark:text-gray-200"
|
||||
return "bg-zinc-100 dark:bg-zinc-700 text-zinc-800 dark:text-zinc-200"
|
||||
}
|
||||
|
||||
/** Reusable debug dialog for LLM call details */
|
||||
|
|
@ -535,7 +526,7 @@ const PromptContent = memo(function PromptContent({ content }: { content: string
|
|||
|
||||
if (parts.length === 0) {
|
||||
return (
|
||||
<pre className="text-sm whitespace-pre-wrap break-words text-gray-800 dark:text-gray-200 leading-relaxed">
|
||||
<pre className="text-sm whitespace-pre-wrap break-words text-zinc-800 dark:text-zinc-200 leading-relaxed">
|
||||
{content}
|
||||
</pre>
|
||||
)
|
||||
|
|
@ -545,7 +536,7 @@ const PromptContent = memo(function PromptContent({ content }: { content: string
|
|||
<div className="space-y-3">
|
||||
{parts.map((part, i) =>
|
||||
part.type === "code" ? (
|
||||
<div key={i} className="rounded-lg overflow-hidden border border-gray-700">
|
||||
<div key={i} className="rounded-sm overflow-hidden border border-zinc-700">
|
||||
<CodeHighlighter
|
||||
language={part.language || "python"}
|
||||
code={part.content}
|
||||
|
|
@ -553,7 +544,7 @@ const PromptContent = memo(function PromptContent({ content }: { content: string
|
|||
/>
|
||||
</div>
|
||||
) : (
|
||||
<pre key={i} className="text-sm whitespace-pre-wrap break-words text-gray-800 dark:text-gray-200 leading-relaxed">
|
||||
<pre key={i} className="text-sm whitespace-pre-wrap break-words text-zinc-800 dark:text-zinc-200 leading-relaxed">
|
||||
{part.content}
|
||||
</pre>
|
||||
)
|
||||
|
|
@ -565,7 +556,7 @@ const PromptContent = memo(function PromptContent({ content }: { content: string
|
|||
const LLMCallDebugDialog = memo(function LLMCallDebugDialog({
|
||||
call,
|
||||
callType,
|
||||
buttonClassName = "text-gray-600 dark:text-gray-400 hover:bg-gray-100 dark:hover:bg-gray-700",
|
||||
buttonClassName = "text-zinc-600 dark:text-zinc-400 hover:bg-zinc-100 dark:hover:bg-zinc-700",
|
||||
}: LLMCallDebugDialogProps) {
|
||||
const [open, setOpen] = useState(false)
|
||||
const [activeTab, setActiveTab] = useState<"user" | "system">("user")
|
||||
|
|
@ -586,7 +577,7 @@ const LLMCallDebugDialog = memo(function LLMCallDebugDialog({
|
|||
<Dialog open={open} onOpenChange={setOpen}>
|
||||
<DialogTrigger asChild>
|
||||
<button
|
||||
className={`flex items-center gap-1.5 px-2 py-1 text-xs rounded transition-colors ${buttonClassName}`}
|
||||
className={`flex items-center gap-1.5 px-2 py-1 text-xs rounded transition-colors duration-150 ${buttonClassName}`}
|
||||
title="View LLM call details"
|
||||
>
|
||||
<Bug className="h-3 w-3" />
|
||||
|
|
@ -594,29 +585,29 @@ const LLMCallDebugDialog = memo(function LLMCallDebugDialog({
|
|||
</button>
|
||||
</DialogTrigger>
|
||||
<DialogContent className="w-[95vw] max-w-[95vw] h-[90vh] max-h-[90vh] flex flex-col overflow-hidden">
|
||||
<DialogHeader className="flex-shrink-0 pb-3 border-b border-gray-200 dark:border-gray-700">
|
||||
<DialogHeader className="flex-shrink-0 pb-3 border-b border-zinc-200 dark:border-zinc-700">
|
||||
<DialogTitle className="flex items-center gap-2">
|
||||
<Bug className="h-4 w-4" />
|
||||
<span>LLM Call Details</span>
|
||||
</DialogTitle>
|
||||
{/* Metadata row */}
|
||||
<div className="flex items-center gap-3 mt-2 text-sm flex-wrap">
|
||||
<span className="font-medium text-gray-900 dark:text-white">{callType}</span>
|
||||
<span className="text-gray-400">·</span>
|
||||
<span className="font-medium text-zinc-900 dark:text-white">{callType}</span>
|
||||
<span className="text-zinc-400">·</span>
|
||||
{call.model_name && (
|
||||
<span className={`${getModelColorClasses(call.model_name)} text-xs px-2 py-0.5 rounded`}>
|
||||
{call.model_name}
|
||||
</span>
|
||||
)}
|
||||
<span className="text-gray-400">·</span>
|
||||
<span className="text-gray-600 dark:text-gray-400">{((call.latency_ms || 0) / 1000).toFixed(2)}s</span>
|
||||
<span className="text-gray-400">·</span>
|
||||
<span className="text-gray-600 dark:text-gray-400">${(call.llm_cost || 0).toFixed(4)}</span>
|
||||
<span className="text-gray-400">·</span>
|
||||
<span className="text-gray-600 dark:text-gray-400">{call.total_tokens?.toLocaleString() ?? "?"} tokens</span>
|
||||
<span className="text-zinc-400">·</span>
|
||||
<span className="text-zinc-600 dark:text-zinc-400">{((call.latency_ms || 0) / 1000).toFixed(2)}s</span>
|
||||
<span className="text-zinc-400">·</span>
|
||||
<span className="text-zinc-600 dark:text-zinc-400">${(call.llm_cost || 0).toFixed(4)}</span>
|
||||
<span className="text-zinc-400">·</span>
|
||||
<span className="text-zinc-600 dark:text-zinc-400">{call.total_tokens?.toLocaleString() ?? "?"} tokens</span>
|
||||
{call.prompt_tokens && call.completion_tokens && (
|
||||
<>
|
||||
<span className="text-gray-400">·</span>
|
||||
<span className="text-zinc-400">·</span>
|
||||
<span className="text-blue-600 dark:text-blue-400 text-xs">
|
||||
{call.prompt_tokens.toLocaleString()} in
|
||||
</span>
|
||||
|
|
@ -627,7 +618,7 @@ const LLMCallDebugDialog = memo(function LLMCallDebugDialog({
|
|||
)}
|
||||
{call.status && (
|
||||
<>
|
||||
<span className="text-gray-400">·</span>
|
||||
<span className="text-zinc-400">·</span>
|
||||
<span className={`text-xs px-1.5 py-0.5 rounded ${
|
||||
call.status === "success" ? "bg-green-100 dark:bg-green-900/50 text-green-700 dark:text-green-300" :
|
||||
call.status === "failed" ? "bg-red-100 dark:bg-red-900/50 text-red-700 dark:text-red-300" :
|
||||
|
|
@ -644,39 +635,39 @@ const LLMCallDebugDialog = memo(function LLMCallDebugDialog({
|
|||
<TabsList className="flex-shrink-0 w-fit mx-auto">
|
||||
<TabsTrigger value="user">
|
||||
User Prompt
|
||||
<span className="ml-1.5 text-xs text-gray-400">
|
||||
<span className="ml-1.5 text-xs text-zinc-400">
|
||||
({(call.user_prompt?.length || 0).toLocaleString()} chars)
|
||||
</span>
|
||||
</TabsTrigger>
|
||||
<TabsTrigger value="system">
|
||||
System Prompt
|
||||
<span className="ml-1.5 text-xs text-gray-400">
|
||||
<span className="ml-1.5 text-xs text-zinc-400">
|
||||
({(call.system_prompt?.length || 0).toLocaleString()} chars)
|
||||
</span>
|
||||
</TabsTrigger>
|
||||
</TabsList>
|
||||
|
||||
{/* Only render active tab content to avoid parsing/rendering both prompts */}
|
||||
<div className="flex-1 overflow-y-auto mt-3 p-4 bg-white dark:bg-gray-900 rounded-lg border border-gray-200 dark:border-gray-700">
|
||||
<div className="flex-1 overflow-y-auto mt-3 p-4 bg-white dark:bg-zinc-900 rounded-sm border border-zinc-200 dark:border-zinc-700">
|
||||
{!contentReady ? (
|
||||
<div className="animate-pulse space-y-3">
|
||||
<div className="h-4 bg-gray-200 dark:bg-gray-700 rounded w-3/4" />
|
||||
<div className="h-4 bg-gray-200 dark:bg-gray-700 rounded w-1/2" />
|
||||
<div className="h-4 bg-gray-200 dark:bg-gray-700 rounded w-2/3" />
|
||||
<div className="h-4 bg-zinc-200 dark:bg-zinc-700 rounded w-3/4" />
|
||||
<div className="h-4 bg-zinc-200 dark:bg-zinc-700 rounded w-1/2" />
|
||||
<div className="h-4 bg-zinc-200 dark:bg-zinc-700 rounded w-2/3" />
|
||||
</div>
|
||||
) : activeTab === "user" ? (
|
||||
call.user_prompt ? (
|
||||
<PromptContent content={call.user_prompt} />
|
||||
) : (
|
||||
<span className="text-gray-400">No user prompt</span>
|
||||
<span className="text-zinc-400">No user prompt</span>
|
||||
)
|
||||
) : (
|
||||
call.system_prompt ? (
|
||||
<pre className="text-sm whitespace-pre-wrap break-words text-gray-800 dark:text-gray-200 leading-relaxed font-mono">
|
||||
<pre className="text-sm whitespace-pre-wrap break-words text-zinc-800 dark:text-zinc-200 leading-relaxed font-mono">
|
||||
{call.system_prompt}
|
||||
</pre>
|
||||
) : (
|
||||
<span className="text-gray-400">No system prompt</span>
|
||||
<span className="text-zinc-400">No system prompt</span>
|
||||
)
|
||||
)}
|
||||
</div>
|
||||
|
|
@ -806,13 +797,13 @@ const CallItem = memo(function CallItem({
|
|||
}
|
||||
}
|
||||
return {
|
||||
border: "border-gray-200 dark:border-gray-700",
|
||||
border: "border-zinc-200 dark:border-zinc-700",
|
||||
bg: "",
|
||||
headerBg: "bg-gray-50 dark:bg-gray-800",
|
||||
headerBorder: "border-gray-200 dark:border-gray-700",
|
||||
text: "text-gray-700 dark:text-gray-300",
|
||||
icon: "text-gray-500",
|
||||
debugBtn: "text-gray-600 dark:text-gray-400 hover:bg-gray-100 dark:hover:bg-gray-700",
|
||||
headerBg: "bg-zinc-50 dark:bg-zinc-800",
|
||||
headerBorder: "border-zinc-200 dark:border-zinc-700",
|
||||
text: "text-zinc-700 dark:text-zinc-300",
|
||||
icon: "text-zinc-500",
|
||||
debugBtn: "text-zinc-600 dark:text-zinc-400 hover:bg-zinc-100 dark:hover:bg-zinc-700",
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -820,7 +811,7 @@ const CallItem = memo(function CallItem({
|
|||
const title = getTitle()
|
||||
|
||||
return (
|
||||
<div className={`rounded-lg border overflow-hidden ${theme.border} ${theme.bg}`}>
|
||||
<div className={`rounded-sm border overflow-hidden ${theme.border} ${theme.bg}`}>
|
||||
{/* Header */}
|
||||
<div className={`p-3 border-b ${theme.headerBg} ${theme.headerBorder}`}>
|
||||
<div className="flex items-center justify-between">
|
||||
|
|
@ -850,15 +841,15 @@ const CallItem = memo(function CallItem({
|
|||
</div>
|
||||
{/* Explanation text */}
|
||||
{candidate && !refinementCandidate && candidate.explanation && (
|
||||
<p className="mt-2 text-sm text-gray-700 dark:text-gray-300 whitespace-pre-wrap leading-relaxed">
|
||||
<p className="mt-2 text-sm text-zinc-700 dark:text-zinc-300 whitespace-pre-wrap leading-relaxed">
|
||||
{candidate.explanation}
|
||||
</p>
|
||||
)}
|
||||
{refinementCandidate && refinementCandidate.explanation && (
|
||||
<p className="mt-2 text-sm text-gray-700 dark:text-gray-300 whitespace-pre-wrap leading-relaxed">
|
||||
<p className="mt-2 text-sm text-zinc-700 dark:text-zinc-300 whitespace-pre-wrap leading-relaxed">
|
||||
{refinementCandidate.explanation}
|
||||
{parentCandidate && (
|
||||
<span className="ml-2 bg-gray-100 dark:bg-gray-700 text-gray-700 dark:text-gray-300 text-xs px-2 py-0.5 rounded">
|
||||
<span className="ml-2 bg-zinc-100 dark:bg-zinc-700 text-zinc-700 dark:text-zinc-300 text-xs px-2 py-0.5 rounded">
|
||||
from Candidate #{parentCandidate.index}
|
||||
</span>
|
||||
)}
|
||||
|
|
@ -1106,8 +1097,8 @@ export const LLMCallsTimeline = memo(function LLMCallsTimeline({
|
|||
}, [])
|
||||
|
||||
return (
|
||||
<div className="bg-white dark:bg-gray-800 rounded-lg shadow border border-gray-200 dark:border-gray-700">
|
||||
<div className="divide-y divide-gray-200 dark:divide-gray-700">
|
||||
<div className="bg-white dark:bg-zinc-800 rounded-sm border border-zinc-200 dark:border-zinc-700">
|
||||
<div className="divide-y divide-zinc-200 dark:divide-zinc-700">
|
||||
{orderedTypes.map(callType => {
|
||||
// Skip ranking calls - they're shown in the Ranking Explanation section
|
||||
if (callType === "ranking") return null
|
||||
|
|
@ -1117,7 +1108,7 @@ export const LLMCallsTimeline = memo(function LLMCallsTimeline({
|
|||
return (
|
||||
<div key={callType} className="p-4">
|
||||
{/* Call Type Header */}
|
||||
<div className="font-semibold text-gray-900 dark:text-white mb-3">
|
||||
<div className="font-semibold text-zinc-900 dark:text-white mb-3">
|
||||
{callType === "optimization" ? "Standard Optimization Candidates" :
|
||||
callType === "line_profiler" ? "Line Profiler-Guided Optimization Candidates" : callType}
|
||||
</div>
|
||||
|
|
@ -1149,11 +1140,11 @@ export const LLMCallsTimeline = memo(function LLMCallsTimeline({
|
|||
{/* Divider between optimization items */}
|
||||
{callType === "optimization" && typeIndex > 0 && (
|
||||
<div className="flex items-center gap-3 mb-4">
|
||||
<div className="flex-1 h-px bg-gray-300 dark:bg-gray-600" />
|
||||
<span className="text-xs text-gray-400 dark:text-gray-500 font-medium">
|
||||
<div className="flex-1 h-px bg-zinc-300 dark:bg-zinc-600" />
|
||||
<span className="text-xs text-zinc-400 dark:text-zinc-500 font-medium">
|
||||
Candidate {typeIndex + 1}
|
||||
</span>
|
||||
<div className="flex-1 h-px bg-gray-300 dark:bg-gray-600" />
|
||||
<div className="flex-1 h-px bg-zinc-300 dark:bg-zinc-600" />
|
||||
</div>
|
||||
)}
|
||||
<CallItem
|
||||
|
|
@ -1185,7 +1176,7 @@ export const LLMCallsTimeline = memo(function LLMCallsTimeline({
|
|||
|
||||
{/* Ranking Explanation */}
|
||||
{rankingExplanation && (
|
||||
<div className="mt-6 rounded-lg border border-indigo-200 dark:border-indigo-800 overflow-hidden">
|
||||
<div className="mt-6 rounded-sm border border-indigo-200 dark:border-indigo-800 overflow-hidden">
|
||||
<div className="p-4 bg-indigo-50 dark:bg-indigo-900/20 border-b border-indigo-200 dark:border-indigo-800">
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-center gap-2">
|
||||
|
|
@ -1199,7 +1190,7 @@ export const LLMCallsTimeline = memo(function LLMCallsTimeline({
|
|||
<Dialog>
|
||||
<DialogTrigger asChild>
|
||||
<button
|
||||
className="flex items-center gap-1.5 px-2 py-1 text-xs text-indigo-600 dark:text-indigo-400 hover:bg-indigo-100 dark:hover:bg-indigo-900/40 rounded transition-colors"
|
||||
className="flex items-center gap-1.5 px-2 py-1 text-xs text-indigo-600 dark:text-indigo-400 hover:bg-indigo-100 dark:hover:bg-indigo-900/40 rounded transition-colors duration-150"
|
||||
title="View ranking LLM call details"
|
||||
>
|
||||
<Bug className="h-3 w-3" />
|
||||
|
|
@ -1221,7 +1212,7 @@ export const LLMCallsTimeline = memo(function LLMCallsTimeline({
|
|||
<div className="flex items-center gap-3 text-sm">
|
||||
<span className="text-green-600 dark:text-green-400">✓</span>
|
||||
<span className="font-medium">ranking</span>
|
||||
<span className="text-gray-500">
|
||||
<span className="text-zinc-500">
|
||||
{((rankingCall.latency_ms || 0) / 1000).toFixed(2)}s · ${(rankingCall.llm_cost || 0).toFixed(4)}
|
||||
</span>
|
||||
{rankingCall.model_name && (
|
||||
|
|
@ -1232,30 +1223,30 @@ export const LLMCallsTimeline = memo(function LLMCallsTimeline({
|
|||
</div>
|
||||
|
||||
{/* Metrics Grid */}
|
||||
<div className="grid grid-cols-2 sm:grid-cols-4 gap-4 p-4 bg-gray-50 dark:bg-gray-900/50 rounded-lg text-sm">
|
||||
<div className="grid grid-cols-2 sm:grid-cols-4 gap-4 p-4 bg-zinc-50 dark:bg-zinc-900/50 rounded-sm text-sm">
|
||||
<div className="flex flex-col">
|
||||
<span className="text-gray-500 dark:text-gray-400 text-xs mb-1">Tokens</span>
|
||||
<span className="text-zinc-500 dark:text-zinc-400 text-xs mb-1">Tokens</span>
|
||||
<span className="font-medium">{rankingCall.total_tokens?.toLocaleString() ?? "N/A"}</span>
|
||||
</div>
|
||||
<div className="flex flex-col">
|
||||
<span className="text-gray-500 dark:text-gray-400 text-xs mb-1">Latency</span>
|
||||
<span className="text-zinc-500 dark:text-zinc-400 text-xs mb-1">Latency</span>
|
||||
<span className="font-medium">{rankingCall.latency_ms ? `${rankingCall.latency_ms}ms` : "N/A"}</span>
|
||||
</div>
|
||||
<div className="flex flex-col">
|
||||
<span className="text-gray-500 dark:text-gray-400 text-xs mb-1">Time</span>
|
||||
<span className="text-zinc-500 dark:text-zinc-400 text-xs mb-1">Time</span>
|
||||
<span className="font-medium">{new Date(rankingCall.created_at).toLocaleTimeString()}</span>
|
||||
</div>
|
||||
<div className="flex flex-col">
|
||||
<span className="text-gray-500 dark:text-gray-400 text-xs mb-1">Cost</span>
|
||||
<span className="text-zinc-500 dark:text-zinc-400 text-xs mb-1">Cost</span>
|
||||
<span className="font-medium">${(rankingCall.llm_cost || 0).toFixed(4)}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Token Distribution */}
|
||||
{rankingCall.prompt_tokens && rankingCall.completion_tokens && (
|
||||
<div className="p-4 bg-gray-50 dark:bg-gray-900/50 rounded-lg">
|
||||
<div className="p-4 bg-zinc-50 dark:bg-zinc-900/50 rounded-sm">
|
||||
<div className="flex items-center gap-2 mb-3">
|
||||
<Hash className="h-4 w-4 text-gray-400" />
|
||||
<Hash className="h-4 w-4 text-zinc-400" />
|
||||
<span className="text-sm font-medium">Token Distribution</span>
|
||||
</div>
|
||||
<TokenDistributionBar
|
||||
|
|
@ -1271,12 +1262,12 @@ export const LLMCallsTimeline = memo(function LLMCallsTimeline({
|
|||
<div className="space-y-2">
|
||||
<div className="flex items-center justify-between">
|
||||
<span className="text-sm font-medium flex items-center gap-2">
|
||||
<FileText className="h-4 w-4 text-gray-400" />
|
||||
<FileText className="h-4 w-4 text-zinc-400" />
|
||||
User Prompt
|
||||
</span>
|
||||
<CopyButton text={rankingCall.user_prompt} label="user prompt" size="sm" />
|
||||
</div>
|
||||
<pre className="p-3 text-xs bg-gray-50 dark:bg-gray-900 rounded-lg overflow-auto max-h-48 whitespace-pre-wrap">
|
||||
<pre className="p-3 text-xs bg-zinc-50 dark:bg-zinc-900 rounded-sm overflow-auto max-h-48 whitespace-pre-wrap">
|
||||
{rankingCall.user_prompt}
|
||||
</pre>
|
||||
</div>
|
||||
|
|
@ -1287,12 +1278,12 @@ export const LLMCallsTimeline = memo(function LLMCallsTimeline({
|
|||
<div className="space-y-2">
|
||||
<div className="flex items-center justify-between">
|
||||
<span className="text-sm font-medium flex items-center gap-2">
|
||||
<Code className="h-4 w-4 text-gray-400" />
|
||||
<Code className="h-4 w-4 text-zinc-400" />
|
||||
Response
|
||||
</span>
|
||||
<CopyButton text={rankingCall.raw_response} label="response" size="sm" />
|
||||
</div>
|
||||
<pre className="p-3 text-xs bg-gray-50 dark:bg-gray-900 rounded-lg overflow-auto max-h-48 whitespace-pre-wrap">
|
||||
<pre className="p-3 text-xs bg-zinc-50 dark:bg-zinc-900 rounded-sm overflow-auto max-h-48 whitespace-pre-wrap">
|
||||
{rankingCall.raw_response}
|
||||
</pre>
|
||||
</div>
|
||||
|
|
@ -1305,8 +1296,8 @@ export const LLMCallsTimeline = memo(function LLMCallsTimeline({
|
|||
)}
|
||||
</div>
|
||||
</div>
|
||||
<div className="p-4 bg-white dark:bg-gray-800">
|
||||
<p className="text-sm text-gray-700 dark:text-gray-300 whitespace-pre-wrap leading-relaxed">
|
||||
<div className="p-4 bg-white dark:bg-zinc-800">
|
||||
<p className="text-sm text-zinc-700 dark:text-zinc-300 whitespace-pre-wrap leading-relaxed">
|
||||
{rankingExplanation}
|
||||
</p>
|
||||
</div>
|
||||
|
|
@ -1335,7 +1326,7 @@ const TokenDistributionBar = memo(function TokenDistributionBar({
|
|||
const completionPercent = effectiveTotal > 0 ? Math.round((completionTokens / effectiveTotal) * 100) : 0
|
||||
|
||||
return (
|
||||
<div className="flex h-6 rounded-lg overflow-hidden border border-gray-200 dark:border-gray-600 mb-2">
|
||||
<div className="flex h-6 rounded-sm overflow-hidden border border-zinc-200 dark:border-zinc-600 mb-2">
|
||||
<div
|
||||
className="bg-blue-500 flex items-center justify-center text-white text-xs font-semibold"
|
||||
style={{ width: `${promptPercent}%` }}
|
||||
|
|
@ -1378,49 +1369,45 @@ const LineProfilerResultsSection = memo(function LineProfilerResultsSection({
|
|||
const hasData = report.functions.length > 0
|
||||
|
||||
return (
|
||||
<div className="mt-3 rounded-lg border border-amber-200 dark:border-amber-800 overflow-hidden">
|
||||
<div className="mt-3 rounded-sm border border-amber-200 dark:border-amber-800 overflow-hidden">
|
||||
<div className="w-full p-3 flex items-center justify-between bg-amber-50 dark:bg-amber-900/20">
|
||||
<div
|
||||
role="button"
|
||||
tabIndex={0}
|
||||
onClick={onToggle}
|
||||
onKeyDown={e => { if (e.key === "Enter" || e.key === " ") onToggle() }}
|
||||
className="flex items-center gap-2 cursor-pointer hover:opacity-80 flex-1"
|
||||
className="flex items-center gap-2 cursor-pointer hover:opacity-80 flex-1 transition-opacity duration-150"
|
||||
>
|
||||
<Activity className="h-4 w-4 text-amber-500" />
|
||||
<span className="text-sm font-medium text-gray-900 dark:text-white">Original code profiling data</span>
|
||||
<span className="text-sm font-medium text-zinc-900 dark:text-white">Original code profiling data</span>
|
||||
{hasData && report.functions[0] && (
|
||||
<span className="text-xs text-gray-500 dark:text-gray-400">
|
||||
<span className="text-xs text-zinc-500 dark:text-zinc-400">
|
||||
{report.functions[0].functionName} · {report.functions[0].totalTime}
|
||||
</span>
|
||||
)}
|
||||
{isExpanded ? (
|
||||
<ChevronDown className="h-4 w-4 text-gray-400" />
|
||||
) : (
|
||||
<ChevronRight className="h-4 w-4 text-gray-400" />
|
||||
)}
|
||||
<ChevronDown className={`h-4 w-4 text-zinc-400 transition-transform duration-200 ${isExpanded ? '' : '-rotate-90'}`} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{isExpanded && (
|
||||
<div className="border-t border-gray-200 dark:border-gray-700">
|
||||
<div className="border-t border-zinc-200 dark:border-zinc-700">
|
||||
{hasData && (
|
||||
<div className="flex gap-1 p-2 bg-gray-100 dark:bg-gray-800 border-b border-gray-200 dark:border-gray-700">
|
||||
<div className="flex gap-1 p-2 bg-zinc-100 dark:bg-zinc-800 border-b border-zinc-200 dark:border-zinc-700">
|
||||
<button
|
||||
onClick={e => { e.stopPropagation(); setViewMode("table") }}
|
||||
className={`px-2 py-1 text-xs rounded ${viewMode === "table" ? "bg-amber-500 text-white" : "bg-gray-200 dark:bg-gray-700 text-gray-600 dark:text-gray-300"}`}
|
||||
className={`px-2 py-1 text-xs rounded ${viewMode === "table" ? "bg-zinc-700 text-white border border-zinc-600" : "bg-zinc-200 dark:bg-transparent text-zinc-600 dark:text-zinc-300 hover:bg-zinc-300 dark:hover:bg-zinc-800"}`}
|
||||
>
|
||||
Table
|
||||
</button>
|
||||
<button
|
||||
onClick={e => { e.stopPropagation(); setViewMode("code") }}
|
||||
className={`px-2 py-1 text-xs rounded ${viewMode === "code" ? "bg-amber-500 text-white" : "bg-gray-200 dark:bg-gray-700 text-gray-600 dark:text-gray-300"}`}
|
||||
className={`px-2 py-1 text-xs rounded ${viewMode === "code" ? "bg-zinc-700 text-white border border-zinc-600" : "bg-zinc-200 dark:bg-transparent text-zinc-600 dark:text-zinc-300 hover:bg-zinc-300 dark:hover:bg-zinc-800"}`}
|
||||
>
|
||||
Code
|
||||
</button>
|
||||
<button
|
||||
onClick={e => { e.stopPropagation(); setViewMode("bars") }}
|
||||
className={`px-2 py-1 text-xs rounded ${viewMode === "bars" ? "bg-amber-500 text-white" : "bg-gray-200 dark:bg-gray-700 text-gray-600 dark:text-gray-300"}`}
|
||||
className={`px-2 py-1 text-xs rounded ${viewMode === "bars" ? "bg-zinc-700 text-white border border-zinc-600" : "bg-zinc-200 dark:bg-transparent text-zinc-600 dark:text-zinc-300 hover:bg-zinc-300 dark:hover:bg-zinc-800"}`}
|
||||
>
|
||||
Bars
|
||||
</button>
|
||||
|
|
@ -1434,7 +1421,7 @@ const LineProfilerResultsSection = memo(function LineProfilerResultsSection({
|
|||
{viewMode === "bars" && <LineProfilerBarsView report={report} />}
|
||||
</>
|
||||
) : (
|
||||
<pre className="p-4 text-sm overflow-auto whitespace-pre-wrap text-gray-900 dark:text-gray-100 leading-relaxed font-mono max-h-96 bg-gray-50 dark:bg-gray-900">
|
||||
<pre className="p-4 text-sm overflow-auto whitespace-pre-wrap text-zinc-900 dark:text-zinc-100 leading-relaxed font-mono max-h-96 bg-zinc-50 dark:bg-zinc-900">
|
||||
{content}
|
||||
</pre>
|
||||
)}
|
||||
|
|
@ -1448,12 +1435,12 @@ const LineProfilerResultsSection = memo(function LineProfilerResultsSection({
|
|||
// View 1: Table view with all columns
|
||||
const LineProfilerTableView = memo(function LineProfilerTableView({ report }: { report: LineProfilerReport }) {
|
||||
return (
|
||||
<div className="bg-gray-50 dark:bg-gray-900 max-h-96 overflow-auto">
|
||||
<div className="bg-zinc-50 dark:bg-zinc-900 max-h-96 overflow-auto">
|
||||
{report.functions.map((func, funcIndex) => (
|
||||
<div key={`${func.functionName}-${funcIndex}`}>
|
||||
<table className="w-full text-xs font-mono">
|
||||
<thead className="sticky top-0 bg-gray-100 dark:bg-gray-800">
|
||||
<tr className="text-gray-500 dark:text-gray-400 border-b border-gray-200 dark:border-gray-700">
|
||||
<thead className="sticky top-0 bg-zinc-100 dark:bg-zinc-800">
|
||||
<tr className="text-zinc-500 dark:text-zinc-400 border-b border-zinc-200 dark:border-zinc-700">
|
||||
<th className="px-2 py-1.5 text-right w-16">Hits</th>
|
||||
<th className="px-2 py-1.5 text-right w-20">Time</th>
|
||||
<th className="px-2 py-1.5 text-right w-16">% Time</th>
|
||||
|
|
@ -1465,17 +1452,17 @@ const LineProfilerTableView = memo(function LineProfilerTableView({ report }: {
|
|||
const heatLevel = getHeatLevel(entry.percentTime)
|
||||
const heatColor = HEAT_COLORS[heatLevel]
|
||||
return (
|
||||
<tr key={i} className={`border-b border-gray-100 dark:border-gray-800 ${heatColor}`}>
|
||||
<td className="px-2 py-0.5 text-right text-gray-500 dark:text-gray-400">{entry.hits}</td>
|
||||
<td className="px-2 py-0.5 text-right text-gray-500 dark:text-gray-400">{entry.time}</td>
|
||||
<tr key={i} className={`border-b border-zinc-100 dark:border-zinc-800 ${heatColor}`}>
|
||||
<td className="px-2 py-0.5 text-right text-zinc-500 dark:text-zinc-400">{entry.hits}</td>
|
||||
<td className="px-2 py-0.5 text-right text-zinc-500 dark:text-zinc-400">{entry.time}</td>
|
||||
<td className="px-2 py-0.5 text-right">
|
||||
{entry.percentTime > 0 && (
|
||||
<span className={heatLevel !== "cold" ? "font-semibold text-orange-600 dark:text-orange-400" : "text-gray-500 dark:text-gray-400"}>
|
||||
<span className={heatLevel !== "cold" ? "font-semibold text-orange-600 dark:text-orange-400" : "text-zinc-500 dark:text-zinc-400"}>
|
||||
{entry.percentTime.toFixed(1)}%
|
||||
</span>
|
||||
)}
|
||||
</td>
|
||||
<td className="px-2 py-0.5 text-gray-900 dark:text-gray-100 whitespace-pre">{entry.lineContents}</td>
|
||||
<td className="px-2 py-0.5 text-zinc-900 dark:text-zinc-100 whitespace-pre">{entry.lineContents}</td>
|
||||
</tr>
|
||||
)
|
||||
})}
|
||||
|
|
@ -1490,16 +1477,16 @@ const LineProfilerTableView = memo(function LineProfilerTableView({ report }: {
|
|||
// View 2: Code-centric view with timing in gutter
|
||||
const LineProfilerCodeView = memo(function LineProfilerCodeView({ report }: { report: LineProfilerReport }) {
|
||||
return (
|
||||
<div className="bg-gray-900 max-h-96 overflow-auto">
|
||||
<div className="bg-zinc-900 max-h-96 overflow-auto">
|
||||
{report.functions.map((func, funcIndex) => (
|
||||
<div key={`${func.functionName}-${funcIndex}`} className="font-mono text-xs">
|
||||
{func.entries.map((entry, i) => {
|
||||
const heatLevel = getHeatLevel(entry.percentTime)
|
||||
const barWidth = Math.min(entry.percentTime, 100)
|
||||
return (
|
||||
<div key={i} className="flex group hover:bg-gray-800">
|
||||
<div key={i} className="flex group hover:bg-zinc-800">
|
||||
{/* Gutter with timing info */}
|
||||
<div className="w-24 flex-shrink-0 flex items-center justify-end pr-2 border-r border-gray-700 bg-gray-950 text-gray-500">
|
||||
<div className="w-24 flex-shrink-0 flex items-center justify-end pr-2 border-r border-zinc-700 bg-zinc-950 text-zinc-500">
|
||||
{entry.percentTime > 0 && (
|
||||
<span className={`${heatLevel !== "cold" ? "text-orange-400 font-semibold" : ""}`}>
|
||||
{entry.percentTime.toFixed(1)}%
|
||||
|
|
@ -1507,7 +1494,7 @@ const LineProfilerCodeView = memo(function LineProfilerCodeView({ report }: { re
|
|||
)}
|
||||
</div>
|
||||
{/* Heat indicator bar */}
|
||||
<div className="w-12 flex-shrink-0 relative bg-gray-950">
|
||||
<div className="w-12 flex-shrink-0 relative bg-zinc-950">
|
||||
{entry.percentTime > 0 && (
|
||||
<div
|
||||
className={`absolute top-0 bottom-0 left-0 ${
|
||||
|
|
@ -1521,7 +1508,7 @@ const LineProfilerCodeView = memo(function LineProfilerCodeView({ report }: { re
|
|||
)}
|
||||
</div>
|
||||
{/* Code */}
|
||||
<pre className="flex-1 py-0.5 px-2 text-gray-100 whitespace-pre overflow-x-auto">
|
||||
<pre className="flex-1 py-0.5 px-2 text-zinc-100 whitespace-pre overflow-x-auto">
|
||||
{entry.lineContents}
|
||||
</pre>
|
||||
</div>
|
||||
|
|
@ -1536,7 +1523,7 @@ const LineProfilerCodeView = memo(function LineProfilerCodeView({ report }: { re
|
|||
// View 3: Horizontal bar chart view
|
||||
const LineProfilerBarsView = memo(function LineProfilerBarsView({ report }: { report: LineProfilerReport }) {
|
||||
return (
|
||||
<div className="bg-gray-50 dark:bg-gray-900 max-h-96 overflow-auto p-2">
|
||||
<div className="bg-zinc-50 dark:bg-zinc-900 max-h-96 overflow-auto p-2">
|
||||
{report.functions.map((func, funcIndex) => (
|
||||
<div key={`${func.functionName}-${funcIndex}`} className="space-y-0.5">
|
||||
{func.entries.map((entry, i) => {
|
||||
|
|
@ -1548,13 +1535,13 @@ const LineProfilerBarsView = memo(function LineProfilerBarsView({ report }: { re
|
|||
{/* Percentage */}
|
||||
<div className="w-12 text-right flex-shrink-0">
|
||||
{hasTime && (
|
||||
<span className={heatLevel !== "cold" ? "text-orange-600 dark:text-orange-400 font-semibold" : "text-gray-400"}>
|
||||
<span className={heatLevel !== "cold" ? "text-orange-600 dark:text-orange-400 font-semibold" : "text-zinc-400"}>
|
||||
{entry.percentTime.toFixed(1)}%
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
{/* Bar */}
|
||||
<div className="w-24 h-4 bg-gray-200 dark:bg-gray-800 rounded-sm overflow-hidden flex-shrink-0">
|
||||
<div className="w-24 h-4 bg-zinc-200 dark:bg-zinc-800 rounded-sm overflow-hidden flex-shrink-0">
|
||||
{hasTime && (
|
||||
<div
|
||||
className={`h-full rounded-sm ${
|
||||
|
|
@ -1562,15 +1549,15 @@ const LineProfilerBarsView = memo(function LineProfilerBarsView({ report }: { re
|
|||
heatLevel === "hot-3" ? "bg-orange-500" :
|
||||
heatLevel === "hot-2" ? "bg-yellow-500" :
|
||||
heatLevel === "hot-1" ? "bg-yellow-400" :
|
||||
"bg-gray-400"
|
||||
"bg-zinc-400"
|
||||
}`}
|
||||
style={{ width: `${barWidth}%` }}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
{/* Code snippet (truncated) */}
|
||||
<div className="flex-1 truncate text-gray-700 dark:text-gray-300" title={entry.lineContents}>
|
||||
{entry.lineContents.trim() || <span className="text-gray-400 italic">empty line</span>}
|
||||
<div className="flex-1 truncate text-zinc-700 dark:text-zinc-300" title={entry.lineContents}>
|
||||
{entry.lineContents.trim() || <span className="text-zinc-400 italic">empty line</span>}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
|
@ -1608,15 +1595,15 @@ const TestContextSection = memo(function TestContextSection({
|
|||
const helpers = context.helper_function_names || []
|
||||
|
||||
return (
|
||||
<div className="mb-3 rounded-lg border border-cyan-200 dark:border-cyan-800 overflow-hidden text-xs">
|
||||
<div className="mb-3 rounded-sm border border-cyan-200 dark:border-cyan-800 overflow-hidden text-xs">
|
||||
{/* Collapsible Header */}
|
||||
<button
|
||||
onClick={() => setIsExpanded(!isExpanded)}
|
||||
className="w-full p-3 flex items-center justify-between bg-cyan-50/50 dark:bg-cyan-900/10 hover:bg-cyan-100/50 dark:hover:bg-cyan-900/20 transition-colors"
|
||||
className="w-full p-3 flex items-center justify-between bg-cyan-50/50 dark:bg-cyan-900/10 hover:bg-cyan-100/50 dark:hover:bg-cyan-900/20 transition-colors duration-150"
|
||||
>
|
||||
<div className="flex items-center gap-2">
|
||||
<FlaskConical className="h-3.5 w-3.5 text-cyan-500" />
|
||||
<span className="font-medium text-gray-700 dark:text-gray-300">Test Generation Context</span>
|
||||
<span className="font-medium text-zinc-700 dark:text-zinc-300">Test Generation Context</span>
|
||||
{testFramework && (
|
||||
<span className="px-1.5 py-0.5 bg-cyan-100 dark:bg-cyan-900/50 text-cyan-700 dark:text-cyan-300 rounded text-[10px] font-medium">
|
||||
{testFramework}
|
||||
|
|
@ -1634,26 +1621,22 @@ const TestContextSection = memo(function TestContextSection({
|
|||
{context.function_to_optimize.qualified_name}
|
||||
</code>
|
||||
)}
|
||||
{isExpanded ? (
|
||||
<ChevronDown className="h-4 w-4 text-gray-400" />
|
||||
) : (
|
||||
<ChevronRight className="h-4 w-4 text-gray-400" />
|
||||
)}
|
||||
<ChevronDown className={`h-4 w-4 text-zinc-400 transition-transform duration-200 ${isExpanded ? '' : '-rotate-90'}`} />
|
||||
</div>
|
||||
</button>
|
||||
|
||||
{/* Expanded Content */}
|
||||
{isExpanded && (
|
||||
<div className="p-3 border-t border-cyan-200 dark:border-cyan-800 bg-white dark:bg-gray-800 space-y-2">
|
||||
<div className="p-3 border-t border-cyan-200 dark:border-cyan-800 bg-white dark:bg-zinc-800 space-y-2">
|
||||
{context.function_to_optimize && (
|
||||
<div>
|
||||
<span className="text-gray-500 dark:text-gray-400 text-[10px] uppercase tracking-wide">Function</span>
|
||||
<span className="text-zinc-500 dark:text-zinc-400 text-[10px] uppercase tracking-wide">Function</span>
|
||||
<div className="flex items-center gap-2 mt-0.5">
|
||||
<code className="text-cyan-700 dark:text-cyan-300 font-mono break-all">
|
||||
{context.function_to_optimize.qualified_name}
|
||||
</code>
|
||||
{context.function_to_optimize.starting_line && context.function_to_optimize.ending_line && (
|
||||
<span className="text-gray-400 dark:text-gray-500 shrink-0">
|
||||
<span className="text-zinc-400 dark:text-zinc-500 shrink-0">
|
||||
L{context.function_to_optimize.starting_line}-{context.function_to_optimize.ending_line}
|
||||
</span>
|
||||
)}
|
||||
|
|
@ -1663,7 +1646,7 @@ const TestContextSection = memo(function TestContextSection({
|
|||
|
||||
{context.function_to_optimize?.file_path && (
|
||||
<div>
|
||||
<span className="text-gray-500 dark:text-gray-400 text-[10px] uppercase tracking-wide">File</span>
|
||||
<span className="text-zinc-500 dark:text-zinc-400 text-[10px] uppercase tracking-wide">File</span>
|
||||
<code className="block text-cyan-700 dark:text-cyan-300 font-mono mt-0.5 break-all">
|
||||
{context.function_to_optimize.file_path}
|
||||
</code>
|
||||
|
|
@ -1672,7 +1655,7 @@ const TestContextSection = memo(function TestContextSection({
|
|||
|
||||
{context.module_path && (
|
||||
<div>
|
||||
<span className="text-gray-500 dark:text-gray-400 text-[10px] uppercase tracking-wide">Module</span>
|
||||
<span className="text-zinc-500 dark:text-zinc-400 text-[10px] uppercase tracking-wide">Module</span>
|
||||
<code className="block text-cyan-700 dark:text-cyan-300 font-mono mt-0.5 break-all">
|
||||
{context.module_path}
|
||||
</code>
|
||||
|
|
@ -1681,7 +1664,7 @@ const TestContextSection = memo(function TestContextSection({
|
|||
|
||||
{context.test_module_path && (
|
||||
<div>
|
||||
<span className="text-gray-500 dark:text-gray-400 text-[10px] uppercase tracking-wide">Test Module</span>
|
||||
<span className="text-zinc-500 dark:text-zinc-400 text-[10px] uppercase tracking-wide">Test Module</span>
|
||||
<code className="block text-cyan-700 dark:text-cyan-300 font-mono mt-0.5 break-all">
|
||||
{context.test_module_path}
|
||||
</code>
|
||||
|
|
@ -1692,9 +1675,9 @@ const TestContextSection = memo(function TestContextSection({
|
|||
<div>
|
||||
<button
|
||||
onClick={(e) => { e.stopPropagation(); setShowHelpers(!showHelpers) }}
|
||||
className="flex items-center gap-1 text-gray-500 dark:text-gray-400 text-[10px] uppercase tracking-wide hover:text-gray-700 dark:hover:text-gray-200"
|
||||
className="flex items-center gap-1 text-zinc-500 dark:text-zinc-400 text-[10px] uppercase tracking-wide hover:text-zinc-700 dark:hover:text-zinc-200"
|
||||
>
|
||||
{showHelpers ? <ChevronDown className="h-3 w-3" /> : <ChevronRight className="h-3 w-3" />}
|
||||
<ChevronDown className={`h-3 w-3 transition-transform duration-200 ${showHelpers ? '' : '-rotate-90'}`} />
|
||||
Helpers ({helpers.length})
|
||||
</button>
|
||||
{showHelpers && (
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@ export function TraceSearch({ initialTraceId = "", isLoading = false, hasResults
|
|||
|
||||
const inputBorderClass = hasResults
|
||||
? "border-green-500 dark:border-green-500 focus:ring-green-500"
|
||||
: "border-gray-300 dark:border-gray-600 focus:ring-blue-500"
|
||||
: "border-zinc-300 dark:border-zinc-600 focus:ring-blue-500"
|
||||
|
||||
return (
|
||||
<div className="w-full max-w-2xl mx-auto">
|
||||
|
|
@ -51,10 +51,10 @@ export function TraceSearch({ initialTraceId = "", isLoading = false, hasResults
|
|||
onChange={handleChange}
|
||||
onKeyDown={handleKeyDown}
|
||||
placeholder="Enter trace ID..."
|
||||
className={`w-full px-4 py-3 pl-11 ${hasResults ? "pr-11" : ""} text-base rounded-lg border ${inputBorderClass} bg-white dark:bg-gray-800 text-gray-900 dark:text-white placeholder-gray-400 dark:placeholder-gray-500 focus:outline-none focus:ring-2 focus:border-transparent transition-shadow`}
|
||||
className={`w-full px-4 py-3 pl-11 ${hasResults ? "pr-11" : ""} text-base rounded-md border ${inputBorderClass} bg-white dark:bg-zinc-950 text-zinc-900 dark:text-zinc-50 placeholder-zinc-400 dark:placeholder-zinc-500 focus:outline-none focus:ring-2 focus:border-transparent transition-all duration-150`}
|
||||
aria-label="Trace ID"
|
||||
/>
|
||||
<Search className="absolute left-4 top-1/2 -translate-y-1/2 h-5 w-5 text-gray-400" />
|
||||
<Search className="absolute left-4 top-1/2 -translate-y-1/2 h-4 w-4 text-zinc-400" />
|
||||
{hasResults && (
|
||||
<CheckCircle className="absolute right-4 top-1/2 -translate-y-1/2 h-5 w-5 text-green-500" />
|
||||
)}
|
||||
|
|
@ -62,7 +62,7 @@ export function TraceSearch({ initialTraceId = "", isLoading = false, hasResults
|
|||
<button
|
||||
onClick={handleSearch}
|
||||
disabled={!traceId.trim() || isLoading}
|
||||
className="px-6 py-3 bg-blue-600 hover:bg-blue-700 disabled:bg-blue-400 disabled:cursor-not-allowed text-white font-medium rounded-lg transition-colors flex items-center gap-2 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2"
|
||||
className="px-6 py-3 bg-blue-600 hover:bg-blue-700 disabled:bg-blue-400 disabled:cursor-not-allowed text-white font-medium rounded-md transition-colors duration-150 flex items-center gap-2 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2"
|
||||
>
|
||||
{isLoading ? (
|
||||
<>
|
||||
|
|
@ -75,7 +75,7 @@ export function TraceSearch({ initialTraceId = "", isLoading = false, hasResults
|
|||
</button>
|
||||
</div>
|
||||
{!hasResults && (
|
||||
<p className="mt-2 text-sm text-gray-500 dark:text-gray-400">
|
||||
<p className="mt-2 text-sm text-zinc-500 dark:text-zinc-400">
|
||||
Paste or type a trace ID to view all associated LLM calls, candidates, and errors
|
||||
</p>
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -49,10 +49,10 @@ export function TraceSummary({
|
|||
const SourceIcon = source.toLowerCase().includes("github") ? Github : Terminal
|
||||
|
||||
return (
|
||||
<div className="bg-white dark:bg-gray-800 rounded-lg shadow p-6 border border-gray-200 dark:border-gray-700">
|
||||
<div className="bg-white dark:bg-zinc-950 rounded-sm p-6 border border-zinc-200 dark:border-zinc-800">
|
||||
<div className="grid grid-cols-2 md:grid-cols-3 lg:grid-cols-6 gap-6">
|
||||
<div>
|
||||
<div className="flex items-center gap-1.5 text-sm text-gray-600 dark:text-gray-400 font-medium mb-2">
|
||||
<div className="group">
|
||||
<div className="flex items-center gap-1.5 text-sm text-zinc-600 dark:text-zinc-400 font-medium mb-2">
|
||||
<StatusIcon className="h-4 w-4" />
|
||||
<span>Status</span>
|
||||
<InfoIcon content="Overall trace status based on all contained calls" side="top" />
|
||||
|
|
@ -60,60 +60,60 @@ export function TraceSummary({
|
|||
<div className={`text-xl font-bold ${statusColor}`}>{status}</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div className="flex items-center gap-1.5 text-sm text-gray-600 dark:text-gray-400 font-medium mb-2">
|
||||
<div className="group">
|
||||
<div className="flex items-center gap-1.5 text-sm text-zinc-600 dark:text-zinc-400 font-medium mb-2">
|
||||
<SourceIcon className="h-4 w-4" />
|
||||
<span>Source</span>
|
||||
<InfoIcon content="Where this optimization was triggered from" side="top" />
|
||||
</div>
|
||||
<div className="text-xl font-bold text-gray-900 dark:text-white">
|
||||
<span className="px-2 py-1 bg-gray-100 dark:bg-gray-700 text-gray-700 dark:text-gray-300 rounded text-sm">
|
||||
<div className="text-xl font-bold text-zinc-900 dark:text-zinc-50">
|
||||
<span className="px-2 py-1 bg-zinc-100 dark:bg-zinc-700 text-zinc-700 dark:text-zinc-300 rounded-sm text-sm hover:bg-zinc-200 dark:hover:bg-zinc-800 transition-colors duration-150">
|
||||
{source}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div className="flex items-center gap-1.5 text-sm text-gray-600 dark:text-gray-400 font-medium mb-2">
|
||||
<div className="group">
|
||||
<div className="flex items-center gap-1.5 text-sm text-zinc-600 dark:text-zinc-400 font-medium mb-2">
|
||||
<Timer className="h-4 w-4" />
|
||||
<span>Duration</span>
|
||||
<InfoIcon content="Total time from first call to last call completion" side="top" />
|
||||
</div>
|
||||
<div className="text-xl font-bold text-gray-900 dark:text-white">
|
||||
<div className="text-xl font-bold text-zinc-900 dark:text-zinc-50">
|
||||
{durationSeconds.toFixed(2)}s
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div className="flex items-center gap-1.5 text-sm text-gray-600 dark:text-gray-400 font-medium mb-2">
|
||||
<div className="group">
|
||||
<div className="flex items-center gap-1.5 text-sm text-zinc-600 dark:text-zinc-400 font-medium mb-2">
|
||||
<DollarSign className="h-4 w-4" />
|
||||
<span>Cost</span>
|
||||
<InfoIcon content="Sum of all LLM call costs in this trace" side="top" />
|
||||
</div>
|
||||
<div className="text-xl font-bold text-gray-900 dark:text-white">
|
||||
<div className="text-xl font-bold text-zinc-900 dark:text-zinc-50">
|
||||
${totalCost.toFixed(4)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div className="flex items-center gap-1.5 text-sm text-gray-600 dark:text-gray-400 font-medium mb-2">
|
||||
<div className="group">
|
||||
<div className="flex items-center gap-1.5 text-sm text-zinc-600 dark:text-zinc-400 font-medium mb-2">
|
||||
<Hash className="h-4 w-4" />
|
||||
<span>Tokens</span>
|
||||
<InfoIcon content="Total tokens (prompt + completion) across all calls" side="top" />
|
||||
</div>
|
||||
<div className="text-xl font-bold text-gray-900 dark:text-white">
|
||||
<div className="text-xl font-bold text-zinc-900 dark:text-zinc-50">
|
||||
{totalTokens.toLocaleString()}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{candidatesCount !== undefined && (
|
||||
<div>
|
||||
<div className="flex items-center gap-1.5 text-sm text-gray-600 dark:text-gray-400 font-medium mb-2">
|
||||
<div className="group">
|
||||
<div className="flex items-center gap-1.5 text-sm text-zinc-600 dark:text-zinc-400 font-medium mb-2">
|
||||
<CodeIcon className="h-4 w-4" />
|
||||
<span>Candidates</span>
|
||||
<InfoIcon content="Number of optimization candidates generated" side="top" />
|
||||
</div>
|
||||
<div className="text-xl font-bold text-gray-900 dark:text-white">
|
||||
<div className="text-xl font-bold text-zinc-900 dark:text-zinc-50">
|
||||
{candidatesCount}
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
Loading…
Reference in a new issue