Skip to content

Commit b813bf7

Browse files
improvement: workflow, blocks, preview, avatars, output-select (#2840)
* improvement(workflow): ui/ux, refactors, optimizations * improvement: blocks, preview, avatars * improvement(output-select): ui * update API endpoint picker to match output selector * improvement: subflow ui/ux --------- Co-authored-by: waleed <walif6@gmail.com>
1 parent 81cc88b commit b813bf7

File tree

16 files changed

+420
-616
lines changed

16 files changed

+420
-616
lines changed

apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/workflow-block/components/action-bar/action-bar.tsx renamed to apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/action-bar/action-bar.tsx

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,7 @@ export const ActionBar = memo(
100100
const isStartBlock = blockType === 'starter' || blockType === 'start_trigger'
101101
const isResponseBlock = blockType === 'response'
102102
const isNoteBlock = blockType === 'note'
103+
const isSubflowBlock = blockType === 'loop' || blockType === 'parallel'
103104

104105
/**
105106
* Get appropriate tooltip message based on disabled state
@@ -125,7 +126,7 @@ export const ActionBar = memo(
125126
'dark:border-transparent dark:bg-[var(--surface-4)]'
126127
)}
127128
>
128-
{!isNoteBlock && (
129+
{!isNoteBlock && !isSubflowBlock && (
129130
<Tooltip.Root>
130131
<Tooltip.Trigger asChild>
131132
<Button
@@ -148,7 +149,7 @@ export const ActionBar = memo(
148149
</Tooltip.Root>
149150
)}
150151

151-
{!isStartBlock && !isResponseBlock && (
152+
{!isStartBlock && !isResponseBlock && !isSubflowBlock && (
152153
<Tooltip.Root>
153154
<Tooltip.Trigger asChild>
154155
<Button
@@ -169,7 +170,7 @@ export const ActionBar = memo(
169170
</Tooltip.Root>
170171
)}
171172

172-
{!isNoteBlock && (
173+
{!isNoteBlock && !isSubflowBlock && (
173174
<Tooltip.Root>
174175
<Tooltip.Trigger asChild>
175176
<Button

apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/chat/components/output-select/output-select.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -331,13 +331,16 @@ export function OutputSelect({
331331
return (
332332
<Combobox
333333
size='sm'
334-
className='!w-fit !py-[2px] [&>svg]:!ml-[4px] [&>svg]:!h-3 [&>svg]:!w-3 [&>span]:!text-[var(--text-secondary)] min-w-[100px] rounded-[6px] bg-transparent px-[9px] hover:bg-[var(--surface-5)] dark:hover:border-[var(--surface-6)] dark:hover:bg-transparent [&>span]:text-center'
334+
className='!w-fit !py-[2px] min-w-[100px] rounded-[6px] px-[9px]'
335335
groups={comboboxGroups}
336336
options={[]}
337337
multiSelect
338338
multiSelectValues={normalizedSelectedValues}
339339
onMultiSelectChange={onOutputSelect}
340340
placeholder={selectedDisplayText}
341+
overlayContent={
342+
<span className='truncate text-[var(--text-primary)]'>{selectedDisplayText}</span>
343+
}
341344
disabled={disabled || workflowOutputs.length === 0}
342345
align={align}
343346
maxHeight={maxHeight}

apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/note-block/note-block.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,13 @@ import type { NodeProps } from 'reactflow'
44
import remarkGfm from 'remark-gfm'
55
import { cn } from '@/lib/core/utils/cn'
66
import { useUserPermissionsContext } from '@/app/workspace/[workspaceId]/providers/workspace-permissions-provider'
7+
import { ActionBar } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/action-bar/action-bar'
78
import { useBlockVisual } from '@/app/workspace/[workspaceId]/w/[workflowId]/hooks'
89
import {
910
BLOCK_DIMENSIONS,
1011
useBlockDimensions,
1112
} from '@/app/workspace/[workspaceId]/w/[workflowId]/hooks/use-block-dimensions'
1213
import { useSubBlockStore } from '@/stores/workflows/subblock/store'
13-
import { ActionBar } from '../workflow-block/components'
1414
import type { WorkflowBlockProps } from '../workflow-block/types'
1515

1616
interface NoteBlockNodeData extends WorkflowBlockProps {}

apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/deploy/components/deploy-modal/components/api/api.tsx

Lines changed: 14 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,12 @@
33
import { useState } from 'react'
44
import { Check, Clipboard } from 'lucide-react'
55
import {
6-
Badge,
76
Button,
87
ButtonGroup,
98
ButtonGroupItem,
109
Code,
10+
Combobox,
1111
Label,
12-
Popover,
13-
PopoverContent,
14-
PopoverItem,
15-
PopoverTrigger,
1612
Tooltip,
1713
} from '@/components/emcn'
1814
import { Skeleton } from '@/components/ui'
@@ -602,48 +598,19 @@ console.log(limits);`
602598
<span>{copied.async ? 'Copied' : 'Copy'}</span>
603599
</Tooltip.Content>
604600
</Tooltip.Root>
605-
<Popover>
606-
<PopoverTrigger asChild>
607-
<div className='min-w-0 max-w-full'>
608-
<Badge
609-
variant='outline'
610-
className='flex-none cursor-pointer whitespace-nowrap rounded-[6px]'
611-
>
612-
<span className='whitespace-nowrap text-[12px]'>
613-
{getAsyncExampleTitle()}
614-
</span>
615-
</Badge>
616-
</div>
617-
</PopoverTrigger>
618-
<PopoverContent
619-
side='bottom'
620-
align='end'
621-
sideOffset={4}
622-
maxHeight={300}
623-
maxWidth={300}
624-
minWidth={160}
625-
border
626-
>
627-
<PopoverItem
628-
active={asyncExampleType === 'execute'}
629-
onClick={() => setAsyncExampleType('execute')}
630-
>
631-
Execute Job
632-
</PopoverItem>
633-
<PopoverItem
634-
active={asyncExampleType === 'status'}
635-
onClick={() => setAsyncExampleType('status')}
636-
>
637-
Check Status
638-
</PopoverItem>
639-
<PopoverItem
640-
active={asyncExampleType === 'rate-limits'}
641-
onClick={() => setAsyncExampleType('rate-limits')}
642-
>
643-
Rate Limits
644-
</PopoverItem>
645-
</PopoverContent>
646-
</Popover>
601+
<Combobox
602+
size='sm'
603+
className='!w-fit !py-[2px] min-w-[100px] rounded-[6px] px-[9px]'
604+
options={[
605+
{ label: 'Execute Job', value: 'execute' },
606+
{ label: 'Check Status', value: 'status' },
607+
{ label: 'Rate Limits', value: 'rate-limits' },
608+
]}
609+
value={asyncExampleType}
610+
onChange={(value) => setAsyncExampleType(value as AsyncExampleType)}
611+
align='end'
612+
dropdownWidth={160}
613+
/>
647614
</div>
648615
</div>
649616
<Code.Viewer

apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/deploy/components/deploy-modal/components/general/general.tsx

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -334,7 +334,6 @@ export function GeneralDeploy({
334334
}}
335335
onPaneClick={() => setExpandedSelectedBlockId(null)}
336336
selectedBlockId={expandedSelectedBlockId}
337-
lightweight
338337
/>
339338
</div>
340339
{expandedSelectedBlockId && workflowToShow.blocks?.[expandedSelectedBlockId] && (

apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/subflows/subflow-node.tsx

Lines changed: 16 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
import { memo, useMemo, useRef } from 'react'
22
import { RepeatIcon, SplitIcon } from 'lucide-react'
33
import { Handle, type NodeProps, Position, useReactFlow } from 'reactflow'
4-
import { Button, Trash } from '@/components/emcn'
54
import { cn } from '@/lib/core/utils/cn'
65
import { HANDLE_POSITIONS } from '@/lib/workflows/blocks/block-dimensions'
76
import { type DiffStatus, hasDiffStatus } from '@/lib/workflows/diff/types'
7+
import { useUserPermissionsContext } from '@/app/workspace/[workspaceId]/providers/workspace-permissions-provider'
8+
import { ActionBar } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/action-bar/action-bar'
89
import { useCurrentWorkflow } from '@/app/workspace/[workspaceId]/w/[workflowId]/hooks'
9-
import { useCollaborativeWorkflow } from '@/hooks/use-collaborative-workflow'
1010
import { usePanelEditorStore } from '@/stores/panel'
1111

1212
/**
@@ -18,11 +18,16 @@ import { usePanelEditorStore } from '@/stores/panel'
1818
const SubflowNodeStyles: React.FC = () => {
1919
return (
2020
<style jsx global>{`
21-
/* Z-index management for subflow nodes */
21+
/* Z-index management for subflow nodes - default behind blocks */
2222
.workflow-container .react-flow__node-subflowNode {
2323
z-index: -1 !important;
2424
}
2525
26+
/* Selected subflows appear above other subflows but below blocks (z-21) */
27+
.workflow-container .react-flow__node-subflowNode:has([data-subflow-selected='true']) {
28+
z-index: 10 !important;
29+
}
30+
2631
/* Drag-over states */
2732
.loop-node-drag-over,
2833
.parallel-node-drag-over {
@@ -63,8 +68,8 @@ export interface SubflowNodeData {
6368
*/
6469
export const SubflowNodeComponent = memo(({ data, id }: NodeProps<SubflowNodeData>) => {
6570
const { getNodes } = useReactFlow()
66-
const { collaborativeBatchRemoveBlocks } = useCollaborativeWorkflow()
6771
const blockRef = useRef<HTMLDivElement>(null)
72+
const userPermissions = useUserPermissionsContext()
6873

6974
const currentWorkflow = useCurrentWorkflow()
7075
const currentBlock = currentWorkflow.getBlockById(id)
@@ -80,6 +85,8 @@ export const SubflowNodeComponent = memo(({ data, id }: NodeProps<SubflowNodeDat
8085
const currentBlockId = usePanelEditorStore((state) => state.currentBlockId)
8186
const isFocused = currentBlockId === id
8287

88+
const isPreviewSelected = data?.isPreviewSelected || false
89+
8390
/**
8491
* Calculate the nesting level of this subflow node based on its parent hierarchy.
8592
* Used to apply appropriate styling for nested containers.
@@ -125,8 +132,6 @@ export const SubflowNodeComponent = memo(({ data, id }: NodeProps<SubflowNodeDat
125132
return { top: `${HANDLE_POSITIONS.DEFAULT_Y_OFFSET}px`, transform: 'translateY(-50%)' }
126133
}
127134

128-
const isPreviewSelected = data?.isPreviewSelected || false
129-
130135
/**
131136
* Determine the ring styling based on subflow state priority:
132137
* 1. Focused (selected in editor) or preview selected - blue ring
@@ -162,7 +167,12 @@ export const SubflowNodeComponent = memo(({ data, id }: NodeProps<SubflowNodeDat
162167
data-node-id={id}
163168
data-type='subflowNode'
164169
data-nesting-level={nestingLevel}
170+
data-subflow-selected={isFocused || isPreviewSelected}
165171
>
172+
{!isPreview && (
173+
<ActionBar blockId={id} blockType={data.kind} disabled={!userPermissions.canEdit} />
174+
)}
175+
166176
{/* Header Section */}
167177
<div
168178
className={cn(
@@ -180,18 +190,6 @@ export const SubflowNodeComponent = memo(({ data, id }: NodeProps<SubflowNodeDat
180190
{blockName}
181191
</span>
182192
</div>
183-
{!isPreview && (
184-
<Button
185-
variant='ghost'
186-
onClick={(e) => {
187-
e.stopPropagation()
188-
collaborativeBatchRemoveBlocks([id])
189-
}}
190-
className='h-[14px] w-[14px] p-0 opacity-0 transition-opacity duration-100 group-hover:opacity-100'
191-
>
192-
<Trash className='h-[14px] w-[14px]' />
193-
</Button>
194-
)}
195193
</div>
196194

197195
{!isPreview && (

apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/workflow-block/components/connections/connections.tsx

Lines changed: 0 additions & 23 deletions
This file was deleted.

apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/workflow-block/components/index.ts

Lines changed: 0 additions & 2 deletions
This file was deleted.

apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/workflow-block/workflow-block.tsx

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,7 @@ import { getBaseUrl } from '@/lib/core/utils/urls'
99
import { createMcpToolId } from '@/lib/mcp/utils'
1010
import { getProviderIdFromServiceId } from '@/lib/oauth'
1111
import { useUserPermissionsContext } from '@/app/workspace/[workspaceId]/providers/workspace-permissions-provider'
12-
import {
13-
ActionBar,
14-
Connections,
15-
} from '@/app/workspace/[workspaceId]/w/[workflowId]/components/workflow-block/components'
12+
import { ActionBar } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/action-bar/action-bar'
1613
import {
1714
useBlockProperties,
1815
useChildWorkflow,
@@ -934,8 +931,6 @@ export const WorkflowBlock = memo(function WorkflowBlock({
934931
<ActionBar blockId={id} blockType={type} disabled={!userPermissions.canEdit} />
935932
)}
936933

937-
{shouldShowDefaultHandles && <Connections blockId={id} />}
938-
939934
{shouldShowDefaultHandles && (
940935
<Handle
941936
type='target'

apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/workflow-controls/workflow-controls.tsx

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -126,24 +126,24 @@ export function WorkflowControls() {
126126
</PopoverTrigger>
127127
<Tooltip.Content side='top'>{mode === 'hand' ? 'Mover' : 'Pointer'}</Tooltip.Content>
128128
</Tooltip.Root>
129-
<PopoverContent align='center' side='top' sideOffset={8} maxWidth={100} minWidth={100}>
129+
<PopoverContent side='top' sideOffset={8} maxWidth={100} minWidth={100}>
130130
<PopoverItem
131131
onClick={() => {
132-
setMode('cursor')
132+
setMode('hand')
133133
setIsCanvasModeOpen(false)
134134
}}
135135
>
136-
<Cursor className='h-3 w-3' />
137-
<span>Pointer</span>
136+
<Hand className='h-3 w-3' />
137+
<span>Mover</span>
138138
</PopoverItem>
139139
<PopoverItem
140140
onClick={() => {
141-
setMode('hand')
141+
setMode('cursor')
142142
setIsCanvasModeOpen(false)
143143
}}
144144
>
145-
<Hand className='h-3 w-3' />
146-
<span>Mover</span>
145+
<Cursor className='h-3 w-3' />
146+
<span>Pointer</span>
147147
</PopoverItem>
148148
</PopoverContent>
149149
</Popover>

0 commit comments

Comments
 (0)