11import { useMemo , useState } from 'react'
22import { format } from 'date-fns'
3- import { AlertCircle , AlertTriangle , Calendar , CheckCircle2 , Clock , Terminal } from 'lucide-react'
3+ import {
4+ AlertCircle ,
5+ AlertTriangle ,
6+ Calendar ,
7+ CheckCircle2 ,
8+ ChevronDown ,
9+ ChevronUp ,
10+ Clock ,
11+ Terminal ,
12+ } from 'lucide-react'
13+ import { Button } from '@/components/ui/button'
414import { ConsoleEntry as ConsoleEntryType } from '@/stores/panel/console/types'
515import { getBlock } from '@/blocks'
616import { JSONView } from '../json-view/json-view'
@@ -47,6 +57,7 @@ const WordWrap = ({ text }: { text: string }) => {
4757
4858export function ConsoleEntry ( { entry, consoleWidth } : ConsoleEntryProps ) {
4959 const [ isExpanded , setIsExpanded ] = useState ( false )
60+ const [ expandAllJson , setExpandAllJson ] = useState ( false )
5061
5162 const blockConfig = useMemo ( ( ) => {
5263 if ( ! entry . blockType ) return null
@@ -63,6 +74,22 @@ export function ConsoleEntry({ entry, consoleWidth }: ConsoleEntryProps) {
6374 < CheckCircle2 className = "h-4 w-4 text-muted-foreground" />
6475 )
6576
77+ // Helper function to check if data has nested objects or arrays
78+ const hasNestedStructure = ( data : any ) : boolean => {
79+ if ( data === null || typeof data !== 'object' ) return false
80+
81+ // Check if it's an empty object or array
82+ if ( Object . keys ( data ) . length === 0 ) return false
83+
84+ // For arrays, check if any element is an object
85+ if ( Array . isArray ( data ) ) {
86+ return data . some ( ( item ) => typeof item === 'object' && item !== null )
87+ }
88+
89+ // For objects, check if any value is an object
90+ return Object . values ( data ) . some ( ( value ) => typeof value === 'object' && value !== null )
91+ }
92+
6693 return (
6794 < div
6895 className = { `border-b border-border transition-colors ${
@@ -106,8 +133,37 @@ export function ConsoleEntry({ entry, consoleWidth }: ConsoleEntryProps) {
106133 { ! entry . error && ! entry . warning && (
107134 < div className = "flex items-start gap-2" >
108135 < Terminal className = "h-4 w-4 text-muted-foreground mt-1" />
109- < div className = "text-sm font-mono flex-1 break-normal whitespace-normal overflow-wrap-anywhere" >
110- < JSONView data = { entry . output } initiallyExpanded = { isExpanded } />
136+ < div className = "text-sm font-mono flex-1 break-normal whitespace-normal overflow-wrap-anywhere relative" >
137+ { typeof entry . output === 'object' &&
138+ entry . output !== null &&
139+ hasNestedStructure ( entry . output ) && (
140+ < div className = "absolute right-0 top-0 z-10" >
141+ < Button
142+ variant = "ghost"
143+ size = "sm"
144+ className = "h-6 px-2 text-muted-foreground hover:text-foreground"
145+ onClick = { ( e ) => {
146+ e . stopPropagation ( )
147+ setExpandAllJson ( ! expandAllJson )
148+ } }
149+ >
150+ < span className = "flex items-center" >
151+ { expandAllJson ? (
152+ < >
153+ < ChevronUp className = "h-3 w-3 mr-1" />
154+ < span className = "text-xs" > Collapse all</ span >
155+ </ >
156+ ) : (
157+ < >
158+ < ChevronDown className = "h-3 w-3 mr-1" />
159+ < span className = "text-xs" > Expand all</ span >
160+ </ >
161+ ) }
162+ </ span >
163+ </ Button >
164+ </ div >
165+ ) }
166+ < JSONView data = { entry . output } initiallyExpanded = { expandAllJson } />
111167 </ div >
112168 </ div >
113169 ) }
0 commit comments