diff --git a/apps/sim/app/workspace/[workspaceId]/settings/components/mcp/mcp.tsx b/apps/sim/app/workspace/[workspaceId]/settings/components/mcp/mcp.tsx index 488a0ef206c..3a04667501f 100644 --- a/apps/sim/app/workspace/[workspaceId]/settings/components/mcp/mcp.tsx +++ b/apps/sim/app/workspace/[workspaceId]/settings/components/mcp/mcp.tsx @@ -1,7 +1,16 @@ 'use client' import { useEffect, useRef, useState } from 'react' -import { Badge, Button, Chip, ChipConfirmModal, cn, Tooltip, toast } from '@sim/emcn' +import { + Badge, + Chip, + ChipConfirmModal, + CollapsibleCard, + cn, + OverflowText, + Tooltip, + toast, +} from '@sim/emcn' import { ArrowLeft, ChevronDown, Plus } from '@sim/emcn/icons' import { createLogger } from '@sim/logger' import { getErrorMessage } from '@sim/utils/errors' @@ -535,59 +544,53 @@ export function MCP() { const requiredParams = tool.inputSchema?.required || [] return ( -
- - + + } + badge={ + <> + {issues.length > 0 && ( + + + + + {getIssueBadgeLabel(issues[0].issue)} + + + + + Update in: {affectedWorkflows.join(', ')} + + + )} + {hasParams && ( + + )} + + } + > {isExpanded && hasParams && ( -
-

+ <> +

Parameters

@@ -631,9 +634,9 @@ export function MCP() { } )}
-
+ )} -
+ ) })} diff --git a/apps/sim/app/workspace/[workspaceId]/settings/components/workflow-mcp-servers/workflow-mcp-servers.tsx b/apps/sim/app/workspace/[workspaceId]/settings/components/workflow-mcp-servers/workflow-mcp-servers.tsx index c0bbd87f022..62348517213 100644 --- a/apps/sim/app/workspace/[workspaceId]/settings/components/workflow-mcp-servers/workflow-mcp-servers.tsx +++ b/apps/sim/app/workspace/[workspaceId]/settings/components/workflow-mcp-servers/workflow-mcp-servers.tsx @@ -18,6 +18,7 @@ import { ChipSelect, Code, type ComboboxOption, + FieldCard, Label, useCopyToClipboard, } from '@sim/emcn' @@ -713,36 +714,29 @@ function ServerDetailView({ return hasParams ? (
{Object.entries(properties).map(([name, prop]) => ( -
+ {prop.type || 'any'} + + } > -
-
- - {name} - - - {prop.type || 'any'} - -
-
-
-
- - - setEditingParameterDescriptions((prev) => ({ - ...prev, - [name]: e.target.value, - })) - } - placeholder={`Enter description for ${name}`} - /> -
+
+ + + setEditingParameterDescriptions((prev) => ({ + ...prev, + [name]: e.target.value, + })) + } + placeholder={`Enter description for ${name}`} + />
-
+ ))}
) : ( diff --git a/apps/sim/app/workspace/[workspaceId]/tables/[tableId]/components/table-grid/data-row.tsx b/apps/sim/app/workspace/[workspaceId]/tables/[tableId]/components/table-grid/data-row.tsx index 0c603a6e1dc..5321fbf0140 100644 --- a/apps/sim/app/workspace/[workspaceId]/tables/[tableId]/components/table-grid/data-row.tsx +++ b/apps/sim/app/workspace/[workspaceId]/tables/[tableId]/components/table-grid/data-row.tsx @@ -287,11 +287,11 @@ export const DataRow = React.memo(function DataRow({ {hasWorkflowColumns && ( @@ -270,7 +265,7 @@ export function DocumentTagEntry({ variant='ghost-destructive' onClick={() => removeTag(tag.id)} disabled={isReadOnly} - className='h-auto p-0' + size='icon' > Delete Tag diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/eval-input/eval-input.tsx b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/eval-input/eval-input.tsx index 652f79b6e3a..a8c87277a31 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/eval-input/eval-input.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/eval-input/eval-input.tsx @@ -147,7 +147,7 @@ export function EvalInput({ variant='ghost' onClick={addMetric} disabled={isPreview || disabled} - className='h-auto p-0' + size='icon' > Add Metric @@ -162,7 +162,7 @@ export function EvalInput({ variant='ghost-destructive' onClick={() => removeMetric(metric.id)} disabled={isPreview || disabled || metrics.length === 1} - className='h-auto p-0' + size='icon' > Delete Metric diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/field-mode-toggle/field-mode-toggle.tsx b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/field-mode-toggle/field-mode-toggle.tsx new file mode 100644 index 00000000000..e7916f529d2 --- /dev/null +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/field-mode-toggle/field-mode-toggle.tsx @@ -0,0 +1,37 @@ +import type { ButtonHTMLAttributes } from 'react' +import { cn, Tooltip } from '@sim/emcn' +import { ArrowLeftRight } from '@sim/emcn/icons' + +interface FieldModeToggleProps { + label: string + active: boolean + disabled?: boolean + onClick: ButtonHTMLAttributes['onClick'] +} + +/** Shared field-mode affordance; the caller owns the mode and its stored values. */ +export function FieldModeToggle({ label, active, disabled, onClick }: FieldModeToggleProps) { + return ( + + + + + +

{label}

+
+
+ ) +} diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/filter-builder/components/filter-rule-row.tsx b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/filter-builder/components/filter-rule-row.tsx index 7e0e4375b3b..33a1fc95fc7 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/filter-builder/components/filter-rule-row.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/filter-builder/components/filter-rule-row.tsx @@ -129,7 +129,7 @@ export function FilterRuleRow({ const renderActions = () => ( <> - @@ -137,7 +137,7 @@ export function FilterRuleRow({ variant='ghost-destructive' onClick={() => onRemove(rule.id)} disabled={isReadOnly} - className='h-auto p-0' + size='icon' > Delete Condition diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/knowledge-tag-filters/knowledge-tag-filters.tsx b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/knowledge-tag-filters/knowledge-tag-filters.tsx index dc4e6b62c34..bd5e8b0dbf0 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/knowledge-tag-filters/knowledge-tag-filters.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/knowledge-tag-filters/knowledge-tag-filters.tsx @@ -252,24 +252,24 @@ export function KnowledgeTagFilters({ <> @@ -96,7 +96,7 @@ export function SortRuleRow({ variant='ghost-destructive' onClick={() => onRemove(rule.id)} disabled={isReadOnly} - className='h-auto p-0' + size='icon' > Delete Sort diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/starter/input-format.tsx b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/starter/input-format.tsx index 8502854b75d..a949d97a61b 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/starter/input-format.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/starter/input-format.tsx @@ -3,27 +3,26 @@ import { Badge, Button, Code, + CollapsibleCard, Combobox, type ComboboxOption, calculateGutterWidth, cn, - Expandable, - ExpandableContent, getCodeEditorProps, - handleKeyboardActivation, highlight, Input, Label, languages, - Tooltip, + OverflowText, } from '@sim/emcn' -import { ArrowLeftRight, Plus, Trash } from '@sim/emcn/icons' +import { Plus, Trash } from '@sim/emcn/icons' import Editor from 'react-simple-code-editor' import { createDefaultInputFormatField, isFileFieldType, parseInputFormatFiles, } from '@/lib/workflows/input-format' +import { FieldModeToggle } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/field-mode-toggle/field-mode-toggle' import { FileUpload } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/file-upload/file-upload' import { formatDisplayText } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/formatted-text' import { @@ -167,32 +166,17 @@ export function FieldFormat({ if (!canUseUploader) return null const label = mode === 'upload' ? 'Switch to JSON' : 'Switch to file uploader' return ( - - - - - -

{label}

-
-
+ + setFileFieldModes((prev) => ({ + ...prev, + [field.id]: mode === 'upload' ? 'json' : 'upload', + })) + } + /> ) } @@ -388,52 +372,6 @@ export function FieldFormat({ ) } - /** - * Renders the field header with name, type badge, and action buttons - */ - const renderFieldHeader = (field: Field, index: number) => ( -
toggleCollapse(field.id)} - onKeyDown={(event) => { - if (event.target !== event.currentTarget) return - handleKeyboardActivation(event, () => toggleCollapse(field.id)) - }} - > -
- - {field.name || `${title} ${index + 1}`} - - {field.name && showType && ( - - {field.type} - - )} -
-
e.stopPropagation()} - > - - -
-
- ) - /** * Renders the value input field based on the field type */ @@ -670,113 +608,141 @@ export function FieldFormat({ return (
{fields.map((field, index) => ( -
+ + {field.name && showType && ( + + {field.type} + + )} + + } + collapsed={!!field.collapsed} + onToggleCollapse={() => toggleCollapse(field.id)} + animated + actions={ + <> + + + + } > - {renderFieldHeader(field, index)} - - - -
-
- {renderFieldLabel('Name')} -
{renderNameInput(field)}
-
+
+ {renderFieldLabel('Name')} +
{renderNameInput(field)}
+
+ + {showType && ( +
+ {renderFieldLabel('Type')} + updateField(field.id, 'type', value)} + disabled={isReadOnly} + /> +
+ )} - {showType && ( -
- {renderFieldLabel('Type')} - updateField(field.id, 'type', value)} - disabled={isReadOnly} - /> -
- )} - - {showDescription && ( -
- {renderFieldLabel('Description')} -
- { - if (el) descriptionInputRefs.current[field.id] = el - }} - value={field.description ?? ''} - onChange={(e) => updateField(field.id, 'description', e.target.value)} - onScroll={(e) => - syncDescriptionOverlayScroll(field.id, e.currentTarget.scrollLeft) - } - onPaste={() => - setTimeout(() => { - const input = descriptionInputRefs.current[field.id] - input && syncDescriptionOverlayScroll(field.id, input.scrollLeft) - }, 0) - } - placeholder={descriptionPlaceholder} - disabled={isReadOnly} - autoComplete='off' - className='allow-scroll w-full overflow-x-auto overflow-y-hidden text-transparent caret-foreground [letter-spacing:inherit] placeholder:text-muted-foreground/50' - /> -
{ - if (el) descriptionOverlayRefs.current[field.id] = el - }} - style={{ scrollbarWidth: 'none' }} - className={cn( - 'pointer-events-none absolute inset-0 flex items-center overflow-x-auto bg-transparent px-2 py-1.5 font-sans text-sm', - isReadOnly && 'opacity-50' - )} - > - - {formatDisplayText( - field.description ?? '', - accessiblePrefixes - ? { - accessiblePrefixes, - workflowSearchHighlight: getActiveWorkflowSearchHighlight({ - activeSearchTarget, - blockId, - subBlockId, - valuePath: [index, 'description'], - }), - } - : { - highlightAll: true, - workflowSearchHighlight: getActiveWorkflowSearchHighlight({ - activeSearchTarget, - blockId, - subBlockId, - valuePath: [index, 'description'], - }), - } - )} - -
-
-
- )} - - {showValue && ( -
- {isFileFieldType(field.type) ? ( -
- {renderFieldLabel('Value')} - {renderFileModeToggle(field)} -
- ) : ( - renderFieldLabel('Value') + {showDescription && ( +
+ {renderFieldLabel('Description')} +
+ { + if (el) descriptionInputRefs.current[field.id] = el + }} + value={field.description ?? ''} + onChange={(e) => updateField(field.id, 'description', e.target.value)} + onScroll={(e) => + syncDescriptionOverlayScroll(field.id, e.currentTarget.scrollLeft) + } + onPaste={() => + setTimeout(() => { + const input = descriptionInputRefs.current[field.id] + input && syncDescriptionOverlayScroll(field.id, input.scrollLeft) + }, 0) + } + placeholder={descriptionPlaceholder} + disabled={isReadOnly} + autoComplete='off' + className='allow-scroll w-full overflow-x-auto overflow-y-hidden text-transparent caret-foreground [letter-spacing:inherit] placeholder:text-muted-foreground/50' + /> +
{ + if (el) descriptionOverlayRefs.current[field.id] = el + }} + style={{ scrollbarWidth: 'none' }} + className={cn( + 'pointer-events-none absolute inset-0 flex items-center overflow-x-auto bg-transparent px-2 py-1.5 font-sans text-sm', + isReadOnly && 'opacity-50' + )} + > + + {formatDisplayText( + field.description ?? '', + accessiblePrefixes + ? { + accessiblePrefixes, + workflowSearchHighlight: getActiveWorkflowSearchHighlight({ + activeSearchTarget, + blockId, + subBlockId, + valuePath: [index, 'description'], + }), + } + : { + highlightAll: true, + workflowSearchHighlight: getActiveWorkflowSearchHighlight({ + activeSearchTarget, + blockId, + subBlockId, + valuePath: [index, 'description'], + }), + } )} -
{renderValueInput(field)}
-
- )} + +
- - -
+
+ )} + + {showValue && ( +
+ {isFileFieldType(field.type) ? ( +
+ {renderFieldLabel('Value')} + {renderFileModeToggle(field)} +
+ ) : ( + renderFieldLabel('Value') + )} +
{renderValueInput(field)}
+
+ )} + ))}
) diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/tool-input/components/tools/usage-control.tsx b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/tool-input/components/tools/usage-control.tsx index e79ab62abcd..684602b559c 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/tool-input/components/tools/usage-control.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/tool-input/components/tools/usage-control.tsx @@ -1,7 +1,7 @@ -import { Combobox, cn, Label, Tooltip } from '@sim/emcn' -import { ArrowLeftRight } from '@sim/emcn/icons' +import { Combobox, Label } from '@sim/emcn' import type { CanonicalMode } from '@/lib/workflows/subblocks/visibility' import type { StoredTool } from '@/lib/workflows/tool-input/types' +import { FieldModeToggle } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/field-mode-toggle/field-mode-toggle' import { ShortInput } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/short-input' import type { SubBlockConfig } from '@/blocks/types' @@ -63,29 +63,12 @@ export function ToolUsageControl({
- - - - - -

{toggleLabel}

-
-
+
{mode === 'advanced' ? ( diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/variables-input/variables-input.tsx b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/variables-input/variables-input.tsx index 5ce53f38f50..84b832a7cbb 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/variables-input/variables-input.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/variables-input/variables-input.tsx @@ -2,18 +2,19 @@ import { useEffect, useRef, useState } from 'react' import { Badge, Button, + CollapsibleCard, Combobox, type ComboboxOption, cn, - handleKeyboardActivation, Input, Label, + OverflowText, Textarea, - Tooltip, } from '@sim/emcn' -import { ArrowLeftRight, FileText, Plus, Trash } from '@sim/emcn/icons' +import { FileText, Plus, Trash } from '@sim/emcn/icons' import { generateId } from '@sim/utils/id' import { useParams } from 'next/navigation' +import { FieldModeToggle } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/field-mode-toggle/field-mode-toggle' import { formatDisplayText } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/formatted-text' import { checkTagTrigger, @@ -382,39 +383,32 @@ export function VariablesInput({ }) return ( -
-
toggleCollapse(assignment.id)} - onKeyDown={(event) => { - if (event.target !== event.currentTarget) return - handleKeyboardActivation(event, () => toggleCollapse(assignment.id)) - }} - > -
- + title={ + + {assignment.variableName ? formatDisplayText(assignment.variableName, { workflowSearchHighlight: variableLabelHighlight, }) : `Variable ${index + 1}`} - + {assignment.variableName && ( {assignment.type} )} -
-
+ + } + collapsed={collapsed} + onToggleCollapse={() => toggleCollapse(assignment.id)} + actions={ + <> -
+ + } + > +
+ + ({ label: v.name, value: v.id }))} + value={assignment.variableId || ''} + onChange={(value) => handleVariableSelect(assignment.id, value)} + placeholder='Select a variable...' + disabled={isReadOnly} + overlayContent={ + variableLabelHighlight ? ( + + {formatDisplayText(variableLabel, { + workflowSearchHighlight: variableLabelHighlight, + })} + + ) : undefined + } + />
- {!collapsed && ( -
-
- - ({ label: v.name, value: v.id }))} - value={assignment.variableId || ''} - onChange={(value) => handleVariableSelect(assignment.id, value)} - placeholder='Select a variable...' +
+
+ + {assignment.type === 'boolean' && ( + - {formatDisplayText(variableLabel, { - workflowSearchHighlight: variableLabelHighlight, - })} - - ) : undefined + onClick={() => + setManualBooleanModes((prev) => ({ + ...prev, + [assignment.id]: !isManualBoolean, + })) } /> -
- -
-
- - {assignment.type === 'boolean' && ( - - - - - -

- {isManualBoolean ? 'Switch to selector' : 'Switch to manual value'} -

-
-
- )} -
- {assignment.type === 'boolean' && !isManualBoolean ? ( - - !isReadOnly && updateAssignment(assignment.id, { value: v }) + )} +
+ {assignment.type === 'boolean' && !isManualBoolean ? ( + !isReadOnly && updateAssignment(assignment.id, { value: v })} + placeholder='Select value' + disabled={isReadOnly} + overlayContent={ + booleanLabelHighlight ? ( + + {formatDisplayText(assignment.value ?? '', { + workflowSearchHighlight: booleanLabelHighlight, + })} + + ) : undefined + } + /> + ) : assignment.type === 'object' || assignment.type === 'array' ? ( +
+