improvement: code subblock, action bar, connections (#2024)

* improvement: action bar, connections

* fix: code block draggable resize
This commit is contained in:
Emir Karabeg
2025-11-17 11:04:41 -08:00
committed by GitHub
parent 7e3e38a6f2
commit e37b01b92c
4 changed files with 27 additions and 94 deletions

View File

@@ -74,10 +74,10 @@ export const ActionBar = memo(
return (
<div
className={cn(
'-right-20 absolute top-0',
'flex flex-col items-center',
'-top-[46px] absolute right-0',
'flex flex-row items-center',
'opacity-0 transition-opacity duration-200 group-hover:opacity-100',
'gap-[6px] rounded-[10px] bg-[var(--surface-3)] p-[6px]'
'gap-[5px] rounded-[10px] bg-[var(--surface-3)] p-[5px]'
)}
>
<Tooltip.Root>
@@ -90,17 +90,17 @@ export const ActionBar = memo(
collaborativeToggleBlockEnabled(blockId)
}
}}
className='h-[30px] w-[30px] rounded-[8px] bg-[var(--surface-9)] p-0 text-[#868686] hover:bg-[var(--brand-secondary)] hover:text-[var(--bg)] dark:text-[#868686] dark:hover:bg-[var(--brand-secondary)] dark:hover:text-[var(--bg)]'
className='h-[23px] w-[23px] rounded-[8px] bg-[var(--surface-9)] p-0 text-[#868686] hover:bg-[var(--brand-secondary)] hover:text-[var(--bg)] dark:text-[#868686] dark:hover:bg-[var(--brand-secondary)] dark:hover:text-[var(--bg)]'
disabled={disabled}
>
{isEnabled ? (
<Circle className='h-[14px] w-[14px]' />
<Circle className='h-[11px] w-[11px]' />
) : (
<CircleOff className='h-[14px] w-[14px]' />
<CircleOff className='h-[11px] w-[11px]' />
)}
</Button>
</Tooltip.Trigger>
<Tooltip.Content side='right'>
<Tooltip.Content side='top'>
{getTooltipMessage(isEnabled ? 'Disable Block' : 'Enable Block')}
</Tooltip.Content>
</Tooltip.Root>
@@ -116,13 +116,13 @@ export const ActionBar = memo(
collaborativeDuplicateBlock(blockId)
}
}}
className='h-[30px] w-[30px] rounded-[8px] bg-[var(--surface-9)] p-0 text-[#868686] hover:bg-[var(--brand-secondary)] hover:text-[var(--bg)] dark:text-[#868686] dark:hover:bg-[var(--brand-secondary)] dark:hover:text-[var(--bg)]'
className='h-[23px] w-[23px] rounded-[8px] bg-[var(--surface-9)] p-0 text-[#868686] hover:bg-[var(--brand-secondary)] hover:text-[var(--bg)] dark:text-[#868686] dark:hover:bg-[var(--brand-secondary)] dark:hover:text-[var(--bg)]'
disabled={disabled}
>
<Duplicate className='h-[14px] w-[14px]' />
<Duplicate className='h-[11px] w-[11px]' />
</Button>
</Tooltip.Trigger>
<Tooltip.Content side='right'>{getTooltipMessage('Duplicate Block')}</Tooltip.Content>
<Tooltip.Content side='top'>{getTooltipMessage('Duplicate Block')}</Tooltip.Content>
</Tooltip.Root>
)}
@@ -139,15 +139,13 @@ export const ActionBar = memo(
)
}
}}
className='h-[30px] w-[30px] rounded-[8px] bg-[var(--surface-9)] p-0 text-[#868686] hover:bg-[var(--brand-secondary)] hover:text-[var(--bg)] dark:text-[#868686] dark:hover:bg-[var(--brand-secondary)] dark:hover:text-[var(--bg)]'
className='h-[23px] w-[23px] rounded-[8px] bg-[var(--surface-9)] p-0 text-[#868686] hover:bg-[var(--brand-secondary)] hover:text-[var(--bg)] dark:text-[#868686] dark:hover:bg-[var(--brand-secondary)] dark:hover:text-[var(--bg)]'
disabled={disabled || !userPermissions.canEdit}
>
<LogOut className='h-[14px] w-[14px]' />
<LogOut className='h-[11px] w-[11px]' />
</Button>
</Tooltip.Trigger>
<Tooltip.Content side='right'>
{getTooltipMessage('Remove From Subflow')}
</Tooltip.Content>
<Tooltip.Content side='top'>{getTooltipMessage('Remove from Subflow')}</Tooltip.Content>
</Tooltip.Root>
)}
@@ -161,17 +159,17 @@ export const ActionBar = memo(
collaborativeToggleBlockHandles(blockId)
}
}}
className='h-[30px] w-[30px] rounded-[8px] bg-[var(--surface-9)] p-0 text-[#868686] hover:bg-[var(--brand-secondary)] hover:text-[var(--bg)] dark:text-[#868686] dark:hover:bg-[var(--brand-secondary)] dark:hover:text-[var(--bg)]'
className='h-[23px] w-[23px] rounded-[8px] bg-[var(--surface-9)] p-0 text-[#868686] hover:bg-[var(--brand-secondary)] hover:text-[var(--bg)] dark:text-[#868686] dark:hover:bg-[var(--brand-secondary)] dark:hover:text-[var(--bg)]'
disabled={disabled}
>
{horizontalHandles ? (
<ArrowLeftRight className='h-[14px] w-[14px]' />
<ArrowLeftRight className='h-[11px] w-[11px]' />
) : (
<ArrowUpDown className='h-[14px] w-[14px]' />
<ArrowUpDown className='h-[11px] w-[11px]' />
)}
</Button>
</Tooltip.Trigger>
<Tooltip.Content side='right'>
<Tooltip.Content side='top'>
{getTooltipMessage(horizontalHandles ? 'Vertical Ports' : 'Horizontal Ports')}
</Tooltip.Content>
</Tooltip.Root>
@@ -186,13 +184,13 @@ export const ActionBar = memo(
collaborativeRemoveBlock(blockId)
}
}}
className='h-[30px] w-[30px] rounded-[8px] bg-[var(--surface-9)] p-0 text-[#868686] hover:bg-[var(--brand-secondary)] hover:text-[var(--bg)] dark:text-[#868686] dark:hover:bg-[var(--brand-secondary)] dark:hover:text-[var(--bg)] '
className='h-[23px] w-[23px] rounded-[8px] bg-[var(--surface-9)] p-0 text-[#868686] hover:bg-[var(--brand-secondary)] hover:text-[var(--bg)] dark:text-[#868686] dark:hover:bg-[var(--brand-secondary)] dark:hover:text-[var(--bg)] '
disabled={disabled}
>
<Trash2 className='h-[14px] w-[14px]' />
<Trash2 className='h-[11px] w-[11px]' />
</Button>
</Tooltip.Trigger>
<Tooltip.Content side='right'>{getTooltipMessage('Delete Block')}</Tooltip.Content>
<Tooltip.Content side='top'>{getTooltipMessage('Delete Block')}</Tooltip.Content>
</Tooltip.Root>
</div>
)

