Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -42,11 +42,12 @@ import {
getDisplayName,
hasErrorInTree,
hasUnhandledErrorInTree,
iconColorClass,
isIterationType,
parseTime,
} from '@/app/workspace/[workspaceId]/logs/components/log-details/utils'
import { BlockTile } from '@/blocks/block-tile'
import { isCustomBlockType } from '@/blocks/custom/build-config'
import { getTileIconColorClass } from '@/blocks/icon-color'
import { useCodeViewerFeatures } from '@/hooks/use-code-viewer'

const DEFAULT_TREE_PANE_WIDTH = 240
Expand Down Expand Up @@ -331,12 +332,12 @@ const TraceTreeRow = memo(function TraceTreeRow({
<div className='size-[14px] flex-shrink-0' />
)}
{!isIterationType(span.type) && (
<div
className='flex size-[14px] flex-shrink-0 items-center justify-center overflow-hidden rounded-sm [&_img]:size-full'
style={{ background: bgColor }}
>
{BlockIcon && <BlockIcon className={cn('size-[10px]', iconColorClass(bgColor))} />}
</div>
<BlockTile
blockType={span.type?.toLowerCase() ?? ''}
icon={BlockIcon ?? undefined}
bgColor={bgColor}
size='sm'
/>
)}
<Tooltip.Root>
<Tooltip.Trigger asChild>
Expand Down Expand Up @@ -711,7 +712,9 @@ const TraceDetailPane = memo(function TraceDetailPane({ span }: { span: TraceSpa
className='mt-[2px] flex size-[18px] flex-shrink-0 items-center justify-center overflow-hidden rounded-sm [&_img]:size-full'
style={{ background: bgColor }}
>
{BlockIcon && <BlockIcon className={cn('size-[12px]', iconColorClass(bgColor))} />}
{BlockIcon && (
<BlockIcon className={cn('size-[12px]', getTileIconColorClass(bgColor))} />
)}
</div>
)}
<div className='flex min-w-0 flex-1 flex-col gap-0.5'>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,12 +86,6 @@ export function getBlockIconAndColor(
*/
const MAX_YIQ_SUM = 255_000

/** Returns 'text-white' for dark backgrounds, dark text for light ones. */
export function iconColorClass(bgColor: string): string {
const brightness = perceivedBrightness(bgColor)
return brightness !== null && brightness > 160_000 / MAX_YIQ_SUM ? 'text-[#111111]' : 'text-white'
}

/**
* Near-black bgColors disappear against the dark-mode surface (--bg: #1b1b1b).
* Below the brightness threshold we fall back to the neutral block color used
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@ import type { EnrichmentProviderOutcome, EnrichmentRunDetail } from '@/lib/table
import {
adjustBgForContrast,
getBlockIconAndColor,
iconColorClass,
} from '@/app/workspace/[workspaceId]/logs/components/log-details/utils'
import { useLogDetailsResize } from '@/app/workspace/[workspaceId]/logs/hooks'
import { formatDate } from '@/app/workspace/[workspaceId]/logs/utils'
import { getTileIconColorClass } from '@/blocks/icon-color'
import { useEnrichmentDetail } from '@/hooks/queries/tables'
import { formatCost } from '@/providers/utils'
import { useLogDetailsUIStore } from '@/stores/logs/store'
Expand Down Expand Up @@ -255,7 +255,9 @@ function EnrichmentDetailsContent({
style={{ background: bgColor }}
>
{ProviderIcon && (
<ProviderIcon className={cn('size-[11px]', iconColorClass(bgColor))} />
<ProviderIcon
className={cn('size-[11px]', getTileIconColorClass(bgColor))}
/>
)}
</div>
<span className='min-w-0 flex-1 truncate text-[var(--text-secondary)] text-caption'>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
'use client'

import type React from 'react'
import { useMemo, useState } from 'react'
import {
Button,
Expand All @@ -18,7 +17,7 @@ import {
Tooltip,
toast,
} from '@sim/emcn'
import { ArrowLeft, ChevronDown, Repeat, Split, SquareArrowUpRight, X } from '@sim/emcn/icons'
import { ArrowLeft, ChevronDown, SquareArrowUpRight, X } from '@sim/emcn/icons'
import { toError } from '@sim/utils/errors'
import { generateId } from '@sim/utils/id'
import { useMutation, useQueryClient } from '@tanstack/react-query'
Expand Down Expand Up @@ -57,8 +56,7 @@ import {
RequiredLabel,
} from '@/app/workspace/[workspaceId]/tables/[tableId]/components/sidebar-fields'
import { PreviewWorkflow } from '@/app/workspace/[workspaceId]/w/components/preview'
import { getBlock } from '@/blocks'
import { getTileIconColorClass } from '@/blocks/icon-color'
import { BlockTile } from '@/blocks/block-tile'
import {
useAddWorkflowGroup,
useUpdateColumn,
Expand Down Expand Up @@ -140,8 +138,6 @@ interface BlockOutputGroup {
blockId: string
blockName: string
blockType: string
blockIcon: string | React.ComponentType<{ className?: string }>
blockColor: string
paths: string[]
}

Expand All @@ -164,25 +160,6 @@ function tableColumnTypeToInputType(colType: ColumnDefinition['type'] | undefine
return columnTypeById(colType).workflowInputType
}

const TagIcon: React.FC<{
icon: string | React.ComponentType<{ className?: string }>
color: string
}> = ({ icon, color }) => (
<div
className='flex size-[14px] flex-shrink-0 items-center justify-center overflow-hidden rounded [&_img]:size-full'
style={{ background: color }}
>
{typeof icon === 'string' ? (
<span className={cn(getTileIconColorClass(color, true), 'font-bold text-micro')}>{icon}</span>
) : (
(() => {
const IconComponent = icon
return <IconComponent className={cn(getTileIconColorClass(color, true), 'size-[9px]')} />
})()
)}
</div>
)

/**
* Right-edge sidebar for workflow group configuration. Three flows:
* - create a new group (workflow + outputs + deps),
Expand Down Expand Up @@ -468,20 +445,10 @@ export function WorkflowSidebarBody({
for (const f of flat) {
let group = groupsByBlockId.get(f.blockId)
if (!group) {
const blockConfig = getBlock(f.blockType)
const blockColor = blockConfig?.bgColor || '#2F55FF'
let blockIcon: string | React.ComponentType<{ className?: string }> = f.blockName
.charAt(0)
.toUpperCase()
if (blockConfig?.icon) blockIcon = blockConfig.icon
else if (f.blockType === 'loop') blockIcon = Repeat
else if (f.blockType === 'parallel') blockIcon = Split
group = {
blockId: f.blockId,
blockName: f.blockName,
blockType: f.blockType,
blockIcon,
blockColor,
paths: [],
}
groupsByBlockId.set(f.blockId, group)
Expand All @@ -504,7 +471,11 @@ export function WorkflowSidebarBody({
section: group.blockName,
sectionElement: (
<div className='flex items-center gap-1.5 px-1.5 pt-1.5 pb-1'>
<TagIcon icon={group.blockIcon} color={group.blockColor} />
<BlockTile
blockType={group.blockType}
fallbackLabel={group.blockName.charAt(0).toUpperCase()}
size='sm'
/>
<span className='text-[var(--text-secondary)] text-caption'>{group.blockName}</span>
</div>
),
Expand Down
Original file line number Diff line number Diff line change
@@ -1,47 +1,18 @@
'use client'

import type React from 'react'
import { useMemo } from 'react'
import { ChipCombobox, Combobox, type ComboboxOptionGroup, cn } from '@sim/emcn'
import { Repeat, Split } from '@sim/emcn/icons'
import { useShallow } from 'zustand/react/shallow'
import {
type FlattenOutputsBlockInput,
flattenWorkflowOutputs,
} from '@/lib/workflows/blocks/flatten-outputs'
import { getBlock } from '@/blocks'
import { getTileIconColorClass } from '@/blocks/icon-color'
import { BlockTile } from '@/blocks/block-tile'
import { normalizeName } from '@/executor/constants'
import { useWorkflowDiffStore } from '@/stores/workflow-diff/store'
import { useSubBlockStore } from '@/stores/workflows/subblock/store'
import { useWorkflowStore } from '@/stores/workflows/workflow/store'

/**
* Renders a tag icon with background color for block section headers.
*
* @param icon - Either a letter string or a Lucide icon component
* @param color - Background color for the icon container
* @returns A styled icon element
*/
const TagIcon: React.FC<{
icon: string | React.ComponentType<{ className?: string }>
color: string
}> = ({ icon, color }) => (
<div
className='flex size-[14px] flex-shrink-0 items-center justify-center overflow-hidden rounded [&_img]:size-full'
style={{ background: color }}
>
{typeof icon === 'string' ? (
<span className={cn(getTileIconColorClass(color, true), 'font-bold text-micro')}>{icon}</span>
) : (
(() => {
const IconComponent = icon
return <IconComponent className={cn(getTileIconColorClass(color, true), 'size-[9px]')} />
})()
)}
</div>
)

const EMPTY_OUTPUTS: string[] = []

/**
Expand Down Expand Up @@ -200,16 +171,6 @@ export function OutputSelect({
return `${validOutputs.length} outputs`
}, [selectedOutputs, workflowOutputs, placeholder])

/**
* Gets the background color for a block output based on its type
* @param blockType - The type of the block
* @returns The hex color code for the block
*/
const getOutputColor = (blockType: string) => {
const blockConfig = getBlock(blockType)
return blockConfig?.bgColor || '#2F55FF'
}

/**
* Groups outputs by block and sorts by distance from starter block.
* Returns ComboboxOptionGroup[] for use with Combobox.
Expand Down Expand Up @@ -261,25 +222,15 @@ export function OutputSelect({

return sortedGroups.map(({ blockName, outputs }) => {
const firstOutput = outputs[0]
const blockConfig = getBlock(firstOutput.blockType)
const blockColor = getOutputColor(firstOutput.blockType)

let blockIcon: string | React.ComponentType<{ className?: string }> = blockName
.charAt(0)
.toUpperCase()

if (blockConfig?.icon) {
blockIcon = blockConfig.icon
} else if (firstOutput.blockType === 'loop') {
blockIcon = Repeat
} else if (firstOutput.blockType === 'parallel') {
blockIcon = Split
}

return {
sectionElement: (
<div className='flex items-center gap-1.5 px-1.5 py-1'>
<TagIcon icon={blockIcon} color={blockColor} />
<BlockTile
blockType={firstOutput.blockType}
fallbackLabel={blockName.charAt(0).toUpperCase()}
size='sm'
/>
<span className='text-small'>{blockName}</span>
</div>
),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -433,8 +433,9 @@ export function ConnectionBlockSelector({ id, data }: NodeProps<ConnectionBlockS
}}
icon={result.item.icon}
bgColor={result.item.bgColor}
showColoredIcon
workflowType={result.kind === 'block' ? result.item.type : undefined}
blockType={
result.kind === 'tool_operation' ? result.item.blockType : result.item.type
}
label={result.item.name}
/>
))}
Expand All @@ -453,7 +454,7 @@ export function ConnectionBlockSelector({ id, data }: NodeProps<ConnectionBlockS
onSelect={() => handleToolOperationSelect(result.item)}
icon={result.item.icon}
bgColor={result.item.bgColor}
showColoredIcon
blockType={result.item.blockType}
label={result.item.name}
/>
)
Expand All @@ -470,8 +471,7 @@ export function ConnectionBlockSelector({ id, data }: NodeProps<ConnectionBlockS
}
icon={result.item.icon}
bgColor={result.item.bgColor}
showColoredIcon
workflowType={result.kind === 'block' ? result.item.type : undefined}
blockType={result.item.type}
label={result.item.name}
/>
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

import { useCallback, useRef, useState } from 'react'
import { ChevronDown, handleKeyboardActivation } from '@sim/emcn'
import { Repeat, Split } from '@sim/emcn/icons'
import { createLogger } from '@sim/logger'
import clsx from 'clsx'
import { useShallow } from 'zustand/react/shallow'
Expand All @@ -12,8 +11,7 @@ import {
} from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/connection-blocks/components/field-item/field-item'
import type { ConnectedBlock } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/hooks/use-block-connections'
import { useBlockOutputFields } from '@/app/workspace/[workspaceId]/w/[workflowId]/hooks/use-block-output-fields'
import { getTileIconColorClass } from '@/blocks/icon-color'
import { getBlock } from '@/blocks/registry'
import { BlockTile } from '@/blocks/block-tile'
import { normalizeName } from '@/executor/constants'
import { useWorkflowRegistry } from '@/stores/workflows/registry/store'
import { EMPTY_SUBBLOCK_VALUES, useSubBlockStore } from '@/stores/workflows/subblock/store'
Expand Down Expand Up @@ -103,8 +101,6 @@ function ConnectionItem({
mergedSubBlocks,
sourceBlock,
}: ConnectionItemProps) {
const blockConfig = getBlock(connection.type)

const fields = useBlockOutputFields({
blockId: connection.id,
blockType: connection.type,
Expand All @@ -113,19 +109,6 @@ function ConnectionItem({
})
const hasFields = fields.length > 0

let Icon = blockConfig?.icon
let bgColor = blockConfig?.bgColor || '#6B7280'

if (!blockConfig) {
if (connection.type === 'loop') {
Icon = Repeat
bgColor = '#2FB3FF'
} else if (connection.type === 'parallel') {
Icon = Split
bgColor = '#FEE12B'
}
}

return (
<div className='mb-0.5 last:mb-0' ref={connectionRef}>
<div
Expand All @@ -144,21 +127,7 @@ function ConnectionItem({
handleKeyboardActivation(event, () => onToggleExpand(connection.id))
}}
>
<div
className='[&_img]:!size-full relative flex size-[14px] flex-shrink-0 items-center justify-center overflow-hidden rounded-sm'
style={{ background: bgColor }}
>
{Icon && (
<Icon
className={clsx(
'transition-transform duration-200',
getTileIconColorClass(bgColor),
hasFields && 'group-hover:scale-110',
'!size-[9px]'
)}
/>
)}
</div>
<BlockTile blockType={connection.type} size='sm' />
<span
className={clsx(
'truncate',
Expand Down
Loading
Loading