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 @@ -14,9 +14,10 @@ export const SUGGESTION_SURFACE_CLASS =
*/
export const SUGGESTION_SCROLL_CLASS = 'max-h-[240px] scroll-py-1.5 overflow-y-auto overscroll-none'

/** A selectable row: icon + label, 14px icon in `--text-icon`, truncating label. */
/** A selectable row: icon + label, 14px icon in `--text-icon`, truncating label. The `img` rules
* size custom-block image icons (rendered as `<img>`, so the `svg` rules never reach them). */
export const SUGGESTION_ITEM_CLASS =
'relative flex w-full min-w-0 cursor-pointer select-none items-center gap-2 rounded-[5px] px-2 py-1.5 text-left font-medium text-[var(--text-body)] text-caption outline-none transition-colors [&>span]:min-w-0 [&>span]:truncate [&_svg]:pointer-events-none [&_svg]:size-[14px] [&_svg]:shrink-0 [&_svg]:text-[var(--text-icon)]'
'relative flex w-full min-w-0 cursor-pointer select-none items-center gap-2 rounded-[5px] px-2 py-1.5 text-left font-medium text-[var(--text-body)] text-caption outline-none transition-colors [&>span]:min-w-0 [&>span]:truncate [&_svg]:pointer-events-none [&_svg]:size-[14px] [&_svg]:shrink-0 [&_svg]:text-[var(--text-icon)] [&_img]:pointer-events-none [&_img]:size-[14px] [&_img]:shrink-0'

