From 36d78e47423a03c0cbf34e9e8b8618fd852c9f58 Mon Sep 17 00:00:00 2001 From: Theodore Li Date: Wed, 22 Jul 2026 20:22:52 -0700 Subject: [PATCH] fix(custom-blocks): stop image icons blowing up on class-sized surfaces --- .../menus/suggestion-menu-chrome.ts | 5 +++-- .../components/agent-group/tool-call-item.tsx | 8 +------- .../components/trace-view/trace-view.tsx | 4 ++-- .../workflow-sidebar/workflow-sidebar.tsx | 2 +- .../output-select/output-select.tsx | 2 +- .../connection-blocks/connection-blocks.tsx | 4 ++-- .../components/tag-dropdown/tag-dropdown.tsx | 2 +- .../components/tool-input/tool-input.tsx | 2 +- .../panel/components/editor/editor.tsx | 2 +- .../panel/components/toolbar/toolbar.tsx | 2 +- .../components/terminal/terminal.tsx | 6 +++--- .../preview-editor/preview-editor.tsx | 2 +- .../components/block/block.tsx | 2 +- .../command-items/command-items.tsx | 2 +- apps/sim/blocks/custom/custom-block-icon.tsx | 20 ++++++++++--------- .../workflow-block/workflow-block-view.tsx | 2 +- 16 files changed, 32 insertions(+), 35 deletions(-) diff --git a/apps/sim/app/workspace/[workspaceId]/files/components/file-viewer/rich-markdown-editor/menus/suggestion-menu-chrome.ts b/apps/sim/app/workspace/[workspaceId]/files/components/file-viewer/rich-markdown-editor/menus/suggestion-menu-chrome.ts index c468042a18d..4b33f7de8aa 100644 --- a/apps/sim/app/workspace/[workspaceId]/files/components/file-viewer/rich-markdown-editor/menus/suggestion-menu-chrome.ts +++ b/apps/sim/app/workspace/[workspaceId]/files/components/file-viewer/rich-markdown-editor/menus/suggestion-menu-chrome.ts @@ -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 ``, 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 = diff --git a/apps/sim/app/workspace/[workspaceId]/home/components/message-content/components/agent-group/tool-call-item.tsx b/apps/sim/app/workspace/[workspaceId]/home/components/message-content/components/agent-group/tool-call-item.tsx index 2b94fcff9b4..f5d50141d1f 100644 --- a/apps/sim/app/workspace/[workspaceId]/home/components/message-content/components/agent-group/tool-call-item.tsx +++ b/apps/sim/app/workspace/[workspaceId]/home/components/message-content/components/agent-group/tool-call-item.tsx @@ -107,13 +107,7 @@ export function ToolCallItem({ return (
{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. - + )} {isExecuting ? ( diff --git a/apps/sim/app/workspace/[workspaceId]/logs/components/log-details/components/trace-view/trace-view.tsx b/apps/sim/app/workspace/[workspaceId]/logs/components/log-details/components/trace-view/trace-view.tsx index 9ea1131717c..6315cc4e304 100644 --- a/apps/sim/app/workspace/[workspaceId]/logs/components/log-details/components/trace-view/trace-view.tsx +++ b/apps/sim/app/workspace/[workspaceId]/logs/components/log-details/components/trace-view/trace-view.tsx @@ -332,7 +332,7 @@ const TraceTreeRow = memo(function TraceTreeRow({ )} {!isIterationType(span.type) && (
{BlockIcon && } @@ -707,7 +707,7 @@ const TraceDetailPane = memo(function TraceDetailPane({ span }: { span: TraceSpa
{!isIterationType(span.type) && (
{BlockIcon && } diff --git a/apps/sim/app/workspace/[workspaceId]/tables/[tableId]/components/workflow-sidebar/workflow-sidebar.tsx b/apps/sim/app/workspace/[workspaceId]/tables/[tableId]/components/workflow-sidebar/workflow-sidebar.tsx index 8392000e51a..b6702c721f8 100644 --- a/apps/sim/app/workspace/[workspaceId]/tables/[tableId]/components/workflow-sidebar/workflow-sidebar.tsx +++ b/apps/sim/app/workspace/[workspaceId]/tables/[tableId]/components/workflow-sidebar/workflow-sidebar.tsx @@ -178,7 +178,7 @@ const TagIcon: React.FC<{ color: string }> = ({ icon, color }) => (
{typeof icon === 'string' ? ( diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/chat/components/output-select/output-select.tsx b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/chat/components/output-select/output-select.tsx index a4a0b8fd1f9..c6c9ee01ec6 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/chat/components/output-select/output-select.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/chat/components/output-select/output-select.tsx @@ -28,7 +28,7 @@ const TagIcon: React.FC<{ color: string }> = ({ icon, color }) => (
{typeof icon === 'string' ? ( diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/connection-blocks/connection-blocks.tsx b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/connection-blocks/connection-blocks.tsx index 7899d1cf8fd..d28620e2a38 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/connection-blocks/connection-blocks.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/connection-blocks/connection-blocks.tsx @@ -145,7 +145,7 @@ function ConnectionItem({ }} >
{Icon && ( @@ -154,7 +154,7 @@ function ConnectionItem({ 'transition-transform duration-200', getTileIconColorClass(bgColor), hasFields && 'group-hover:scale-110', - '!h-[9px] !w-[9px]' + '!size-[9px]' )} /> )} diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/tag-dropdown/tag-dropdown.tsx b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/tag-dropdown/tag-dropdown.tsx index 6f62851c289..cd8b9df42cf 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/tag-dropdown/tag-dropdown.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/tag-dropdown/tag-dropdown.tsx @@ -387,7 +387,7 @@ const TagIcon: React.FC<{ color: string }> = ({ icon, color }) => (
{typeof icon === 'string' ? ( diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/tool-input/tool-input.tsx b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/tool-input/tool-input.tsx index 3cfcb3fe3e8..0b34f353382 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/tool-input/tool-input.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/tool-input/tool-input.tsx @@ -408,7 +408,7 @@ function createToolIcon( ) { return (
diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/editor.tsx b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/editor.tsx index bd9558e6789..e88f9aa79d1 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/editor.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/editor.tsx @@ -373,7 +373,7 @@ export function Editor() {
{(blockConfig || isSubflow) && currentBlock?.type !== 'note' && (
{Icon && ( diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/terminal/terminal.tsx b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/terminal/terminal.tsx index 7d36f6a97bf..6d945a8bcc2 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/terminal/terminal.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/terminal/terminal.tsx @@ -124,7 +124,7 @@ const BlockRow = memo(function BlockRow({ >
{BlockIcon && ( @@ -300,7 +300,7 @@ const SubflowNodeRow = memo(function SubflowNodeRow({ >
{BlockIcon && ( @@ -427,7 +427,7 @@ const WorkflowNodeRow = memo(function WorkflowNodeRow({ >
{BlockIcon && ( diff --git a/apps/sim/app/workspace/[workspaceId]/w/components/preview/components/preview-editor/preview-editor.tsx b/apps/sim/app/workspace/[workspaceId]/w/components/preview/components/preview-editor/preview-editor.tsx index 1c095a9c4c1..d287c7a94a8 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/components/preview/components/preview-editor/preview-editor.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/components/preview/components/preview-editor/preview-editor.tsx @@ -1198,7 +1198,7 @@ function PreviewEditorContent({
{block.type !== 'note' && (
{!isNoteBlock && (
() + /** * Build a `BlockIcon` from an uploaded icon image URL. Rendered as an `` 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() - 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) => ( )) // double-cast-allowed: an renderer must satisfy the SVG-typed BlockIcon slot diff --git a/packages/workflow-renderer/src/workflow-block/workflow-block-view.tsx b/packages/workflow-renderer/src/workflow-block/workflow-block-view.tsx index 2ee141b10b2..82ce02c9d05 100644 --- a/packages/workflow-renderer/src/workflow-block/workflow-block-view.tsx +++ b/packages/workflow-renderer/src/workflow-block/workflow-block-view.tsx @@ -212,7 +212,7 @@ export function WorkflowBlockView({ >