Skip to content

Commit 7a50be1

Browse files
authored
feat(traces): add fold/unfold toggle button to JSON panels (#396)
## Summary - Add a fold/unfold toggle button next to each JSON panel title (Raw Request, Raw Response, etc.) on the Traces page - Clicking fold collapses all nested JSON levels while keeping the outermost `{ }` visible - Clicking again unfolds everything - Useful for quickly inspecting top-level keys in large JSON payloads (e.g. prompt structure) without scrolling through deeply nested content ## Changes - Added `editorRef` to `AccordionPanel` to hold the Monaco Editor instance via `onMount` - Added `handleToggleFold`: calls `editor.foldAll` then unfolds line 1 to keep the root object open - Toggle button uses `Minimize2`/`Maximize2` icons to indicate current state ## Test plan - [ ] Open the Traces page and click on a trace to view details - [ ] Click the fold button next to Raw Request title — inner JSON should collapse, outermost `{ }` stays visible - [ ] Click again — all JSON should unfold - [ ] Verify the copy button still works correctly
1 parent e261096 commit 7a50be1

1 file changed

Lines changed: 34 additions & 1 deletion

File tree

packages/frontend/src/pages/Debug.tsx

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import React, { useEffect, useMemo, useState } from 'react';
1+
import React, { useEffect, useMemo, useRef, useState } from 'react';
22
import { api } from '../lib/api';
33
import Editor from '@monaco-editor/react';
44
import {
@@ -13,6 +13,8 @@ import {
1313
Download,
1414
Filter,
1515
X,
16+
Minimize2,
17+
Maximize2,
1618
} from 'lucide-react';
1719
import { clsx } from 'clsx';
1820
import { Button } from '../components/ui/Button';
@@ -562,6 +564,8 @@ const AccordionPanel: React.FC<{
562564
}> = ({ title, content, color, defaultOpen = false }) => {
563565
const [isOpen, setIsOpen] = useState(defaultOpen);
564566
const [copied, setCopied] = useState(false);
567+
const [folded, setFolded] = useState(false);
568+
const editorRef = useRef<any>(null);
565569

566570
const handleCopy = async (e: React.MouseEvent) => {
567571
e.stopPropagation();
@@ -573,6 +577,25 @@ const AccordionPanel: React.FC<{
573577
}
574578
};
575579

580+
const handleToggleFold = (e: React.MouseEvent) => {
581+
e.stopPropagation();
582+
const editor = editorRef.current;
583+
if (!editor) return;
584+
if (folded) {
585+
editor.trigger('unfoldAll', 'editor.unfoldAll', null);
586+
} else {
587+
// Fold everything first
588+
editor.trigger('foldAll', 'editor.foldAll', null);
589+
// Then unfold the outermost object (line 1) to keep it visible
590+
setTimeout(() => {
591+
editor.setSelection({ startLineNumber: 1, startColumn: 1, endLineNumber: 1, endColumn: 1 });
592+
editor.trigger('unfold', 'editor.unfold', null);
593+
editor.setSelection({ startLineNumber: 0, startColumn: 0, endLineNumber: 0, endColumn: 0 });
594+
}, 50);
595+
}
596+
setFolded(!folded);
597+
};
598+
576599
return (
577600
<div className="border-b border-border-glass bg-bg-surface">
578601
<div
@@ -584,6 +607,13 @@ const AccordionPanel: React.FC<{
584607
<span className={clsx('truncate text-[11px] font-bold uppercase tracking-wider', color)}>
585608
{title}
586609
</span>
610+
<button
611+
className="bg-transparent border-0 text-text-muted p-0.5 rounded cursor-pointer transition-all duration-200 flex items-center justify-center hover:bg-white/10 hover:text-text"
612+
onClick={handleToggleFold}
613+
title={folded ? 'Unfold all' : 'Fold all'}
614+
>
615+
{folded ? <Maximize2 size={12} /> : <Minimize2 size={12} />}
616+
</button>
587617
</div>
588618
<button
589619
className="bg-transparent border-0 text-text-muted p-1 rounded cursor-pointer transition-all duration-200 flex items-center justify-center hover:bg-white/10 hover:text-text"
@@ -605,6 +635,9 @@ const AccordionPanel: React.FC<{
605635
defaultLanguage="json"
606636
theme="vs-dark"
607637
value={content}
638+
onMount={(editor) => {
639+
editorRef.current = editor;
640+
}}
608641
options={{
609642
readOnly: true,
610643
minimap: { enabled: false },

0 commit comments

Comments
 (0)