Skip to content

Commit d87edd3

Browse files
committed
improvement: finshed console (audio display not working)
1 parent 0e9cf2f commit d87edd3

File tree

4 files changed

+378
-397
lines changed

4 files changed

+378
-397
lines changed

apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/console/components/console-entry/console-entry.tsx

Lines changed: 145 additions & 146 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,6 @@
1-
import { useMemo, useState } from 'react'
1+
import { useEffect, useMemo, useState } from 'react'
22
import { format } from 'date-fns'
3-
import {
4-
AlertCircle,
5-
AlertTriangle,
6-
Calendar,
7-
ChevronDown,
8-
ChevronUp,
9-
Clock,
10-
Terminal,
11-
} from 'lucide-react'
3+
import { AlertCircle, Check, ChevronDown, ChevronUp, Copy } from 'lucide-react'
124
import { Button } from '@/components/ui/button'
135
import { getBlock } from '@/blocks'
146
import type { ConsoleEntry as ConsoleEntryType } from '@/stores/panel/console/types'
@@ -19,168 +11,175 @@ interface ConsoleEntryProps {
1911
consoleWidth: number
2012
}
2113

22-
// Maximum character length for a word before it's broken up
23-
const MAX_WORD_LENGTH = 25
24-
25-
const WordWrap = ({ text }: { text: string }) => {
26-
if (!text) return null
27-
28-
// Split text into words, keeping spaces and punctuation
29-
const parts = text.split(/(\s+)/g)
30-
31-
return (
32-
<>
33-
{parts.map((part, index) => {
34-
// If the part is whitespace or shorter than the max length, render it as is
35-
if (part.match(/\s+/) || part.length <= MAX_WORD_LENGTH) {
36-
return <span key={index}>{part}</span>
37-
}
38-
39-
// For long words, break them up into chunks
40-
const chunks = []
41-
for (let i = 0; i < part.length; i += MAX_WORD_LENGTH) {
42-
chunks.push(part.substring(i, i + MAX_WORD_LENGTH))
43-
}
44-
45-
return (
46-
<span key={index} className='break-all'>
47-
{chunks.map((chunk, chunkIndex) => (
48-
<span key={chunkIndex}>{chunk}</span>
49-
))}
50-
</span>
51-
)
52-
})}
53-
</>
54-
)
55-
}
56-
5714
export function ConsoleEntry({ entry, consoleWidth }: ConsoleEntryProps) {
58-
const [isExpanded, setIsExpanded] = useState(false)
59-
const [expandAllJson, setExpandAllJson] = useState(false)
15+
const [isExpanded, setIsExpanded] = useState(true) // Default expanded
16+
const [showCopySuccess, setShowCopySuccess] = useState(false)
6017

6118
const blockConfig = useMemo(() => {
6219
if (!entry.blockType) return null
6320
return getBlock(entry.blockType)
6421
}, [entry.blockType])
6522

66-
const BlockIcon = blockConfig?.icon
67-
68-
// Helper function to check if data has nested objects or arrays
69-
const hasNestedStructure = (data: any): boolean => {
70-
if (data === null || typeof data !== 'object') return false
71-
72-
// Check if it's an empty object or array
73-
if (Object.keys(data).length === 0) return false
23+
const handleCopy = () => {
24+
const stringified = JSON.stringify(entry.output, null, 2)
25+
navigator.clipboard.writeText(stringified)
26+
setShowCopySuccess(true)
27+
}
7428

75-
// For arrays, check if any element is an object
76-
if (Array.isArray(data)) {
77-
return data.some((item) => typeof item === 'object' && item !== null)
29+
useEffect(() => {
30+
if (showCopySuccess) {
31+
const timer = setTimeout(() => {
32+
setShowCopySuccess(false)
33+
}, 2000)
34+
return () => clearTimeout(timer)
7835
}
36+
}, [showCopySuccess])
7937

80-
// For objects, check if any value is an object
81-
return Object.values(data).some((value) => typeof value === 'object' && value !== null)
82-
}
38+
const BlockIcon = blockConfig?.icon
39+
const blockColor = blockConfig?.bgColor || '#6B7280'
8340

8441
return (
85-
<div
86-
className={`border-border border-b transition-colors ${
87-
!entry.error && !entry.warning && entry.success ? 'cursor-pointer hover:bg-accent/50' : ''
88-
}`}
89-
onClick={() => !entry.error && !entry.warning && entry.success && setIsExpanded(!isExpanded)}
90-
>
91-
<div className='space-y-4 p-4'>
42+
<div className='space-y-3'>
43+
{/* Header: Icon | Block name */}
44+
<div className='flex items-center gap-2'>
45+
{BlockIcon && (
46+
<div
47+
className='flex h-5 w-5 items-center justify-center rounded-md'
48+
style={{ backgroundColor: blockColor }}
49+
>
50+
<BlockIcon className='h-3 w-3 text-white' />
51+
</div>
52+
)}
53+
<span className='font-normal text-base text-sm leading-normal'>
54+
{entry.blockName || 'Unknown Block'}
55+
</span>
56+
</div>
57+
58+
{/* Duration tag | Time tag */}
59+
<div className='flex items-center gap-2'>
9260
<div
93-
className={`${
94-
consoleWidth >= 400 ? 'flex items-center justify-between' : 'grid grid-cols-1 gap-4'
61+
className={`flex h-5 items-center rounded-lg px-2 ${
62+
entry.error ? 'bg-[#F6D2D2] dark:bg-[#442929]' : 'bg-secondary'
9563
}`}
9664
>
97-
{entry.blockName && (
98-
<div className='flex items-center gap-2 text-sm'>
99-
{BlockIcon ? (
100-
<BlockIcon className='h-4 w-4 text-muted-foreground' />
101-
) : (
102-
<Terminal className='h-4 w-4 text-muted-foreground' />
103-
)}
104-
<span className='text-muted-foreground'>{entry.blockName}</span>
65+
{entry.error ? (
66+
<div className='flex items-center gap-1'>
67+
<AlertCircle className='h-3 w-3 text-[#DC2626] dark:text-[#F87171]' />
68+
<span className='font-normal text-[#DC2626] text-xs leading-normal dark:text-[#F87171]'>
69+
Error
70+
</span>
10571
</div>
72+
) : (
73+
<span className='font-normal text-muted-foreground text-xs leading-normal'>
74+
{entry.durationMs ?? 0}ms
75+
</span>
10676
)}
107-
<div
108-
className={`${
109-
consoleWidth >= 400 ? 'flex gap-4' : 'grid grid-cols-2 gap-4'
110-
} text-muted-foreground text-sm`}
111-
>
112-
<div className='flex items-center gap-2'>
113-
<Calendar className='h-4 w-4' />
114-
<span>{entry.startedAt ? format(new Date(entry.startedAt), 'HH:mm:ss') : 'N/A'}</span>
115-
</div>
116-
<div className='flex items-center gap-2'>
117-
<Clock className='h-4 w-4' />
118-
<span>Duration: {entry.durationMs ?? 0}ms</span>
119-
</div>
120-
</div>
12177
</div>
78+
<div className='flex h-5 items-center rounded-lg bg-secondary px-2'>
79+
<span className='font-normal text-muted-foreground text-xs leading-normal'>
80+
{entry.startedAt ? format(new Date(entry.startedAt), 'HH:mm:ss') : 'N/A'}
81+
</span>
82+
</div>
83+
</div>
12284

123-
<div className='space-y-4'>
124-
{!entry.error && !entry.warning && (
125-
<div className='flex items-start gap-2'>
126-
<Terminal className='mt-1 h-4 w-4 text-muted-foreground' />
127-
<div className='overflow-wrap-anywhere relative flex-1 whitespace-normal break-normal font-mono text-sm'>
128-
{entry.output != null && (
129-
<div className='absolute top-0 right-0 z-10'>
130-
<Button
131-
variant='ghost'
132-
size='sm'
133-
className='h-6 px-2 text-muted-foreground hover:text-foreground'
134-
onClick={(e) => {
135-
e.stopPropagation()
136-
setExpandAllJson(!expandAllJson)
137-
}}
138-
>
139-
<span className='flex items-center'>
140-
{expandAllJson ? (
141-
<>
142-
<ChevronUp className='mr-1 h-3 w-3' />
143-
<span className='text-xs'>Collapse</span>
144-
</>
145-
) : (
146-
<>
147-
<ChevronDown className='mr-1 h-3 w-3' />
148-
<span className='text-xs'>Expand</span>
149-
</>
150-
)}
151-
</span>
152-
</Button>
153-
</div>
154-
)}
155-
<JSONView data={entry.output} initiallyExpanded={expandAllJson} />
156-
</div>
85+
{/* Response area */}
86+
<div className='space-y-2'>
87+
{/* Error display */}
88+
{entry.error && (
89+
<div className='rounded-lg bg-[#F6D2D2] p-3 dark:bg-[#442929]'>
90+
<div className='whitespace-pre-wrap font-normal text-[#DC2626] text-sm leading-normal dark:text-[#F87171]'>
91+
{entry.error}
15792
</div>
158-
)}
93+
</div>
94+
)}
15995

160-
{entry.error && (
161-
<div className='flex items-start gap-2 rounded-md border border-red-500 bg-red-50 p-3 text-destructive dark:border-border dark:bg-background dark:text-foreground'>
162-
<AlertCircle className='mt-1 h-4 w-4 flex-shrink-0 text-red-500' />
163-
<div className='min-w-0 flex-1'>
164-
<div className='font-medium'>Error</div>
165-
<div className='w-full overflow-hidden whitespace-pre-wrap text-sm'>
166-
<WordWrap text={entry.error} />
96+
{/* Warning display */}
97+
{entry.warning && (
98+
<div className='rounded-lg border-yellow-200 bg-yellow-50 p-3 dark:border-yellow-800/50'>
99+
<div className='mb-1 font-normal text-sm text-yellow-800 leading-normal dark:text-yellow-200'>
100+
Warning
101+
</div>
102+
<div className='whitespace-pre-wrap font-normal text-sm text-yellow-700 leading-normal dark:text-yellow-300'>
103+
{entry.warning}
104+
</div>
105+
</div>
106+
)}
107+
108+
{/* Success output */}
109+
{!entry.error && !entry.warning && entry.output != null && (
110+
<div className='rounded-lg bg-secondary/50 p-3'>
111+
{isExpanded ? (
112+
<div className='relative'>
113+
{/* Copy and Expand/Collapse buttons */}
114+
<div className='absolute top-0 right-0 z-10 flex items-center gap-1'>
115+
<Button
116+
variant='ghost'
117+
size='sm'
118+
className='h-6 w-6 p-0 hover:bg-transparent'
119+
onClick={handleCopy}
120+
>
121+
{showCopySuccess ? (
122+
<Check className='h-3 w-3 text-gray-500' />
123+
) : (
124+
<Copy className='h-3 w-3 text-muted-foreground' />
125+
)}
126+
</Button>
127+
<Button
128+
variant='ghost'
129+
size='sm'
130+
className='h-6 w-6 p-0 hover:bg-transparent'
131+
onClick={() => setIsExpanded(!isExpanded)}
132+
>
133+
<ChevronUp className='h-3 w-3 text-muted-foreground' />
134+
</Button>
135+
</div>
136+
<div className='overflow-hidden pr-16 font-mono font-normal text-muted-foreground text-sm leading-normal'>
137+
<JSONView data={entry.output} />
167138
</div>
168139
</div>
169-
</div>
170-
)}
171-
172-
{entry.warning && (
173-
<div className='flex items-start gap-2 rounded-md border border-yellow-500 bg-yellow-50 p-3 text-yellow-700 dark:border-border dark:bg-background dark:text-yellow-500'>
174-
<AlertTriangle className='mt-1 h-4 w-4 flex-shrink-0 text-yellow-500' />
175-
<div className='min-w-0 flex-1'>
176-
<div className='font-medium'>Warning</div>
177-
<div className='w-full overflow-hidden whitespace-pre-wrap text-sm'>
178-
<WordWrap text={entry.warning} />
140+
) : (
141+
<div className='relative'>
142+
<div className='absolute top-0 right-0 z-10 flex items-center gap-1'>
143+
<Button
144+
variant='ghost'
145+
size='sm'
146+
className='h-6 w-6 p-0 hover:bg-transparent'
147+
onClick={handleCopy}
148+
>
149+
{showCopySuccess ? (
150+
<Check className='h-3 w-3 text-gray-500' />
151+
) : (
152+
<Copy className='h-3 w-3 text-muted-foreground' />
153+
)}
154+
</Button>
155+
<Button
156+
variant='ghost'
157+
size='sm'
158+
className='h-6 w-6 p-0 hover:bg-transparent'
159+
onClick={() => setIsExpanded(!isExpanded)}
160+
>
161+
<ChevronDown className='h-3 w-3 text-muted-foreground' />
162+
</Button>
163+
</div>
164+
<div
165+
className='cursor-pointer pr-16 font-mono font-normal text-muted-foreground text-sm leading-normal'
166+
onClick={() => setIsExpanded(true)}
167+
>
168+
{'{...}'}
179169
</div>
180170
</div>
171+
)}
172+
</div>
173+
)}
174+
175+
{/* No output message */}
176+
{!entry.error && !entry.warning && entry.output == null && (
177+
<div className='rounded-lg bg-secondary/50 p-3'>
178+
<div className='text-center font-normal text-muted-foreground text-sm leading-normal'>
179+
No output
181180
</div>
182-
)}
183-
</div>
181+
</div>
182+
)}
184183
</div>
185184
</div>
186185
)

0 commit comments

Comments
 (0)