View File

@@ -1,84 +1,23 @@
import { RepeatIcon, SplitIcon } from 'lucide-react'
import {
type ConnectedBlock,
useBlockConnections,
} from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel-new/components/editor/hooks/use-block-connections'
import { getBlock } from '@/blocks'
import { useBlockConnections } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel-new/components/editor/hooks/use-block-connections'
interface ConnectionsProps {
blockId: string
horizontalHandles: boolean
}
/**
* Retrieves the icon component for a given connection block
* @param connection - The connected block to get the icon for
* @returns The icon component or null if not found
* Displays incoming connections at the bottom left of the workflow block
*/
function getConnectionIcon(connection: ConnectedBlock) {
const blockConfig = getBlock(connection.type)
if (blockConfig?.icon) {
return blockConfig.icon
}
if (connection.type === 'loop') {
return RepeatIcon
}
if (connection.type === 'parallel') {
return SplitIcon
}
return null
}
/**
* Displays incoming connections as compact floating text above the workflow block
*/
export function Connections({ blockId, horizontalHandles }: ConnectionsProps) {
export function Connections({ blockId }: ConnectionsProps) {
const { incomingConnections, hasIncomingConnections } = useBlockConnections(blockId)
if (!hasIncomingConnections) return null
const connectionCount = incomingConnections.length
const maxVisibleIcons = 4
const visibleConnections = incomingConnections.slice(0, maxVisibleIcons)
const remainingCount = connectionCount - maxVisibleIcons
const connectionText = `${connectionCount} ${connectionCount === 1 ? 'connection' : 'connections'}`
const connectionIcons = (
<>
{visibleConnections.map((connection: ConnectedBlock) => {
const Icon = getConnectionIcon(connection)
if (!Icon) return null
return (
<Icon key={connection.id} className='h-[14px] w-[14px] text-[var(--text-tertiary)]' />
)
})}
{remainingCount > 0 && (
<span className='text-[14px] text-[var(--text-tertiary)]'>+{remainingCount}</span>
)}
</>
)
if (!horizontalHandles) {
return (
<div className='-translate-x-full -translate-y-1/2 pointer-events-none absolute top-1/2 left-0 flex flex-col items-end gap-[8px] pr-[8px] opacity-0 transition-opacity group-hover:opacity-100'>
<span className='text-[14px] text-[var(--text-tertiary)] leading-[14px]'>
{connectionText}
</span>
<div className='flex items-center justify-end gap-[4px]'>{connectionIcons}</div>
</div>
)
}
return (
<div className='pointer-events-none absolute bottom-full left-0 ml-[8px] flex items-center gap-[8px] pb-[8px] opacity-0 transition-opacity group-hover:opacity-100'>
<span className='text-[14px] text-[var(--text-tertiary)]'>{connectionText}</span>
<div className='h-[14px] w-[1px] bg-[var(--text-tertiary)]' />
<div className='flex items-center gap-[4px]'>{connectionIcons}</div>
<div className='pointer-events-none absolute top-full left-0 ml-[8px] flex items-center gap-[8px] pt-[8px] opacity-0 transition-opacity group-hover:opacity-100'>
<span className='text-[12px] text-[var(--text-tertiary)]'>{connectionText}</span>
</div>
)
}

View File

@@ -723,9 +723,7 @@ export const WorkflowBlock = memo(function WorkflowBlock({
<ActionBar blockId={id} blockType={type} disabled={!userPermissions.canEdit} />
{shouldShowDefaultHandles && (
<Connections blockId={id} horizontalHandles={horizontalHandles} />
)}
{shouldShowDefaultHandles && <Connections blockId={id} />}
{shouldShowDefaultHandles && (
<Handle

View File

@@ -81,9 +81,7 @@ function Container({
'bg-[#1F1F1F] font-medium font-mono text-sm transition-colors',
'dark:border-[var(--border-strong)]',
// Overflow handling for long content
'overflow-x-auto',
// Vertical resize handle
'resize-y overflow-y-auto',
'overflow-x-auto overflow-y-auto',
// Streaming state
isStreaming && 'streaming-effect',
className