/** A group heading above a run of rows. */
export const SUGGESTION_GROUP_LABEL_CLASS =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -107,13 +107,7 @@ export function ToolCallItem({
return (
<div className='flex items-center gap-[6px] pl-6'>
{BlockIcon && (
// Size via inline style: a custom block's image icon carries a trailing
// `size-full` that defeats size *classes* (it fills tiled surfaces), so a
// class-only size renders the uploaded icon at natural size here.
<BlockIcon
className='size-[14px] flex-shrink-0'
style={{ width: 14, height: 14, ...getBareIconStyle(BlockIcon) }}
/>
<BlockIcon className='size-[14px] flex-shrink-0' style={getBareIconStyle(BlockIcon)} />
)}
{isExecuting ? (
<ShimmerText className='text-[13px] [--shimmer-rest:var(--text-secondary)]'>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -332,7 +332,7 @@ const TraceTreeRow = memo(function TraceTreeRow({
)}
{!isIterationType(span.type) && (
<div
className='flex size-[14px] flex-shrink-0 items-center justify-center overflow-hidden rounded-sm'
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))} />}
Expand Down Expand Up @@ -707,7 +707,7 @@ const TraceDetailPane = memo(function TraceDetailPane({ span }: { span: TraceSpa
<div className='flex items-start gap-2'>
{!isIterationType(span.type) && (
<div
className='mt-[2px] flex size-[18px] flex-shrink-0 items-center justify-center rounded-sm'
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))} />}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,7 @@ const TagIcon: React.FC<{
color: string
}> = ({ icon, color }) => (
<div
className='flex size-[14px] flex-shrink-0 items-center justify-center rounded'
className='flex size-[14px] flex-shrink-0 items-center justify-center overflow-hidden rounded [&_img]:size-full'
style={{ background: color }}
>
{typeof icon === 'string' ? (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ const TagIcon: React.FC<{
color: string
}> = ({ icon, color }) => (
<div
className='flex size-[14px] flex-shrink-0 items-center justify-center rounded'
className='flex size-[14px] flex-shrink-0 items-center justify-center overflow-hidden rounded [&_img]:size-full'
style={{ background: color }}
>
{typeof icon === 'string' ? (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@ function ConnectionItem({
}}
>
<div
className='relative flex size-[14px] flex-shrink-0 items-center justify-center overflow-hidden rounded-sm'
className='[&_img]:!size-full relative flex size-[14px] flex-shrink-0 items-center justify-center overflow-hidden rounded-sm'
style={{ background: bgColor }}
>
{Icon && (
Expand All @@ -154,7 +154,7 @@ function ConnectionItem({
'transition-transform duration-200',
getTileIconColorClass(bgColor),
hasFields && 'group-hover:scale-110',
'!h-[9px] !w-[9px]'
'!size-[9px]'
)}
/>
)}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -387,7 +387,7 @@ const TagIcon: React.FC<{
color: string
}> = ({ icon, color }) => (
<div
className='flex size-[14px] flex-shrink-0 items-center justify-center rounded'
className='flex size-[14px] flex-shrink-0 items-center justify-center overflow-hidden rounded [&_img]:size-full'
style={{ background: color }}
>
{typeof icon === 'string' ? (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -408,7 +408,7 @@ function createToolIcon(
) {
return (
<div
className='flex size-[16px] flex-shrink-0 items-center justify-center rounded-sm'
className='flex size-[16px] flex-shrink-0 items-center justify-center overflow-hidden rounded-sm [&_img]:size-full'
style={{ background: bgColor }}
>
<IconComponent className={cn('size-[10px]', getTileIconColorClass(bgColor))} />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -373,7 +373,7 @@ export function Editor() {
<div className='flex min-w-0 flex-1 items-center gap-2'>
{(blockConfig || isSubflow) && currentBlock?.type !== 'note' && (
<div
className='flex size-[18px] items-center justify-center rounded-sm'
className='flex size-[18px] items-center justify-center overflow-hidden rounded-sm [&_img]:size-full'
style={{ background: isSubflow ? subflowConfig?.bgColor : blockConfig?.bgColor }}
>
<IconComponent
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ const ToolbarItem = memo(function ToolbarItem({
onKeyDown={handleKeyDown}
>
<div
className='relative flex size-[16px] flex-shrink-0 items-center justify-center overflow-hidden rounded-sm'
className='relative flex size-[16px] flex-shrink-0 items-center justify-center overflow-hidden rounded-sm [&_img]:size-full'
style={{ background: item.bgColor }}
>
{Icon && (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ const BlockRow = memo(function BlockRow({
>
<div className='flex min-w-0 flex-1 items-center gap-2'>
<div
className='flex size-[16px] flex-shrink-0 items-center justify-center rounded-sm'
className='flex size-[16px] flex-shrink-0 items-center justify-center overflow-hidden rounded-sm [&_img]:size-full'
style={{ background: bgColor }}
>
{BlockIcon && (
Expand Down Expand Up @@ -300,7 +300,7 @@ const SubflowNodeRow = memo(function SubflowNodeRow({
>
<div className='flex min-w-0 flex-1 items-center gap-2'>
<div
className='flex size-[16px] flex-shrink-0 items-center justify-center rounded-sm'
className='flex size-[16px] flex-shrink-0 items-center justify-center overflow-hidden rounded-sm [&_img]:size-full'
style={{ background: bgColor }}
>
{BlockIcon && (
Expand Down Expand Up @@ -427,7 +427,7 @@ const WorkflowNodeRow = memo(function WorkflowNodeRow({
>
<div className='flex min-w-0 flex-1 items-center gap-2'>
<div
className='flex size-[16px] flex-shrink-0 items-center justify-center rounded-sm'
className='flex size-[16px] flex-shrink-0 items-center justify-center overflow-hidden rounded-sm [&_img]:size-full'
style={{ background: bgColor }}
>
{BlockIcon && (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1198,7 +1198,7 @@ function PreviewEditorContent({
<div className='mx-[-1px] flex flex-shrink-0 items-center gap-2 rounded-b-[4px] border-[var(--border)] border-x border-b bg-[var(--surface-4)] px-3 py-1.5'>
{block.type !== 'note' && (
<div
className='flex size-[18px] flex-shrink-0 items-center justify-center rounded-sm'
className='flex size-[18px] flex-shrink-0 items-center justify-center overflow-hidden rounded-sm [&_img]:size-full'
style={{ backgroundColor: blockConfig.bgColor }}
>
<IconComponent
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -373,7 +373,7 @@ function WorkflowPreviewBlockInner({ data }: NodeProps<WorkflowPreviewBlockData>
<div className='relative z-10 flex min-w-0 flex-1 items-center gap-2.5'>
{!isNoteBlock && (
<div
className='flex size-[24px] flex-shrink-0 items-center justify-center rounded-md'
className='flex size-[24px] flex-shrink-0 items-center justify-center overflow-hidden rounded-md [&_img]:size-full'
style={{ background: enabled ? blockConfig.bgColor : 'gray' }}
>
<IconComponent
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ export const MemoizedCommandItem = memo(
return (
<Command.Item value={value} onSelect={onSelect} className={COMMAND_ITEM_CLASSNAME}>
<div
className='relative flex size-[16px] flex-shrink-0 items-center justify-center overflow-hidden rounded-sm'
className='relative flex size-[16px] flex-shrink-0 items-center justify-center overflow-hidden rounded-sm [&_img]:size-full'
style={{ background: showColoredIcon ? bgColor : 'transparent' }}
>
<Icon
Expand Down
20 changes: 11 additions & 9 deletions apps/sim/blocks/custom/custom-block-icon.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,30 +5,32 @@ import { cn } from '@sim/emcn'
import { Box } from 'lucide-react'
import type { BlockIcon } from '@/blocks/types'

const cache = new Map<string, BlockIcon>()

/**
* Build a `BlockIcon` from an uploaded icon image URL. Rendered as an `<img>` so
* any uploaded PNG/JPEG/SVG works; `className` (size) is forwarded like every
* other block icon. Cached by URL so the component reference stays stable across
* the many tiles/nodes that render a custom block.
*/
const cache = new Map<string, BlockIcon>()

export function makeImageIcon(url: string): BlockIcon {
const cached = cache.get(url)
if (cached) return cached

// Fill the tile so an uploaded image/logo reads at the same footprint as other
// blocks' colored tiles, instead of a small glyph floating in a transparent
// square. Trailing `size-full` beats a consumer size *class* (twMerge keeps the
// last of a conflict group) so a tiled surface (canvas/toolbar/palette) fills;
// it loses to a consumer inline `style` (specificity) so a tile-less inline
// surface that sizes via `style={{ width, height }}` still renders at its px.
/**
* `size-full` is only the DEFAULT (fills a fixed-size tile parent when no size
* is given); a consumer size class or inline style always wins, so flow
* surfaces that render icons at `size-[14px]` get exactly that. Tiled surfaces
* (canvas node, toolbar, search modal, …) pass a glyph-size class but want the
* image to fill the tile — they opt in with `[&_img]:size-full` on the fixed
* wrapper, which out-specifies the size class on the img.
*/
const ImageComponent = memo(({ className, style }: SVGProps<SVGSVGElement>) => (
<img
src={url}
alt=''
style={style}
className={cn('rounded-[4px] object-contain', className, 'size-full')}
className={cn('size-full rounded-[4px] object-contain', className)}
/>
))
// double-cast-allowed: an <img> renderer must satisfy the SVG-typed BlockIcon slot
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -212,7 +212,7 @@ export function WorkflowBlockView({
>
<div className='relative z-10 flex min-w-0 flex-1 items-center gap-2.5'>
<div
className='flex size-[24px] flex-shrink-0 items-center justify-center rounded-md'
className='flex size-[24px] flex-shrink-0 items-center justify-center overflow-hidden rounded-md [&_img]:size-full'
style={{
background: isEnabled ? iconBgColor : 'gray',
}}
Expand Down
Loading