chore: remove unnecessary comments from observability components
This commit is contained in:
parent
1df2b1b67b
commit
c38500c7f0
9 changed files with 24 additions and 135 deletions
|
|
@ -46,7 +46,6 @@ export default async function Observability2Page({ searchParams }: Observability
|
|||
const params = await searchParams
|
||||
const traceId = params.trace_id?.trim()
|
||||
|
||||
// Fetch data once and pass to child (avoids double fetch)
|
||||
let traceData: Awaited<ReturnType<typeof getTraceData>> | null = null
|
||||
if (traceId) {
|
||||
const tracePrefix = traceId.substring(0, 33)
|
||||
|
|
@ -59,7 +58,6 @@ export default async function Observability2Page({ searchParams }: Observability
|
|||
|
||||
return (
|
||||
<div className="min-h-screen bg-zinc-50 dark:bg-zinc-900">
|
||||
{/* Search Section */}
|
||||
<div className="border-b border-zinc-200 dark:border-zinc-700 bg-white dark:bg-zinc-800">
|
||||
<div className="max-w-4xl mx-auto px-4 py-4">
|
||||
<Suspense fallback={<SearchSkeleton />}>
|
||||
|
|
@ -68,7 +66,6 @@ export default async function Observability2Page({ searchParams }: Observability
|
|||
</div>
|
||||
</div>
|
||||
|
||||
{/* Content */}
|
||||
{traceId && traceData ? (
|
||||
<Suspense fallback={<TraceContentSkeleton />}>
|
||||
<TraceContent traceId={traceId} traceData={traceData} />
|
||||
|
|
@ -92,12 +89,10 @@ interface TraceData {
|
|||
function TraceContent({ traceId, traceData }: { traceId: string; traceData: TraceData }) {
|
||||
const { rawLlmCalls, errors, optimizationFeatures, optimizationEvent } = traceData
|
||||
|
||||
// If no data found
|
||||
if (rawLlmCalls.length === 0 && errors.length === 0) {
|
||||
return <NotFoundState traceId={traceId} />
|
||||
}
|
||||
|
||||
// Extract optimization candidates
|
||||
const optimizationsOrigin =
|
||||
(optimizationFeatures?.optimizations_origin as Record<
|
||||
string,
|
||||
|
|
@ -130,7 +125,6 @@ function TraceContent({ traceId, traceData }: { traceId: string; traceData: Trac
|
|||
.sort((a, b) => (a.callSequence ?? Infinity) - (b.callSequence ?? Infinity))
|
||||
.map((c, index) => ({ ...c, index: index + 1 }))
|
||||
|
||||
// Extract refinement candidates (with parent relationship)
|
||||
const refinementCandidates = allCandidates
|
||||
.filter(c => c.source === "REFINE")
|
||||
.sort((a, b) => (a.callSequence ?? Infinity) - (b.callSequence ?? Infinity))
|
||||
|
|
@ -140,13 +134,11 @@ function TraceContent({ traceId, traceData }: { traceId: string; traceData: Trac
|
|||
parentId: optimizationsOrigin[c.id]?.parent || null,
|
||||
}))
|
||||
|
||||
// Extract ranking data
|
||||
const rankingData = optimizationFeatures?.ranking as
|
||||
| { ranking?: string[]; explanation?: string }
|
||||
| null
|
||||
const bestCandidateId = rankingData?.ranking?.[0] ?? null
|
||||
|
||||
// Check if best candidate was used for PR
|
||||
const pullRequestRaw = optimizationFeatures?.pull_request
|
||||
const usedForPr = Boolean(
|
||||
pullRequestRaw != null &&
|
||||
|
|
@ -155,7 +147,6 @@ function TraceContent({ traceId, traceData }: { traceId: string; traceData: Trac
|
|||
Object.keys(pullRequestRaw as Record<string, unknown>).length > 0,
|
||||
)
|
||||
|
||||
// Map candidate ID to rank position (1-based, 1 = best)
|
||||
const candidateRankMap: Record<string, number> = {}
|
||||
if (rankingData?.ranking) {
|
||||
rankingData.ranking.forEach((id, index) => {
|
||||
|
|
@ -163,25 +154,21 @@ function TraceContent({ traceId, traceData }: { traceId: string; traceData: Trac
|
|||
})
|
||||
}
|
||||
|
||||
// Extract generated tests
|
||||
const generatedTests = (optimizationFeatures?.generated_test ?? []).map((code, index) => ({
|
||||
code,
|
||||
index: index + 1,
|
||||
}))
|
||||
|
||||
// Extract instrumented tests (behavior instrumented)
|
||||
const instrumentedTests = (optimizationFeatures?.instrumented_generated_test ?? []).map((code, index) => ({
|
||||
code,
|
||||
index: index + 1,
|
||||
}))
|
||||
|
||||
// Extract instrumented perf tests (field exists in DB but not in Prisma schema yet)
|
||||
const instrumentedPerfTests = ((optimizationFeatures as Record<string, unknown>)?.instrumented_perf_test as string[] ?? []).map((code, index) => ({
|
||||
code,
|
||||
index: index + 1,
|
||||
}))
|
||||
|
||||
// Sort by call_sequence from context if available
|
||||
const llmCalls = rawLlmCalls.sort((a, b) => {
|
||||
const seqA = (a.context as { call_sequence?: number } | null)?.call_sequence ?? Infinity
|
||||
const seqB = (b.context as { call_sequence?: number } | null)?.call_sequence ?? Infinity
|
||||
|
|
@ -189,7 +176,6 @@ function TraceContent({ traceId, traceData }: { traceId: string; traceData: Trac
|
|||
return new Date(a.created_at).getTime() - new Date(b.created_at).getTime()
|
||||
})
|
||||
|
||||
// Transform LLM calls for timeline
|
||||
const transformedCalls = llmCalls.map(call => ({
|
||||
id: call.id,
|
||||
call_type: call.call_type,
|
||||
|
|
@ -202,7 +188,6 @@ function TraceContent({ traceId, traceData }: { traceId: string; traceData: Trac
|
|||
context: call.context as { call_sequence?: number } | null,
|
||||
}))
|
||||
|
||||
// Transform to timeline sections
|
||||
const { sections, totalDuration } = transformToTimelineSections({
|
||||
calls: transformedCalls,
|
||||
optimizationCandidates,
|
||||
|
|
@ -219,7 +204,6 @@ function TraceContent({ traceId, traceData }: { traceId: string; traceData: Trac
|
|||
usedForPr,
|
||||
})
|
||||
|
||||
// Transform errors for ErrorsSection
|
||||
const transformedErrors = errors.map(error => ({
|
||||
id: error.id,
|
||||
error_type: error.error_type,
|
||||
|
|
@ -263,7 +247,6 @@ function TraceContent({ traceId, traceData }: { traceId: string; traceData: Trac
|
|||
filePath={filePath}
|
||||
/>
|
||||
|
||||
{/* Errors Section (if any) */}
|
||||
{transformedErrors.length > 0 && (
|
||||
<div className="max-w-4xl mx-auto px-4 pb-20">
|
||||
<ErrorsSection errors={transformedErrors} />
|
||||
|
|
@ -324,13 +307,11 @@ function SearchSkeleton() {
|
|||
function TraceContentSkeleton() {
|
||||
return (
|
||||
<div className="max-w-4xl mx-auto px-4 py-8">
|
||||
{/* Progress skeleton */}
|
||||
<div className="mb-8">
|
||||
<div className="h-4 w-48 bg-zinc-200 dark:bg-zinc-700 rounded mb-2 animate-pulse" />
|
||||
<div className="h-1 w-full bg-zinc-200 dark:bg-zinc-700 rounded animate-pulse" />
|
||||
</div>
|
||||
|
||||
{/* Timeline items skeleton */}
|
||||
<div className="space-y-8">
|
||||
{[1, 2, 3].map((i) => (
|
||||
<div key={i} className="flex gap-4">
|
||||
|
|
|
|||
|
|
@ -25,15 +25,12 @@ function estimateTokens(text: string): number {
|
|||
return Math.ceil(text.length / 4)
|
||||
}
|
||||
|
||||
/** Extract filename from a full path */
|
||||
function getFilename(path: string): string {
|
||||
return path.split("/").pop() || path
|
||||
}
|
||||
|
||||
/** Parse markdown code blocks into individual files */
|
||||
function parseMarkdownCodeBlocks(markdown: string): ParsedFile[] {
|
||||
const files: ParsedFile[] = []
|
||||
// Match ```language:path\ncode\n``` blocks
|
||||
const regex = /```(\w+):([^\n]+)\n([\s\S]*?)```/g
|
||||
let match
|
||||
|
||||
|
|
@ -61,7 +58,6 @@ export const CodeContextSection = memo(function CodeContextSection({
|
|||
const [expandedSections, setExpandedSections] = useState<Set<string>>(new Set())
|
||||
const [expandedFiles, setExpandedFiles] = useState<Set<string>>(new Set())
|
||||
|
||||
// Parse files from markdown
|
||||
const { rwFiles, roFiles, metrics } = useMemo(() => {
|
||||
const rwFiles = originalCode ? parseMarkdownCodeBlocks(originalCode) : []
|
||||
const roFiles = dependencyCode ? parseMarkdownCodeBlocks(dependencyCode) : []
|
||||
|
|
@ -103,14 +99,12 @@ export const CodeContextSection = memo(function CodeContextSection({
|
|||
})
|
||||
}, [])
|
||||
|
||||
// Don't render if no context data
|
||||
if (!originalCode && !dependencyCode) {
|
||||
return null
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="bg-white dark:bg-zinc-900 rounded-sm border border-zinc-200 dark:border-zinc-800">
|
||||
{/* Header - always visible */}
|
||||
<div
|
||||
role="button"
|
||||
tabIndex={0}
|
||||
|
|
@ -127,7 +121,6 @@ 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-zinc-500 dark:text-zinc-400">
|
||||
<span className="flex items-center gap-1">
|
||||
<Hash className="h-4 w-4" />
|
||||
|
|
@ -142,10 +135,8 @@ export const CodeContextSection = memo(function CodeContextSection({
|
|||
</div>
|
||||
</div>
|
||||
|
||||
{/* Expanded content */}
|
||||
{isExpanded && (
|
||||
<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 && (
|
||||
|
|
@ -167,7 +158,6 @@ export const CodeContextSection = memo(function CodeContextSection({
|
|||
</div>
|
||||
)}
|
||||
|
||||
{/* Token breakdown - only show when both types exist */}
|
||||
{rwFiles.length > 0 && roFiles.length > 0 && (
|
||||
<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">
|
||||
|
|
@ -198,7 +188,6 @@ export const CodeContextSection = memo(function CodeContextSection({
|
|||
</div>
|
||||
)}
|
||||
|
||||
{/* Read-Writable Code Section */}
|
||||
{rwFiles.length > 0 && (
|
||||
<CodeGroupSection
|
||||
title="Read-Writable Code"
|
||||
|
|
@ -215,7 +204,6 @@ export const CodeContextSection = memo(function CodeContextSection({
|
|||
/>
|
||||
)}
|
||||
|
||||
{/* Read-Only Dependencies Section */}
|
||||
{roFiles.length > 0 && (
|
||||
<CodeGroupSection
|
||||
title="Read-Only Dependencies"
|
||||
|
|
@ -314,7 +302,6 @@ const CodeGroupSection = memo(function CodeGroupSection({
|
|||
const isFileExpanded = expandedFiles.has(fileKey)
|
||||
return (
|
||||
<div key={fileKey}>
|
||||
{/* File header */}
|
||||
<div className="px-4 py-2 flex items-center justify-between bg-zinc-100 dark:bg-zinc-950">
|
||||
<div
|
||||
role="button"
|
||||
|
|
@ -340,7 +327,6 @@ const CodeGroupSection = memo(function CodeGroupSection({
|
|||
</div>
|
||||
</div>
|
||||
|
||||
{/* File code */}
|
||||
{isFileExpanded && (
|
||||
<div className="max-h-96 overflow-y-auto">
|
||||
<CodeHighlighter
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@
|
|||
import dynamic from "next/dynamic"
|
||||
import { memo } from "react"
|
||||
|
||||
// Lazy-load the heavy syntax highlighter library (~500KB)
|
||||
const SyntaxHighlighter = dynamic(
|
||||
() => import("react-syntax-highlighter").then(m => m.Prism),
|
||||
{
|
||||
|
|
@ -18,11 +17,9 @@ const SyntaxHighlighter = dynamic(
|
|||
}
|
||||
)
|
||||
|
||||
// Custom zinc-based theme for syntax highlighting
|
||||
export const zincDarkTheme = {
|
||||
// Base colors
|
||||
'code[class*="language-"]': {
|
||||
color: 'rgb(250, 250, 250)', // zinc-50
|
||||
color: 'rgb(250, 250, 250)',
|
||||
background: 'none',
|
||||
fontFamily: 'var(--font-mono)',
|
||||
fontSize: '1em',
|
||||
|
|
@ -36,8 +33,8 @@ export const zincDarkTheme = {
|
|||
hyphens: 'none',
|
||||
},
|
||||
'pre[class*="language-"]': {
|
||||
color: 'rgb(250, 250, 250)', // zinc-50
|
||||
background: 'rgb(24, 24, 27)', // zinc-900
|
||||
color: 'rgb(250, 250, 250)',
|
||||
background: 'rgb(24, 24, 27)',
|
||||
fontFamily: 'var(--font-mono)',
|
||||
fontSize: '1em',
|
||||
textAlign: 'left',
|
||||
|
|
@ -52,37 +49,28 @@ export const zincDarkTheme = {
|
|||
margin: '0',
|
||||
overflow: 'auto',
|
||||
},
|
||||
// Comments
|
||||
comment: {
|
||||
color: 'rgb(113, 113, 122)', // zinc-500
|
||||
color: 'rgb(113, 113, 122)',
|
||||
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
|
||||
keyword: { color: 'rgb(96, 165, 250)' },
|
||||
'control-flow': { color: 'rgb(96, 165, 250)' },
|
||||
// Strings
|
||||
string: { color: 'rgb(134, 239, 172)' }, // green-300
|
||||
string: { color: 'rgb(134, 239, 172)' },
|
||||
'attr-value': { color: 'rgb(134, 239, 172)' },
|
||||
// Functions
|
||||
function: { color: 'rgb(253, 224, 71)' }, // yellow-300
|
||||
function: { color: 'rgb(253, 224, 71)' },
|
||||
'class-name': { color: 'rgb(253, 224, 71)' },
|
||||
// Numbers and boolean
|
||||
number: { color: 'rgb(251, 146, 60)' }, // orange-400
|
||||
number: { color: 'rgb(251, 146, 60)' },
|
||||
boolean: { color: 'rgb(251, 146, 60)' },
|
||||
// Operators and punctuation
|
||||
operator: { color: 'rgb(161, 161, 170)' }, // zinc-400
|
||||
operator: { color: 'rgb(161, 161, 170)' },
|
||||
punctuation: { color: 'rgb(161, 161, 170)' },
|
||||
// Variables and properties
|
||||
variable: { color: 'rgb(250, 250, 250)' }, // zinc-50
|
||||
variable: { color: 'rgb(250, 250, 250)' },
|
||||
property: { color: 'rgb(250, 250, 250)' },
|
||||
// Tags and attributes
|
||||
tag: { color: 'rgb(96, 165, 250)' }, // blue-400
|
||||
tag: { color: 'rgb(96, 165, 250)' },
|
||||
'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)',
|
||||
|
|
@ -98,7 +86,6 @@ export const zincDarkTheme = {
|
|||
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)',
|
||||
|
|
@ -109,13 +96,12 @@ export const zincDarkTheme = {
|
|||
},
|
||||
} as const
|
||||
|
||||
// Shared style constants to avoid recreating objects on each render
|
||||
export const CODE_STYLE = {
|
||||
margin: 0,
|
||||
padding: "1rem",
|
||||
fontSize: "0.875rem",
|
||||
lineHeight: 1.5,
|
||||
background: 'rgb(24, 24, 27)', // zinc-900 - ensure background consistency
|
||||
background: 'rgb(24, 24, 27)',
|
||||
} as const
|
||||
|
||||
export const CODE_STYLE_RELAXED = {
|
||||
|
|
@ -123,7 +109,7 @@ export const CODE_STYLE_RELAXED = {
|
|||
padding: "1rem",
|
||||
fontSize: "0.875rem",
|
||||
lineHeight: 1.6,
|
||||
background: 'rgb(24, 24, 27)', // zinc-900 - ensure background consistency
|
||||
background: 'rgb(24, 24, 27)',
|
||||
} as const
|
||||
|
||||
export const CODE_STYLE_SMALL = {
|
||||
|
|
@ -131,7 +117,7 @@ export const CODE_STYLE_SMALL = {
|
|||
padding: "1rem",
|
||||
fontSize: "0.8125rem",
|
||||
lineHeight: 1.5,
|
||||
background: 'rgb(24, 24, 27)', // zinc-900 - ensure background consistency
|
||||
background: 'rgb(24, 24, 27)',
|
||||
} as const
|
||||
|
||||
interface CodeHighlighterProps {
|
||||
|
|
@ -143,13 +129,13 @@ interface CodeHighlighterProps {
|
|||
}
|
||||
|
||||
const highlightStyle = {
|
||||
backgroundColor: 'rgba(250, 204, 21, 0.15)', // yellow-400 at 15% opacity
|
||||
backgroundColor: 'rgba(250, 204, 21, 0.15)',
|
||||
display: 'block',
|
||||
marginLeft: '-1rem',
|
||||
marginRight: '-1rem',
|
||||
paddingLeft: '1rem',
|
||||
paddingRight: '1rem',
|
||||
borderLeft: '3px solid rgb(250, 204, 21)', // yellow-400
|
||||
borderLeft: '3px solid rgb(250, 204, 21)',
|
||||
}
|
||||
|
||||
export const CodeHighlighter = memo(function CodeHighlighter({
|
||||
|
|
|
|||
|
|
@ -51,14 +51,11 @@ export const FunctionToOptimizeSection = memo(function FunctionToOptimizeSection
|
|||
const [actualFile, setActualFile] = useState<ParsedFile | null>(null)
|
||||
const codeContainerRef = useRef<HTMLDivElement>(null)
|
||||
|
||||
// Parse markdown code blocks
|
||||
const allFiles = useMemo(() => {
|
||||
if (!originalCode) return []
|
||||
return parseMarkdownCodeBlocks(originalCode)
|
||||
}, [originalCode])
|
||||
|
||||
// Use tree-sitter to find function location (async)
|
||||
// Search ALL files for the function definition first, then use filePath only as fallback
|
||||
useEffect(() => {
|
||||
if (!functionName || allFiles.length === 0) {
|
||||
setFunctionLocation(null)
|
||||
|
|
@ -69,7 +66,6 @@ export const FunctionToOptimizeSection = memo(function FunctionToOptimizeSection
|
|||
let cancelled = false
|
||||
|
||||
async function findFunction() {
|
||||
// 1. Search ALL files in parallel for function definition
|
||||
const searchPromises = allFiles.map(async (file) => {
|
||||
const location = await findFunctionInCode(file.code, functionName!)
|
||||
return location ? { file, location } : null
|
||||
|
|
@ -78,7 +74,6 @@ export const FunctionToOptimizeSection = memo(function FunctionToOptimizeSection
|
|||
const results = await Promise.all(searchPromises)
|
||||
if (cancelled) return
|
||||
|
||||
// Find first file that contains the function
|
||||
const found = results.find(r => r !== null)
|
||||
if (found) {
|
||||
setFunctionLocation(found.location)
|
||||
|
|
@ -86,7 +81,6 @@ export const FunctionToOptimizeSection = memo(function FunctionToOptimizeSection
|
|||
return
|
||||
}
|
||||
|
||||
// 2. Function not found - fall back to filePath match or first file
|
||||
let fallbackFile = allFiles[0]
|
||||
if (filePath) {
|
||||
const match = allFiles.find(f =>
|
||||
|
|
@ -102,10 +96,8 @@ export const FunctionToOptimizeSection = memo(function FunctionToOptimizeSection
|
|||
return () => { cancelled = true }
|
||||
}, [functionName, filePath, allFiles])
|
||||
|
||||
// The file to display - where the function was found, or fallback to first file
|
||||
const functionFile = actualFile ?? allFiles[0] ?? null
|
||||
|
||||
// Generate line numbers array from location
|
||||
const functionLines = useMemo(() => {
|
||||
if (!functionLocation) return null
|
||||
const lines: number[] = []
|
||||
|
|
@ -122,11 +114,9 @@ export const FunctionToOptimizeSection = memo(function FunctionToOptimizeSection
|
|||
if (!codeContainerRef.current) return
|
||||
const container = codeContainerRef.current
|
||||
|
||||
// Font size 0.875rem (14px) * line-height 1.6 = ~22.4px per line
|
||||
// Plus 16px padding at top
|
||||
const lineHeight = 22.4
|
||||
const paddingTop = 16
|
||||
const targetLine = functionLocation.startLine - 1 // 0-indexed
|
||||
const targetLine = functionLocation.startLine - 1
|
||||
const scrollPosition = paddingTop + (targetLine * lineHeight) - (container.clientHeight / 3)
|
||||
|
||||
container.scrollTo({
|
||||
|
|
@ -135,7 +125,6 @@ export const FunctionToOptimizeSection = memo(function FunctionToOptimizeSection
|
|||
})
|
||||
}
|
||||
|
||||
// Wait for dynamic import to load
|
||||
const timer = setTimeout(scrollToFunction, 300)
|
||||
return () => clearTimeout(timer)
|
||||
}, [isExpanded, functionLocation])
|
||||
|
|
|
|||
|
|
@ -17,9 +17,7 @@ async function getParser(): Promise<ParserType | null> {
|
|||
if (!parserPromise) {
|
||||
parserPromise = (async () => {
|
||||
try {
|
||||
// Dynamic import to avoid SSR issues
|
||||
const { Parser, Language } = await import("web-tree-sitter")
|
||||
// Initialize with WASM files from public directory
|
||||
await Parser.init({
|
||||
locateFile: (scriptName: string) => `/${scriptName}`,
|
||||
})
|
||||
|
|
@ -29,7 +27,6 @@ async function getParser(): Promise<ParserType | null> {
|
|||
return parser
|
||||
} catch (error) {
|
||||
console.error("Tree-sitter initialization failed:", error)
|
||||
// Reset so we can retry on next call
|
||||
parserPromise = null
|
||||
throw error
|
||||
}
|
||||
|
|
@ -42,7 +39,6 @@ export async function findFunctionInCode(
|
|||
code: string,
|
||||
functionName: string
|
||||
): Promise<FunctionLocation | null> {
|
||||
// Try tree-sitter first
|
||||
try {
|
||||
const parser = await getParser()
|
||||
if (parser) {
|
||||
|
|
@ -61,7 +57,6 @@ export async function findFunctionInCode(
|
|||
console.warn("Tree-sitter parse failed, trying regex fallback:", error)
|
||||
}
|
||||
|
||||
// Regex fallback: find function definition
|
||||
return findFunctionWithRegex(code, functionName)
|
||||
}
|
||||
|
||||
|
|
@ -71,7 +66,6 @@ function findFunctionWithRegex(
|
|||
): FunctionLocation | null {
|
||||
const lines = code.split("\n")
|
||||
|
||||
// Match "def function_name(" or "async def function_name("
|
||||
const defPattern = new RegExp(
|
||||
`^(\\s*)(async\\s+)?def\\s+${escapeRegex(functionName)}\\s*\\(`
|
||||
)
|
||||
|
|
@ -83,28 +77,24 @@ function findFunctionWithRegex(
|
|||
const line = lines[i]
|
||||
|
||||
if (startLine === -1) {
|
||||
// Looking for function start
|
||||
const match = line.match(defPattern)
|
||||
if (match) {
|
||||
startLine = i + 1 // 1-indexed
|
||||
startLine = i + 1
|
||||
startIndent = match[1].length
|
||||
}
|
||||
} else {
|
||||
// Looking for function end (next line at same or lower indentation that's not empty/comment)
|
||||
const trimmed = line.trim()
|
||||
if (trimmed === "" || trimmed.startsWith("#")) {
|
||||
continue // Skip empty lines and comments
|
||||
continue
|
||||
}
|
||||
|
||||
const currentIndent = line.length - line.trimStart().length
|
||||
if (currentIndent <= startIndent) {
|
||||
// Found end - previous line was the last line of the function
|
||||
return { startLine, endLine: i } // i is 0-indexed, so this is the last line
|
||||
return { startLine, endLine: i }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If we found a start but no explicit end, function goes to EOF
|
||||
if (startLine !== -1) {
|
||||
return { startLine, endLine: lines.length }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,14 +19,11 @@ import {
|
|||
import { CodeHighlighter, CODE_STYLE } from "./code-highlighter"
|
||||
import type { TimelineSection, TimelineSectionContent } from "./timeline-types"
|
||||
|
||||
// Strip markdown code block wrappers like "```python:src/file.py\n...\n```"
|
||||
function stripCodeHeader(code: string): string {
|
||||
let lines = code.split("\n")
|
||||
// Strip opening line if it matches ```language or ```language:path
|
||||
if (lines[0] && /^`{3}[a-z]*(:.*)?$/i.test(lines[0].trim())) {
|
||||
lines = lines.slice(1)
|
||||
}
|
||||
// Strip closing ``` if present
|
||||
if (lines.length > 0 && lines[lines.length - 1]?.trim() === "```") {
|
||||
lines = lines.slice(0, -1)
|
||||
}
|
||||
|
|
@ -85,10 +82,8 @@ function parseCodeBlock(rawCode: string): ParsedCodeBlock {
|
|||
return { language: "python", filename: null, path: null, code: rawCode }
|
||||
}
|
||||
|
||||
// Parse ALL markdown code blocks from multi-file content
|
||||
function parseAllCodeBlocks(markdown: string): ParsedCodeBlock[] {
|
||||
const files: ParsedCodeBlock[] = []
|
||||
// Match code blocks with optional :path - e.g., ```python:path/file.py or just ```python
|
||||
const regex = /```(\w+)(?::([^\n]+))?\n([\s\S]*?)```/g
|
||||
let match
|
||||
|
||||
|
|
@ -103,7 +98,6 @@ function parseAllCodeBlocks(markdown: string): ParsedCodeBlock[] {
|
|||
})
|
||||
}
|
||||
|
||||
// If no markdown blocks found, treat as single file
|
||||
if (files.length === 0 && markdown.trim()) {
|
||||
return [parseCodeBlock(markdown)]
|
||||
}
|
||||
|
|
@ -111,23 +105,19 @@ function parseAllCodeBlocks(markdown: string): ParsedCodeBlock[] {
|
|||
return files
|
||||
}
|
||||
|
||||
// Find matching file by path/filename
|
||||
function findMatchingFile(
|
||||
files: ParsedCodeBlock[],
|
||||
targetPath: string | null
|
||||
): ParsedCodeBlock | null {
|
||||
if (!targetPath || files.length === 0) return files[0] || null
|
||||
|
||||
// Try exact path match first
|
||||
const exactMatch = files.find(f => f.path === targetPath)
|
||||
if (exactMatch) return exactMatch
|
||||
|
||||
// Try filename match
|
||||
const targetFilename = targetPath.split("/").pop()
|
||||
const filenameMatch = files.find(f => f.filename === targetFilename)
|
||||
if (filenameMatch) return filenameMatch
|
||||
|
||||
// Try partial path match
|
||||
const partialMatch = files.find(f =>
|
||||
f.path && (targetPath.endsWith(f.path) || f.path.endsWith(targetPath))
|
||||
)
|
||||
|
|
@ -365,20 +355,16 @@ const CandidateContent = memo(function CandidateContent({
|
|||
|
||||
const originalCode = content.type === "refinement" ? content.parentCode : content.originalCode
|
||||
|
||||
// Parse ALL files from candidate and original code
|
||||
const candidateFiles = useMemo(() => parseAllCodeBlocks(content.code), [content.code])
|
||||
const originalFiles = useMemo(() => originalCode ? parseAllCodeBlocks(originalCode) : [], [originalCode])
|
||||
|
||||
// Get currently selected candidate file
|
||||
const selectedCandidateFile = candidateFiles[selectedFileIndex] || candidateFiles[0]
|
||||
|
||||
// Find matching original file by path
|
||||
const matchingOriginalFile = useMemo(() => {
|
||||
if (!selectedCandidateFile || originalFiles.length === 0) return null
|
||||
return findMatchingFile(originalFiles, selectedCandidateFile.path)
|
||||
}, [selectedCandidateFile, originalFiles])
|
||||
|
||||
// Reset diff when file selection changes
|
||||
useEffect(() => {
|
||||
setUnifiedDiff(null)
|
||||
}, [selectedFileIndex])
|
||||
|
|
@ -411,7 +397,6 @@ const CandidateContent = memo(function CandidateContent({
|
|||
const hasDiff = matchingOriginalFile !== null
|
||||
const hasMultipleFiles = candidateFiles.length > 1
|
||||
|
||||
// Memoize style objects to avoid recreating on each render
|
||||
const codeContainerStyle = useMemo(
|
||||
() => ({ maxHeight: isActive ? "70vh" : "200px" }),
|
||||
[isActive]
|
||||
|
|
@ -547,7 +532,6 @@ const RankingContent = memo(function RankingContent({ content }: { content: Extr
|
|||
</div>
|
||||
)}
|
||||
|
||||
{/* Ranked candidates - stacked vertically for full-width code review */}
|
||||
{content.rankings.length >= 1 && (
|
||||
<div className="space-y-4">
|
||||
{content.rankings.map((item) => (
|
||||
|
|
@ -724,7 +708,6 @@ export const TimelinePageView = memo(function TimelinePageView({
|
|||
|
||||
useEffect(() => {
|
||||
const handleScroll = () => {
|
||||
// Throttle with requestAnimationFrame to limit to ~60fps
|
||||
if (rafId.current !== null) return
|
||||
rafId.current = requestAnimationFrame(() => {
|
||||
rafId.current = null
|
||||
|
|
|
|||
|
|
@ -1,14 +1,10 @@
|
|||
// ============================================================================
|
||||
// Types for Timeline Page View
|
||||
// ============================================================================
|
||||
|
||||
export interface TimelineSection {
|
||||
id: string
|
||||
type: "test_generation" | "optimization" | "line_profiler" | "refinement" | "ranking" | "summary"
|
||||
title: string
|
||||
subtitle?: string
|
||||
timestamp: number // ms from trace start
|
||||
duration?: number // ms
|
||||
timestamp: number
|
||||
duration?: number
|
||||
status: "success" | "failed" | "partial" | "pending"
|
||||
model?: string | null
|
||||
cost?: number | null
|
||||
|
|
@ -72,10 +68,6 @@ export interface TransformInput {
|
|||
usedForPr: boolean
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Data Transformer
|
||||
// ============================================================================
|
||||
|
||||
export function transformToTimelineSections(input: TransformInput): { sections: TimelineSection[]; totalDuration: number } {
|
||||
const { calls, optimizationCandidates, lineProfilerCandidates, refinementCandidates, generatedTests, instrumentedTests, instrumentedPerfTests, originalCode, testFramework, candidateRankMap, bestCandidateId, rankingExplanation, usedForPr } = input
|
||||
|
||||
|
|
@ -83,7 +75,6 @@ export function transformToTimelineSections(input: TransformInput): { sections:
|
|||
return { sections: [], totalDuration: 0 }
|
||||
}
|
||||
|
||||
// Calculate base time
|
||||
const timestamps = calls.map(c => new Date(c.created_at).getTime())
|
||||
const minTime = Math.min(...timestamps)
|
||||
const maxTime = Math.max(...timestamps)
|
||||
|
|
@ -92,7 +83,6 @@ export function transformToTimelineSections(input: TransformInput): { sections:
|
|||
|
||||
const sections: TimelineSection[] = []
|
||||
|
||||
// Group tests by index - each index has generated, instrumented, and instrumentedPerf variants
|
||||
const maxTestIndex = Math.max(
|
||||
generatedTests.length,
|
||||
instrumentedTests.length,
|
||||
|
|
@ -115,7 +105,6 @@ export function transformToTimelineSections(input: TransformInput): { sections:
|
|||
}
|
||||
}
|
||||
|
||||
// Group all test generation calls into a single section
|
||||
const testCalls = calls.filter(c => c.call_type === "test_generation")
|
||||
if (testCalls.length > 0 || testGroups.length > 0) {
|
||||
const firstTestCall = testCalls[0]
|
||||
|
|
@ -149,7 +138,6 @@ export function transformToTimelineSections(input: TransformInput): { sections:
|
|||
})
|
||||
}
|
||||
|
||||
// Group calls by type and create sections
|
||||
for (const call of calls) {
|
||||
const timestamp = new Date(call.created_at).getTime() - minTime
|
||||
const callType = call.call_type || "unknown"
|
||||
|
|
@ -230,7 +218,6 @@ export function transformToTimelineSections(input: TransformInput): { sections:
|
|||
content: {
|
||||
type: "refinement",
|
||||
code: candidate.code,
|
||||
// Fall back to originalCode when parentCandidate is not found
|
||||
parentCode: parentCandidate?.code ?? originalCode,
|
||||
explanation: candidate.explanation,
|
||||
rank,
|
||||
|
|
@ -244,14 +231,13 @@ export function transformToTimelineSections(input: TransformInput): { sections:
|
|||
.sort(([, a], [, b]) => a - b)
|
||||
.map(([id]) => {
|
||||
const cand = allCandidates.find(c => c.id === id)
|
||||
if (!cand) return null // Filter out candidates not in optimizations_post
|
||||
// Generate proper label based on candidate type
|
||||
if (!cand) return null
|
||||
const source = (cand as { source?: string }).source
|
||||
const prefix = source === "REFINE" ? "Refinement" : source === "OPTIMIZE_LP" ? "LP Candidate" : "Candidate"
|
||||
return { id, rank: 0, label: `${prefix} ${cand.index}`, code: cand.code, isBest: false }
|
||||
})
|
||||
.filter((r): r is NonNullable<typeof r> => r !== null)
|
||||
.map((r, index) => ({ ...r, rank: index + 1, isBest: index === 0 })) // Re-rank sequentially, first is best
|
||||
.map((r, index) => ({ ...r, rank: index + 1, isBest: index === 0 }))
|
||||
|
||||
sections.push({
|
||||
id: call.id,
|
||||
|
|
@ -274,7 +260,6 @@ export function transformToTimelineSections(input: TransformInput): { sections:
|
|||
}
|
||||
}
|
||||
|
||||
// Sort by logical order: test_generation → optimization → line_profiler → refinement → ranking
|
||||
const typeOrder: Record<string, number> = {
|
||||
test_generation: 0,
|
||||
optimization: 1,
|
||||
|
|
@ -288,14 +273,12 @@ export function transformToTimelineSections(input: TransformInput): { sections:
|
|||
const orderA = typeOrder[a.type] ?? 99
|
||||
const orderB = typeOrder[b.type] ?? 99
|
||||
if (orderA !== orderB) return orderA - orderB
|
||||
// Within same type, sort by candidate index (extracted from title) for candidate types
|
||||
const candidateTypes = ["optimization", "line_profiler", "refinement"]
|
||||
if (candidateTypes.includes(a.type)) {
|
||||
const indexA = parseInt(a.title.match(/\d+$/)?.[0] ?? "0", 10)
|
||||
const indexB = parseInt(b.title.match(/\d+$/)?.[0] ?? "0", 10)
|
||||
return indexA - indexB
|
||||
}
|
||||
// For other types, sort by timestamp
|
||||
return a.timestamp - b.timestamp
|
||||
})
|
||||
|
||||
|
|
|
|||
|
|
@ -22,8 +22,6 @@ export function TraceSearch({ initialTraceId = "", isLoading = false, hasResults
|
|||
const trimmedId = traceId.trim()
|
||||
if (!trimmedId) return
|
||||
|
||||
// Use current searchParams without adding to dependency array
|
||||
// This preserves other query params while updating trace_id
|
||||
const params = new URLSearchParams(window.location.search)
|
||||
params.set("trace_id", trimmedId)
|
||||
router.push(`/observability?${params.toString()}`)
|
||||
|
|
|
|||
|
|
@ -8,7 +8,6 @@ const config: Config = {
|
|||
"./src/app/**/*.{js,ts,jsx,tsx,mdx}",
|
||||
],
|
||||
theme: {
|
||||
// Replace default spacing scale with 8px grid
|
||||
spacing: {
|
||||
'px': '1px',
|
||||
'0': '0',
|
||||
|
|
@ -24,7 +23,6 @@ const config: Config = {
|
|||
'9': '72px',
|
||||
'10': '80px',
|
||||
},
|
||||
// Update border radius to use token values
|
||||
borderRadius: {
|
||||
'none': '0px',
|
||||
'sm': 'var(--radius-sm)',
|
||||
|
|
@ -34,9 +32,7 @@ const config: Config = {
|
|||
'full': 'var(--radius-full)',
|
||||
},
|
||||
extend: {
|
||||
// Use zinc tokens from CSS variables for colors
|
||||
colors: {
|
||||
// Zinc color scale with alpha channel support
|
||||
zinc: {
|
||||
'50': 'rgb(var(--color-zinc-50) / <alpha-value>)',
|
||||
'100': 'rgb(var(--color-zinc-100) / <alpha-value>)',
|
||||
|
|
@ -50,7 +46,6 @@ const config: Config = {
|
|||
'900': 'rgb(var(--color-zinc-900) / <alpha-value>)',
|
||||
'950': 'rgb(var(--color-zinc-950) / <alpha-value>)',
|
||||
},
|
||||
// Semantic colors mapping to CSS variables
|
||||
background: 'rgb(var(--background) / <alpha-value>)',
|
||||
foreground: 'rgb(var(--foreground) / <alpha-value>)',
|
||||
card: {
|
||||
|
|
@ -77,7 +72,6 @@ const config: Config = {
|
|||
DEFAULT: 'rgb(var(--accent) / <alpha-value>)',
|
||||
foreground: 'rgb(var(--accent-foreground) / <alpha-value>)',
|
||||
},
|
||||
// Status colors
|
||||
error: {
|
||||
DEFAULT: 'rgb(var(--error) / <alpha-value>)',
|
||||
foreground: 'rgb(var(--error-foreground) / <alpha-value>)',
|
||||
|
|
@ -106,7 +100,6 @@ const config: Config = {
|
|||
input: 'rgb(var(--input) / <alpha-value>)',
|
||||
ring: 'rgb(var(--ring) / <alpha-value>)',
|
||||
},
|
||||
// Extend font family to use token variables
|
||||
fontFamily: {
|
||||
sans: ['var(--font-sans)'],
|
||||
mono: ['var(--font-mono)'],
|
||||
|
|
|
|||
Loading…
Reference in a new issue