wu981526092's picture
🚀 Deploy AgentGraph: Complete agent monitoring and knowledge graph system
c2ea5ed
import React, { useMemo, useState } from "react";
import { Button } from "@/components/ui/button";
import { Badge } from "@/components/ui/badge";
import { Separator } from "@/components/ui/separator";
import { FileText, Download, Code, Type } from "lucide-react";
import { Trace } from "@/types";
import Editor from "@monaco-editor/react";
interface TraceSegmentModalProps {
data: {
trace: Trace;
segment: {
content: string;
startChar: number;
endChar: number;
windowIndex: number;
};
};
onClose: () => void;
}
export function TraceSegmentModal({
data,
onClose: _onClose,
}: TraceSegmentModalProps) {
const { trace, segment } = data;
const [viewMode, setViewMode] = useState<"editor" | "text" | "textarea">(
"textarea"
);
// Debug logging
console.log("TraceSegmentModal data:", { trace, segment });
console.log("Segment content preview:", segment.content.substring(0, 200));
// Content analysis
const lineCount = segment.content.split("\n").length;
const wordCount = segment.content.trim()
? segment.content.trim().split(/\s+/).length
: 0;
// Language detection for syntax highlighting
const detectedLanguage = useMemo(() => {
// Check for JSON content
if (
segment.content.trim().startsWith("{") &&
segment.content.trim().endsWith("}")
) {
try {
JSON.parse(segment.content);
return "json";
} catch {
// Not valid JSON, continue checking
}
}
// Check for Python code patterns
if (/^(import|from|def|class|if __name__|print\()/m.test(segment.content)) {
return "python";
}
// Check for JavaScript/TypeScript patterns
if (
/^(import|export|function|const|let|var|console\.log)/m.test(
segment.content
)
) {
return "javascript";
}
// Check for shell/bash patterns
if (
/^(#!\/bin\/|cd |ls |mkdir |rm |pip install|npm install)/m.test(
segment.content
)
) {
return "shell";
}
// Check for SQL patterns
if (
/^(SELECT|INSERT|UPDATE|DELETE|CREATE|DROP|ALTER)/im.test(segment.content)
) {
return "sql";
}
// Default to plain text
return "text";
}, [segment.content]);
const handleDownload = () => {
const blob = new Blob([segment.content], { type: "text/plain" });
const url = URL.createObjectURL(blob);
const a = document.createElement("a");
a.href = url;
a.download = `${trace.filename}_window_${segment.windowIndex}_segment.txt`;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
URL.revokeObjectURL(url);
};
return (
<div className="flex flex-col h-[70vh]">
{/* Header */}
<div className="px-6 py-4 border-b bg-muted/50">
<div className="flex items-center justify-between mb-2">
<div className="flex items-center gap-2 min-w-0 flex-1">
<FileText className="h-5 w-5 flex-shrink-0" />
<h2
className="text-lg font-semibold truncate"
title={trace.filename}
>
{trace.filename}
</h2>
<Badge variant="outline" className="flex-shrink-0">
Window {segment.windowIndex}
</Badge>
</div>
<div className="flex items-center gap-2">
<Button
variant={viewMode === "editor" ? "default" : "outline"}
size="sm"
onClick={() => setViewMode("editor")}
>
<Code className="h-4 w-4 mr-2" />
Editor
</Button>
<Button
variant={viewMode === "text" ? "default" : "outline"}
size="sm"
onClick={() => setViewMode("text")}
>
<Type className="h-4 w-4 mr-2" />
Text
</Button>
<Button
variant={viewMode === "textarea" ? "default" : "outline"}
size="sm"
onClick={() => setViewMode("textarea")}
>
<Type className="h-4 w-4 mr-2" />
Textarea
</Button>
<Button variant="outline" size="sm" onClick={handleDownload}>
<Download className="h-4 w-4 mr-2" />
Download
</Button>
</div>
</div>
{/* Segment Info */}
<div className="flex items-center gap-4 text-sm text-muted-foreground">
<span>{segment.content.length.toLocaleString()} characters</span>
<span>{lineCount.toLocaleString()} lines</span>
<span>{wordCount.toLocaleString()} words</span>
<Separator orientation="vertical" className="h-4" />
<span>
Characters {segment.startChar.toLocaleString()} -{" "}
{segment.endChar.toLocaleString()}
</span>
</div>
{/* Debug Info */}
<div className="mt-2 p-2 bg-gray-100 rounded text-xs text-gray-600">
<strong>Debug:</strong> Content length: {segment.content.length},
Preview: "{segment.content.substring(0, 50)}..."
</div>
</div>
{/* Content Area */}
<div className="flex-1 overflow-hidden bg-white border border-gray-200 rounded m-2">
{segment.content ? (
viewMode === "editor" ? (
<div className="h-full">
<Editor
height="100%"
defaultLanguage={detectedLanguage}
value={segment.content}
theme="vs"
loading={
<div className="p-4 text-center">Loading editor...</div>
}
onMount={(editor) => {
console.log("Monaco Editor mounted");
console.log(
"Editor content length:",
editor.getValue().length
);
console.log(
"Editor content preview:",
editor.getValue().substring(0, 200)
);
// Force a layout update
setTimeout(() => {
editor.layout();
}, 100);
}}
options={{
readOnly: true,
minimap: { enabled: false },
scrollBeyondLastLine: false,
wordWrap: "on",
lineNumbers: "on",
folding: false,
renderWhitespace: "selection",
fontSize: 14,
lineHeight: 20,
fontFamily:
"'Monaco', 'Menlo', 'Ubuntu Mono', 'Consolas', monospace",
padding: { top: 10, bottom: 10 },
automaticLayout: true,
contextmenu: false,
selectOnLineNumbers: true,
}}
/>
</div>
) : viewMode === "text" ? (
<div className="p-4 overflow-auto h-full">
<pre className="text-sm font-mono whitespace-pre-wrap break-words">
{segment.content}
</pre>
</div>
) : (
<div className="p-4 h-full">
<textarea
value={segment.content}
readOnly
className="w-full h-full p-2 border border-gray-300 rounded font-mono text-sm resize-none focus:outline-none focus:ring-2 focus:ring-blue-500"
placeholder="Content will appear here..."
/>
</div>
)
) : (
<div className="p-8 text-center text-gray-500">
<p>No content available for this segment</p>
<p className="text-sm mt-2">
Characters: {segment.startChar} - {segment.endChar}
</p>
</div>
)}
</div>
</div>
);
}