Skip to content

Commit 3d51849

Browse files
committed
improvement(terminal): ui/ux
1 parent 86653ca commit 3d51849

File tree

12 files changed

+153
-579
lines changed

12 files changed

+153
-579
lines changed

apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/terminal/components/filter-popover/filter-popover.tsx

Lines changed: 3 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,7 @@ import type {
1717
BlockInfo,
1818
TerminalFilters,
1919
} from '@/app/workspace/[workspaceId]/w/[workflowId]/components/terminal/types'
20-
import {
21-
formatRunId,
22-
getBlockIcon,
23-
getRunIdColor,
24-
} from '@/app/workspace/[workspaceId]/w/[workflowId]/components/terminal/utils'
20+
import { getBlockIcon } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/terminal/utils'
2521

2622
/**
2723
* Props for the FilterPopover component
@@ -32,10 +28,7 @@ export interface FilterPopoverProps {
3228
filters: TerminalFilters
3329
toggleStatus: (status: 'error' | 'info') => void
3430
toggleBlock: (blockId: string) => void
35-
toggleRunId: (runId: string) => void
3631
uniqueBlocks: BlockInfo[]
37-
uniqueRunIds: string[]
38-
executionColorMap: Map<string, string>
3932
hasActiveFilters: boolean
4033
}
4134

@@ -48,10 +41,7 @@ export const FilterPopover = memo(function FilterPopover({
4841
filters,
4942
toggleStatus,
5043
toggleBlock,
51-
toggleRunId,
5244
uniqueBlocks,
53-
uniqueRunIds,
54-
executionColorMap,
5545
hasActiveFilters,
5646
}: FilterPopoverProps) {
5747
return (
@@ -69,7 +59,7 @@ export const FilterPopover = memo(function FilterPopover({
6959
</Button>
7060
</PopoverTrigger>
7161
<PopoverContent
72-
side='bottom'
62+
side='top'
7363
align='end'
7464
sideOffset={4}
7565
onClick={(e) => e.stopPropagation()}
@@ -103,7 +93,7 @@ export const FilterPopover = memo(function FilterPopover({
10393

10494
{uniqueBlocks.length > 0 && (
10595
<>
106-
<PopoverDivider />
96+
<PopoverDivider className='my-[4px]' />
10797
<PopoverSection className='!mt-0'>Blocks</PopoverSection>
10898
<PopoverScrollArea className='max-h-[100px]'>
10999
{uniqueBlocks.map((block) => {
@@ -125,35 +115,6 @@ export const FilterPopover = memo(function FilterPopover({
125115
</PopoverScrollArea>
126116
</>
127117
)}
128-
129-
{uniqueRunIds.length > 0 && (
130-
<>
131-
<PopoverDivider />
132-
<PopoverSection className='!mt-0'>Run ID</PopoverSection>
133-
<PopoverScrollArea className='max-h-[100px]'>
134-
{uniqueRunIds.map((runId) => {
135-
const isSelected = filters.runIds.has(runId)
136-
const runIdColor = getRunIdColor(runId, executionColorMap)
137-
138-
return (
139-
<PopoverItem
140-
key={runId}
141-
active={isSelected}
142-
showCheck={isSelected}
143-
onClick={() => toggleRunId(runId)}
144-
>
145-
<span
146-
className='flex-1 font-mono text-[11px]'
147-
style={{ color: runIdColor || '#D2D2D2' }}
148-
>
149-
{formatRunId(runId)}
150-
</span>
151-
</PopoverItem>
152-
)
153-
})}
154-
</PopoverScrollArea>
155-
</>
156-
)}
157118
</PopoverContent>
158119
</Popover>
159120
)
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
export { FilterPopover, type FilterPopoverProps } from './filter-popover'
22
export { LogRowContextMenu, type LogRowContextMenuProps } from './log-row-context-menu'
33
export { OutputPanel, type OutputPanelProps } from './output-panel'
4+
export { RunningBadge, StatusDisplay, type StatusDisplayProps } from './status-display'
45
export { ToggleButton, type ToggleButtonProps } from './toggle-button'

apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/terminal/components/log-row-context-menu/log-row-context-menu.tsx

Lines changed: 1 addition & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -23,12 +23,9 @@ export interface LogRowContextMenuProps {
2323
filters: TerminalFilters
2424
onFilterByBlock: (blockId: string) => void
2525
onFilterByStatus: (status: 'error' | 'info') => void
26-
onFilterByRunId: (runId: string) => void
2726
onCopyRunId: (runId: string) => void
28-
onClearFilters: () => void
2927
onClearConsole: () => void
3028
onFixInCopilot: (entry: ConsoleEntry) => void
31-
hasActiveFilters: boolean
3229
}
3330

3431
/**
@@ -44,19 +41,15 @@ export const LogRowContextMenu = memo(function LogRowContextMenu({
4441
filters,
4542
onFilterByBlock,
4643
onFilterByStatus,
47-
onFilterByRunId,
4844
onCopyRunId,
49-
onClearFilters,
5045
onClearConsole,
5146
onFixInCopilot,
52-
hasActiveFilters,
5347
}: LogRowContextMenuProps) {
5448
const hasRunId = entry?.executionId != null
5549

5650
const isBlockFiltered = entry ? filters.blockIds.has(entry.blockId) : false
5751
const entryStatus = entry?.success ? 'info' : 'error'
5852
const isStatusFiltered = entry ? filters.statuses.has(entryStatus) : false
59-
const isRunIdFiltered = entry?.executionId ? filters.runIds.has(entry.executionId) : false
6053

6154
return (
6255
<Popover
@@ -127,34 +120,11 @@ export const LogRowContextMenu = memo(function LogRowContextMenu({
127120
>
128121
Filter by Status
129122
</PopoverItem>
130-
{hasRunId && (
131-
<PopoverItem
132-
showCheck={isRunIdFiltered}
133-
onClick={() => {
134-
onFilterByRunId(entry.executionId!)
135-
onClose()
136-
}}
137-
>
138-
Filter by Run ID
139-
</PopoverItem>
140-
)}
141123
</>
142124
)}
143125

144-
{/* Clear filters */}
145-
{hasActiveFilters && (
146-
<PopoverItem
147-
onClick={() => {
148-
onClearFilters()
149-
onClose()
150-
}}
151-
>
152-
Clear All Filters
153-
</PopoverItem>
154-
)}
155-
156126
{/* Destructive action */}
157-
{(entry || hasActiveFilters) && <PopoverDivider />}
127+
{entry && <PopoverDivider />}
158128
<PopoverItem
159129
onClick={() => {
160130
onClearConsole()

apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/terminal/components/output-panel/output-panel.tsx

Lines changed: 32 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ import {
99
Check,
1010
Clipboard,
1111
Database,
12-
FilterX,
1312
MoreHorizontal,
1413
Palette,
1514
Pause,
@@ -102,7 +101,6 @@ export interface OutputPanelProps {
102101
filteredEntries: ConsoleEntry[]
103102
handleExportConsole: (e: React.MouseEvent) => void
104103
hasActiveFilters: boolean
105-
clearFilters: () => void
106104
handleClearConsole: (e: React.MouseEvent) => void
107105
shouldShowCodeDisplay: boolean
108106
outputDataStringified: string
@@ -111,10 +109,7 @@ export interface OutputPanelProps {
111109
filters: TerminalFilters
112110
toggleBlock: (blockId: string) => void
113111
toggleStatus: (status: 'error' | 'info') => void
114-
toggleRunId: (runId: string) => void
115112
uniqueBlocks: BlockInfo[]
116-
uniqueRunIds: string[]
117-
executionColorMap: Map<string, string>
118113
}
119114

120115
/**
@@ -139,7 +134,6 @@ export const OutputPanel = React.memo(function OutputPanel({
139134
filteredEntries,
140135
handleExportConsole,
141136
hasActiveFilters,
142-
clearFilters,
143137
handleClearConsole,
144138
shouldShowCodeDisplay,
145139
outputDataStringified,
@@ -148,10 +142,7 @@ export const OutputPanel = React.memo(function OutputPanel({
148142
filters,
149143
toggleBlock,
150144
toggleStatus,
151-
toggleRunId,
152145
uniqueBlocks,
153-
uniqueRunIds,
154-
executionColorMap,
155146
}: OutputPanelProps) {
156147
// Access store-backed settings directly to reduce prop drilling
157148
const outputPanelWidth = useTerminalStore((state) => state.outputPanelWidth)
@@ -224,14 +215,6 @@ export const OutputPanel = React.memo(function OutputPanel({
224215
setOpenOnRun(!openOnRun)
225216
}, [openOnRun, setOpenOnRun])
226217

227-
const handleClearFiltersClick = useCallback(
228-
(e: React.MouseEvent) => {
229-
e.stopPropagation()
230-
clearFilters()
231-
},
232-
[clearFilters]
233-
)
234-
235218
const handleCopyClick = useCallback(
236219
(e: React.MouseEvent) => {
237220
e.stopPropagation()
@@ -364,10 +347,7 @@ export const OutputPanel = React.memo(function OutputPanel({
364347
filters={filters}
365348
toggleStatus={toggleStatus}
366349
toggleBlock={toggleBlock}
367-
toggleRunId={toggleRunId}
368350
uniqueBlocks={uniqueBlocks}
369-
uniqueRunIds={uniqueRunIds}
370-
executionColorMap={executionColorMap}
371351
hasActiveFilters={hasActiveFilters}
372352
/>
373353
)}
@@ -470,55 +450,38 @@ export const OutputPanel = React.memo(function OutputPanel({
470450
</Tooltip.Content>
471451
</Tooltip.Root>
472452
{filteredEntries.length > 0 && (
473-
<Tooltip.Root>
474-
<Tooltip.Trigger asChild>
475-
<Button
476-
variant='ghost'
477-
onClick={handleExportConsole}
478-
aria-label='Download console CSV'
479-
className='!p-1.5 -m-1.5'
480-
>
481-
<ArrowDownToLine className='h-3 w-3' />
482-
</Button>
483-
</Tooltip.Trigger>
484-
<Tooltip.Content>
485-
<span>Download CSV</span>
486-
</Tooltip.Content>
487-
</Tooltip.Root>
488-
)}
489-
{hasActiveFilters && (
490-
<Tooltip.Root>
491-
<Tooltip.Trigger asChild>
492-
<Button
493-
variant='ghost'
494-
onClick={handleClearFiltersClick}
495-
aria-label='Clear filters'
496-
className='!p-1.5 -m-1.5'
497-
>
498-
<FilterX className='h-3 w-3' />
499-
</Button>
500-
</Tooltip.Trigger>
501-
<Tooltip.Content>
502-
<span>Clear filters</span>
503-
</Tooltip.Content>
504-
</Tooltip.Root>
505-
)}
506-
{filteredEntries.length > 0 && (
507-
<Tooltip.Root>
508-
<Tooltip.Trigger asChild>
509-
<Button
510-
variant='ghost'
511-
onClick={handleClearConsole}
512-
aria-label='Clear console'
513-
className='!p-1.5 -m-1.5'
514-
>
515-
<Trash2 className='h-3 w-3' />
516-
</Button>
517-
</Tooltip.Trigger>
518-
<Tooltip.Content>
519-
<Tooltip.Shortcut keys='⌘D'>Clear console</Tooltip.Shortcut>
520-
</Tooltip.Content>
521-
</Tooltip.Root>
453+
<>
454+
<Tooltip.Root>
455+
<Tooltip.Trigger asChild>
456+
<Button
457+
variant='ghost'
458+
onClick={handleExportConsole}
459+
aria-label='Download console CSV'
460+
className='!p-1.5 -m-1.5'
461+
>
462+
<ArrowDownToLine className='h-3 w-3' />
463+
</Button>
464+
</Tooltip.Trigger>
465+
<Tooltip.Content>
466+
<span>Download CSV</span>
467+
</Tooltip.Content>
468+
</Tooltip.Root>
469+
<Tooltip.Root>
470+
<Tooltip.Trigger asChild>
471+
<Button
472+
variant='ghost'
473+
onClick={handleClearConsole}
474+
aria-label='Clear console'
475+
className='!p-1.5 -m-1.5'
476+
>
477+
<Trash2 className='h-3 w-3' />
478+
</Button>
479+
</Tooltip.Trigger>
480+
<Tooltip.Content>
481+
<Tooltip.Shortcut keys='⌘D'>Clear console</Tooltip.Shortcut>
482+
</Tooltip.Content>
483+
</Tooltip.Root>
484+
</>
522485
)}
523486
<Popover open={outputOptionsOpen} onOpenChange={setOutputOptionsOpen} size='sm'>
524487
<PopoverTrigger asChild>
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export { RunningBadge, StatusDisplay, type StatusDisplayProps } from './status-display'
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
'use client'
2+
3+
import { memo } from 'react'
4+
import { Badge } from '@/components/emcn'
5+
import { BADGE_STYLE } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/terminal/types'
6+
7+
/**
8+
* Running badge component - displays a consistent "Running" indicator
9+
*/
10+
export const RunningBadge = memo(function RunningBadge() {
11+
return (
12+
<Badge variant='green' className={BADGE_STYLE}>
13+
Running
14+
</Badge>
15+
)
16+
})
17+
18+
/**
19+
* Props for StatusDisplay component
20+
*/
21+
export interface StatusDisplayProps {
22+
isRunning: boolean
23+
isCanceled: boolean
24+
formattedDuration: string
25+
}
26+
27+
/**
28+
* Reusable status display for terminal rows.
29+
* Shows Running badge, 'canceled' text, or formatted duration.
30+
*/
31+
export const StatusDisplay = memo(function StatusDisplay({
32+
isRunning,
33+
isCanceled,
34+
formattedDuration,
35+
}: StatusDisplayProps) {
36+
if (isRunning) {
37+
return <RunningBadge />
38+
}
39+
if (isCanceled) {
40+
return <>canceled</>
41+
}
42+
return <>{formattedDuration}</>
43+
})

apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/terminal/hooks/use-output-panel-resize.ts

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,7 @@
11
import { useCallback, useEffect, useState } from 'react'
2-
import { OUTPUT_PANEL_WIDTH } from '@/stores/constants'
2+
import { OUTPUT_PANEL_WIDTH, TERMINAL_BLOCK_COLUMN_WIDTH } from '@/stores/constants'
33
import { useTerminalStore } from '@/stores/terminal'
44

5-
const BLOCK_COLUMN_WIDTH = 240
6-
75
export function useOutputPanelResize() {
86
const setOutputPanelWidth = useTerminalStore((state) => state.setOutputPanelWidth)
97
const [isResizing, setIsResizing] = useState(false)
@@ -25,7 +23,7 @@ export function useOutputPanelResize() {
2523

2624
const newWidth = window.innerWidth - e.clientX - panelWidth
2725
const terminalWidth = window.innerWidth - sidebarWidth - panelWidth
28-
const maxWidth = terminalWidth - BLOCK_COLUMN_WIDTH
26+
const maxWidth = terminalWidth - TERMINAL_BLOCK_COLUMN_WIDTH
2927
const clampedWidth = Math.max(OUTPUT_PANEL_WIDTH.MIN, Math.min(newWidth, maxWidth))
3028

3129
setOutputPanelWidth(clampedWidth)

0 commit comments

Comments
 (0)