-
+
No tags defined for this knowledge base
@@ -234,58 +233,6 @@ export function DocumentTagEntry({
)
}
- /**
- * Renders the tag header with name, badge, and action buttons
- * Shows tag name only when collapsed (as summary), generic label when expanded
- */
- const renderTagHeader = (tag: DocumentTag, index: number) => (
-
toggleCollapse(tag.id)}
- onKeyDown={(event) => {
- if (event.target !== event.currentTarget) return
- handleKeyboardActivation(event, () => toggleCollapse(tag.id))
- }}
- >
-
-
- {tag.collapsed ? tag.tagName || `Tag ${index + 1}` : `Tag ${index + 1}`}
-
- {tag.collapsed && tag.tagName && (
-
- {FIELD_TYPE_LABELS[tag.fieldType] || 'Text'}
-
- )}
-
-
e.stopPropagation()}
- >
-
-
-
-
- )
-
/**
* Renders the value input with tag dropdown support
*/
@@ -314,10 +261,25 @@ export function DocumentTagEntry({
return (
-
{
if (el) valueInputRefs.current[cellKey] = el
}}
+ overlayRef={(el) => {
+ if (el) overlayRefs.current[cellKey] = el
+ }}
+ overlayContent={
+
+ {formatDisplayText(
+ fieldValue,
+ accessiblePrefixes
+ ? { accessiblePrefixes, workflowSearchHighlight }
+ : { highlightAll: true, workflowSearchHighlight }
+ )}
+
+ }
+ interactiveOverlay={isReadOnly}
+ inputClassName='allow-scroll'
value={fieldValue}
onChange={handlers.onChange}
onKeyDown={handlers.onKeyDown}
@@ -334,26 +296,8 @@ export function DocumentTagEntry({
disabled={isReadOnly}
autoComplete='off'
placeholder={placeholder}
- className='allow-scroll w-full overflow-auto text-transparent caret-foreground [letter-spacing:inherit]'
+ className='w-full'
/>
-
{
- if (el) overlayRefs.current[cellKey] = el
- }}
- className={cn(
- 'absolute inset-0 flex items-center overflow-x-auto bg-transparent px-2 py-1.5 font-sans text-sm',
- !isReadOnly && 'pointer-events-none'
- )}
- >
-
- {formatDisplayText(
- fieldValue,
- accessiblePrefixes
- ? { accessiblePrefixes, workflowSearchHighlight }
- : { highlightAll: true, workflowSearchHighlight }
- )}
-
-
{fieldState.showTags && (
+ <>
- updateTag(tag.id, 'tagName', value)}
@@ -401,24 +345,51 @@ export function DocumentTagEntry({
{renderValueInput(tag)}
-
+ >
)
}
return (
{tags.map((tag, index) => (
-
+ {FIELD_TYPE_LABELS[tag.fieldType] || 'Text'}
+
+ ) : undefined
+ }
+ actions={
+ <>
+
+
+ >
+ }
+ collapsed={tag.collapsed ?? false}
+ onToggleCollapse={() => toggleCollapse(tag.id)}
>
- {renderTagHeader(tag, index)}
- {!tag.collapsed && renderTagContent(tag)}
-
+ {renderTagContent(tag)}
+
))}
)
diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/dropdown/dropdown.tsx b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/dropdown/dropdown.tsx
index 1eb22d6b5a8..9eccaa8293c 100644
--- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/dropdown/dropdown.tsx
+++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/dropdown/dropdown.tsx
@@ -1,5 +1,5 @@
import { memo, useCallback, useEffect, useMemo, useRef } from 'react'
-import { ChipTag, Combobox, type ComboboxOption } from '@sim/emcn'
+import { ChipSelect, type ChipSelectOption, ChipTag } from '@sim/emcn'
import { generateId } from '@sim/utils/id'
import { isRecordLike } from '@sim/utils/object'
import { formatDisplayText } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/formatted-text'
@@ -210,7 +210,7 @@ export const Dropdown = memo(function Dropdown({
return denied
}, [subBlockId, blockConfig, allOptions, isToolAllowed])
- const comboboxOptions = useMemo((): ComboboxOption[] => {
+ const selectOptions = useMemo((): ChipSelectOption[] => {
const toLabel = (raw: string) => (preserveLabelCase ? raw : raw.toLowerCase())
return allOptions.map((opt) => {
if (typeof opt === 'string') {
@@ -226,13 +226,13 @@ export const Dropdown = memo(function Dropdown({
}, [allOptions, deniedOperationIds, preserveLabelCase])
const optionMap = useMemo(() => {
- return new Map(comboboxOptions.map((opt) => [opt.value, opt.label]))
- }, [comboboxOptions])
+ return new Map(selectOptions.map((opt) => [opt.value, opt.label]))
+ }, [selectOptions])
const defaultOptionValue = useMemo(() => {
if (multiSelect) return undefined
- const firstSelectable = comboboxOptions.find((opt) => !opt.hidden)
+ const firstSelectable = selectOptions.find((opt) => !opt.hidden)
if (defaultValue !== undefined) {
// Don't seed a denied operation as the default; use the first allowed option.
if (deniedOperationIds.has(defaultValue)) {
@@ -242,7 +242,7 @@ export const Dropdown = memo(function Dropdown({
}
return firstSelectable?.value
- }, [defaultValue, comboboxOptions, deniedOperationIds, multiSelect])
+ }, [defaultValue, selectOptions, deniedOperationIds, multiSelect])
useEffect(() => {
if (multiSelect || defaultOptionValue === undefined) {
@@ -427,25 +427,27 @@ export const Dropdown = memo(function Dropdown({
)
}, [activeSearchTarget, blockId, multiSelect, optionMap, singleValue, subBlockId])
- const isSearchable = searchable || (subBlockId === 'operation' && comboboxOptions.length > 5)
+ const isSearchable = searchable || (subBlockId === 'operation' && selectOptions.length > 5)
return (
-
)
})
diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/env-var-dropdown/env-var-dropdown.tsx b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/env-var-dropdown/env-var-dropdown.tsx
index b20ba4efc87..8297bf54f75 100644
--- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/env-var-dropdown/env-var-dropdown.tsx
+++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/env-var-dropdown/env-var-dropdown.tsx
@@ -281,7 +281,7 @@ export const EnvVarDropdown: React.FC
= ({
}
return (
- !open && onClose?.()} colorScheme='inverted'>
+ !open && onClose?.()}>
= ({
/>
e.preventDefault()}
onCloseAutoFocus={(e) => e.preventDefault()}
>
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 6c659aff3dc..2055ac09a41 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
@@ -1,8 +1,12 @@
import { useMemo, useRef } from 'react'
-import { Button, cn, Input, Label, Textarea, Tooltip } from '@sim/emcn'
+import { Button, Label, Tooltip } from '@sim/emcn'
import { Plus, Trash } from '@sim/emcn/icons'
import { generateId } from '@sim/utils/id'
import { formatDisplayText } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/formatted-text'
+import {
+ ReferenceTextarea,
+ ReferenceTextInput,
+} from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/reference-text-control'
import { TagDropdown } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/tag-dropdown/tag-dropdown'
import { getActiveWorkflowSearchHighlight } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/workflow-search-highlight'
import { useSubBlockInput } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/hooks/use-sub-block-input'
@@ -139,7 +143,7 @@ export function EvalInput({
const renderMetricHeader = (metric: EvalMetric, index: number) => (
-
Metric {index + 1}
+
Metric {index + 1}
@@ -187,21 +191,13 @@ export function EvalInput({
{renderFieldLabel('Name')}
-
-
updateMetric(metric.id, 'name', e.target.value)}
- placeholder='Accuracy'
- disabled={isPreview || disabled}
- className='text-transparent caret-foreground [letter-spacing:inherit] placeholder:text-muted-foreground/50'
- />
-
+ updateMetric(metric.id, 'name', e.target.value)}
+ placeholder='Accuracy'
+ disabled={isPreview || disabled}
+ overlayContent={
{formatDisplayText(metric.name || '', {
accessiblePrefixes,
@@ -209,8 +205,8 @@ export function EvalInput({
workflowSearchHighlight: getMetricSearchHighlight(index, ['name']),
})}
-
-
+ }
+ />
@@ -231,10 +227,26 @@ export function EvalInput({
return (
<>
-
diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/field-header/field-header.test.tsx b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/field-header/field-header.test.tsx
new file mode 100644
index 00000000000..0c62d5155f4
--- /dev/null
+++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/field-header/field-header.test.tsx
@@ -0,0 +1,82 @@
+/**
+ * @vitest-environment jsdom
+ */
+import { act, createRef } from 'react'
+import { Tooltip } from '@sim/emcn'
+import { createRoot, type Root } from 'react-dom/client'
+import { afterEach, describe, expect, it, vi } from 'vitest'
+import { SubBlockFieldHeader } from './field-header'
+
+let root: Root | null = null
+let container: HTMLDivElement | null = null
+
+function mount(header: React.ReactNode) {
+ ;(globalThis as { IS_REACT_ACT_ENVIRONMENT?: boolean }).IS_REACT_ACT_ENVIRONMENT = true
+ container = document.createElement('div')
+ document.body.appendChild(container)
+ root = createRoot(container)
+ act(() => root?.render({header}))
+}
+
+describe('SubBlockFieldHeader', () => {
+ afterEach(() => {
+ if (root) act(() => root?.unmount())
+ root = null
+ container?.remove()
+ container = null
+ })
+
+ it('renders required and validation state with canonical actions', () => {
+ const onCopy = vi.fn()
+ const onToggle = vi.fn()
+ mount(
+
+ )
+
+ expect(container?.textContent).toContain('Response format')
+ expect(container?.querySelector('[aria-label="Required"]')).not.toBeNull()
+ expect(container?.querySelector('[aria-label="Switch to manual ID"]')).not.toBeNull()
+
+ const copyButton = container?.querySelector('[aria-label="Copy value"]')
+ if (!copyButton) throw new Error('Copy action did not render')
+ act(() => copyButton.click())
+ expect(onCopy).toHaveBeenCalledOnce()
+ })
+
+ it('submits and cancels the inline generation prompt from the keyboard', () => {
+ const onSubmit = vi.fn()
+ const onCancel = vi.fn()
+ mount(
+ (),
+ }}
+ />
+ )
+
+ const input = container?.querySelector('[aria-label="Generate with AI"]')
+ if (!input) throw new Error('Generation input did not render')
+ act(() => {
+ input.dispatchEvent(new KeyboardEvent('keydown', { bubbles: true, key: 'Enter' }))
+ input.dispatchEvent(new KeyboardEvent('keydown', { bubbles: true, key: 'Escape' }))
+ })
+
+ expect(onSubmit).toHaveBeenCalledOnce()
+ expect(onCancel).toHaveBeenCalledOnce()
+ })
+})
diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/field-header/field-header.tsx b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/field-header/field-header.tsx
new file mode 100644
index 00000000000..1515a1a1282
--- /dev/null
+++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/field-header/field-header.tsx
@@ -0,0 +1,200 @@
+import type { FocusEvent, ReactNode, RefObject } from 'react'
+import { Button, ChipInput, Label, Tooltip } from '@sim/emcn'
+import {
+ ArrowLeftRight,
+ ArrowUp,
+ Check,
+ Clipboard,
+ SquareArrowUpRight,
+ TriangleAlert,
+} from '@sim/emcn/icons'
+
+interface FieldHeaderWandAction {
+ isSearchActive: boolean
+ searchQuery: string
+ isStreaming: boolean
+ onSearchClick: () => void
+ onSearchBlur: () => void
+ onSearchChange: (value: string) => void
+ onSearchSubmit: () => void
+ onSearchCancel: () => void
+ searchInputRef: RefObject
+}
+
+interface FieldHeaderCanonicalAction {
+ mode: 'basic' | 'advanced'
+ disabled?: boolean
+ onToggle?: () => void
+}
+
+interface FieldHeaderCopyAction {
+ copied: boolean
+ onCopy: () => void
+}
+
+interface FieldHeaderExternalLinkAction {
+ onClick: () => void
+ tooltip: string
+}
+
+interface SubBlockFieldHeaderProps {
+ title: string
+ required?: boolean
+ invalidJson?: boolean
+ labelSuffix?: ReactNode
+ wandAction?: FieldHeaderWandAction
+ canonicalAction?: FieldHeaderCanonicalAction
+ copyAction?: FieldHeaderCopyAction
+ externalLinkAction?: FieldHeaderExternalLinkAction
+}
+
+/**
+ * Presents a workflow field title and its contextual actions with one canonical
+ * rhythm while leaving field state and persistence in the parent sub-block.
+ */
+export function SubBlockFieldHeader({
+ title,
+ required = false,
+ invalidJson = false,
+ labelSuffix,
+ wandAction,
+ canonicalAction,
+ copyAction,
+ externalLinkAction,
+}: SubBlockFieldHeaderProps) {
+ const canonicalTooltip =
+ canonicalAction?.mode === 'advanced' ? 'Switch to selector' : 'Switch to manual ID'
+
+ const handleWandBlur = (event: FocusEvent) => {
+ if (event.relatedTarget instanceof HTMLElement && event.relatedTarget.closest('button')) return
+ wandAction?.onSearchBlur()
+ }
+
+ return (
+
+
+
+ {copyAction ? (
+
+
+
+
+ {copyAction.copied ? 'Copied!' : 'Copy'}
+
+ ) : null}
+ {wandAction ? (
+ wandAction.isSearchActive ? (
+
+
wandAction.onSearchChange(event.target.value)}
+ onBlur={handleWandBlur}
+ onKeyDown={(event) => {
+ if (
+ event.key === 'Enter' &&
+ wandAction.searchQuery.trim() &&
+ !wandAction.isStreaming
+ ) {
+ wandAction.onSearchSubmit()
+ } else if (event.key === 'Escape') {
+ wandAction.onSearchCancel()
+ }
+ }}
+ disabled={wandAction.isStreaming}
+ placeholder='Generate with AI...'
+ aria-label='Generate with AI'
+ />
+
+
+ ) : (
+
+ )
+ ) : null}
+ {externalLinkAction ? (
+
+
+
+
+ {externalLinkAction.tooltip}
+
+ ) : null}
+ {canonicalAction ? (
+
+
+
+
+ {canonicalTooltip}
+
+ ) : null}
+
+
+ )
+}
diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/field-header/index.ts b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/field-header/index.ts
new file mode 100644
index 00000000000..de8b043ab5d
--- /dev/null
+++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/field-header/index.ts
@@ -0,0 +1 @@
+export { SubBlockFieldHeader } from './field-header'
diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/file-upload/file-upload.tsx b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/file-upload/file-upload.tsx
index 2b7356c6697..b125db0c23a 100644
--- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/file-upload/file-upload.tsx
+++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/file-upload/file-upload.tsx
@@ -1,7 +1,7 @@
'use client'
import { useMemo, useRef, useState } from 'react'
-import { Button, Combobox, cn } from '@sim/emcn'
+import { Button, ChipCombobox, cn, Loader } from '@sim/emcn'
import { X } from '@sim/emcn/icons'
import { createLogger } from '@sim/logger'
import { getErrorMessage } from '@sim/utils/errors'
@@ -107,7 +107,7 @@ function SingleFileSelector({
return (
-
)
@@ -570,7 +568,7 @@ export function FileUpload({
return (
@@ -580,16 +578,14 @@ export function FileUpload({
)
@@ -599,14 +595,14 @@ export function FileUpload({
return (
{file.name}
({formatFileSize(file.size)})
)
@@ -702,21 +698,25 @@ export function FileUpload({
type='file'
ref={fileInputRef}
onChange={handleFileChange}
- style={{ display: 'none' }}
+ className='hidden'
accept={acceptedTypes}
multiple={multiple}
data-testid='file-input-element'
/>
{showCloudStorageWarning && (
-
+
Cloud storage (S3 or Blob) is required for file uploads. Configure S3_BUCKET_NAME and
AWS_REGION, or Azure Blob env vars.
)}
{/* Error message */}
- {uploadError &&
{uploadError}
}
+ {uploadError && (
+
+ {uploadError}
+
+ )}
{/* File list with consistent spacing - only show for multiple mode or when uploading */}
{((hasFiles && multiple) || isUploading) && (
@@ -738,7 +738,7 @@ export function FileUpload({
className='h-2 w-full'
indicatorClassName='bg-foreground'
/>
-
+
{uploadProgress < 100 ? 'Uploading...' : 'Upload complete!'}
@@ -749,7 +749,7 @@ export function FileUpload({
{/* Add More dropdown for multiple files */}
{hasFiles && multiple && !isUploading && (
-
(
- onToggleCollapse(rule.id)}
- onKeyDown={(event) => {
- if (event.target !== event.currentTarget) return
- handleKeyboardActivation(event, () => onToggleCollapse(rule.id))
- }}
- >
-
-
- {rule.collapsed && rule.column
- ? formatDisplayText(getColumnLabel(rule.column), {
- workflowSearchHighlight: getLabelHighlight('column', getColumnLabel(rule.column)),
- })
- : `Condition ${index + 1}`}
-
- {rule.collapsed && rule.column && (
-
- {formatDisplayText(getOperatorLabel(rule.operator), {
- workflowSearchHighlight: getLabelHighlight(
- 'operator',
- getOperatorLabel(rule.operator)
- ),
- })}
-
- )}
-
-
e.stopPropagation()}
- >
-
-
-
-
- )
-
const renderValueInput = () => (
-
+ {formatDisplayText(
+ rule.value,
+ accessiblePrefixes
+ ? { accessiblePrefixes, workflowSearchHighlight }
+ : { highlightAll: true, workflowSearchHighlight }
+ )}
+
+ }
+ interactiveOverlay={isReadOnly}
+ inputClassName='allow-scroll'
value={rule.value}
onChange={handlers.onChange}
onKeyDown={handlers.onKeyDown}
@@ -175,24 +135,8 @@ export function FilterRuleRow({
disabled={isReadOnly}
autoComplete='off'
placeholder='Enter value'
- className='allow-scroll w-full overflow-auto text-transparent caret-foreground [letter-spacing:inherit]'
+ className='w-full'
/>
-
-
- {formatDisplayText(
- rule.value,
- accessiblePrefixes
- ? { accessiblePrefixes, workflowSearchHighlight }
- : { highlightAll: true, workflowSearchHighlight }
- )}
-
-
{fieldState.showTags && (
(
-
+ <>
{index > 0 && (
-
onUpdate(rule.id, 'logicalOperator', v as 'and' | 'or')}
@@ -236,7 +180,7 @@ export function FilterRuleRow({
-
onUpdate(rule.id, 'column', v)}
@@ -256,7 +200,7 @@ export function FilterRuleRow({
- onUpdate(rule.id, 'operator', v)}
@@ -281,19 +225,57 @@ export function FilterRuleRow({
{renderValueInput()}
-
+ >
)
return (
-
+ {formatDisplayText(getOperatorLabel(rule.operator), {
+ workflowSearchHighlight: getLabelHighlight(
+ 'operator',
+ getOperatorLabel(rule.operator)
+ ),
+ })}
+
+ ) : undefined
+ }
+ actions={
+ <>
+
+
+ >
+ }
+ collapsed={rule.collapsed ?? false}
+ onToggleCollapse={() => onToggleCollapse(rule.id)}
>
- {renderHeader()}
- {!rule.collapsed && renderContent()}
-
+ {renderContent()}
+
)
}
diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/index.ts b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/index.ts
index 15bb29ed20d..cdda2da961d 100644
--- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/index.ts
+++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/index.ts
@@ -1,3 +1,4 @@
+export { BooleanControl } from './boolean-control'
export { CheckboxList } from './checkbox-list'
export { Code } from './code'
export { ComboBox } from './combobox'
@@ -7,6 +8,7 @@ export { DocumentTagEntry } from './document-tag-entry'
export { Dropdown } from './dropdown'
export { checkEnvVarTrigger, EnvVarDropdown } from './env-var-dropdown'
export { EvalInput } from './eval-input'
+export { SubBlockFieldHeader } from './field-header'
export { FileUpload } from './file-upload'
export { FilterBuilder } from './filter-builder'
export { formatDisplayText, type HighlightContext } from './formatted-text'
@@ -18,6 +20,7 @@ export { LongInput } from './long-input'
export { McpDynamicArgs } from './mcp-dynamic-args'
export { McpServerSelector, McpToolSelector } from './mcp-server-modal'
export { MessagesInput } from './messages-input'
+export { ReferenceTextarea, ReferenceTextInput } from './reference-text-control'
export { ResponseFormat } from './response'
export { ScheduleInfo } from './schedule-info'
export { SelectorInput, type SelectorOverrides } from './selector-input'
diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/input-mapping/input-mapping.tsx b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/input-mapping/input-mapping.tsx
index b0b8583cce4..2e4aee0d2d7 100644
--- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/input-mapping/input-mapping.tsx
+++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/input-mapping/input-mapping.tsx
@@ -1,7 +1,8 @@
import { useEffect, useMemo, useRef, useState } from 'react'
-import { Badge, CollapsibleCard, cn, Input, Label } from '@sim/emcn'
+import { Badge, CollapsibleCard, Label } from '@sim/emcn'
import { extractInputFieldsFromBlocks } from '@/lib/workflows/input-format'
import { formatDisplayText } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/formatted-text'
+import { ReferenceTextInput } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/reference-text-control'
import { TagDropdown } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/tag-dropdown/tag-dropdown'
import { getActiveWorkflowSearchHighlight } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/workflow-search-highlight'
import { useDependsOnGate } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/hooks/use-depends-on-gate'
@@ -132,7 +133,7 @@ export function InputMapping({
return (
-
No workflow selected
+
No workflow selected
Select a workflow above to configure inputs
@@ -253,10 +254,25 @@ function InputMappingField({
-
{
if (el) inputRefs.current.set(fieldId, el)
}}
+ overlayRef={(el) => {
+ if (el) overlayRefs.current.set(fieldId, el)
+ }}
+ overlayContent={
+
+ {formatDisplayText(
+ value,
+ accessiblePrefixes
+ ? { accessiblePrefixes, workflowSearchHighlight }
+ : { highlightAll: true, workflowSearchHighlight }
+ )}
+
+ }
+ interactiveOverlay={disabled}
+ inputClassName='allow-scroll'
name='value'
value={value}
onChange={handlers.onChange}
@@ -274,33 +290,9 @@ function InputMappingField({
placeholder='Enter value or reference'
disabled={disabled}
autoComplete='off'
- className={cn(
- 'allow-scroll w-full overflow-auto text-transparent caret-foreground [letter-spacing:inherit]'
- )}
+ className='w-full'
style={{ overflowX: 'auto' }}
/>
-
{
- if (el) overlayRefs.current.set(fieldId, el)
- }}
- className={cn(
- 'absolute inset-0 flex items-center overflow-x-auto bg-transparent px-2 py-1.5 font-sans text-sm',
- !disabled && 'pointer-events-none'
- )}
- style={{ overflowX: 'auto' }}
- >
-
- {formatDisplayText(
- value,
- accessiblePrefixes
- ? { accessiblePrefixes, workflowSearchHighlight }
- : { highlightAll: true, workflowSearchHighlight }
- )}
-
-
{fieldState.showTags && (
({
queryKey: knowledgeKeys.detail(selectedId),
- queryFn: () => fetchKnowledgeBase(selectedId),
+ queryFn: ({ signal }) => fetchKnowledgeBase(selectedId, signal),
enabled: Boolean(selectedId),
- staleTime: 60 * 1000,
+ staleTime: KNOWLEDGE_BASE_DETAIL_STALE_TIME,
})),
})
@@ -198,31 +198,24 @@ export function KnowledgeBaseSelector({
label: labelOf(kb),
})
return (
- handleRemoveKnowledgeBase(kb.id) : undefined
+ }
>
-
-
- {formatDisplayText(labelOf(kb), { workflowSearchHighlight })}
-
- {!disabled && !isPreview && (
-
- )}
-
+ {formatDisplayText(labelOf(kb), { workflowSearchHighlight })}
+
)
})}
)}
-
-
+
{appliedFilters > 0 ? `${appliedFilters} filter(s) applied` : 'No filters'}
@@ -227,60 +226,6 @@ export function KnowledgeTagFilters({
)
}
- /**
- * Renders the filter header with name, badge, and action buttons
- * Shows tag name only when collapsed (as summary), generic label when expanded
- */
- const renderFilterHeader = (filter: TagFilter, index: number) => (
- toggleCollapse(filter.id)}
- onKeyDown={(event) => {
- if (event.target !== event.currentTarget) return
- handleKeyboardActivation(event, () => toggleCollapse(filter.id))
- }}
- >
-
-
- {filter.collapsed ? filter.tagName || `Filter ${index + 1}` : `Filter ${index + 1}`}
-
- {filter.collapsed && filter.tagName && (
-
- {FIELD_TYPE_LABELS[filter.fieldType] || 'Text'}
-
- )}
-
-
-
-
-
-
- )
-
/**
* Renders the value input with tag dropdown support
*/
@@ -309,10 +254,25 @@ export function KnowledgeTagFilters({
return (
-
{
if (el) valueInputRefs.current[cellKey] = el
}}
+ overlayRef={(el) => {
+ if (el) overlayRefs.current[cellKey] = el
+ }}
+ overlayContent={
+
+ {formatDisplayText(
+ fieldValue,
+ accessiblePrefixes
+ ? { accessiblePrefixes, workflowSearchHighlight }
+ : { highlightAll: true, workflowSearchHighlight }
+ )}
+
+ }
+ interactiveOverlay={isReadOnly}
+ inputClassName='allow-scroll'
value={fieldValue}
onChange={handlers.onChange}
onKeyDown={handlers.onKeyDown}
@@ -329,26 +289,8 @@ export function KnowledgeTagFilters({
disabled={isReadOnly}
autoComplete='off'
placeholder={placeholder}
- className='allow-scroll w-full overflow-auto text-transparent caret-foreground [letter-spacing:inherit]'
+ className='w-full'
/>
-
{
- if (el) overlayRefs.current[cellKey] = el
- }}
- className={cn(
- 'absolute inset-0 flex items-center overflow-x-auto bg-transparent px-2 py-1.5 font-sans text-sm',
- !isReadOnly && 'pointer-events-none'
- )}
- >
-
- {formatDisplayText(
- fieldValue,
- accessiblePrefixes
- ? { accessiblePrefixes, workflowSearchHighlight }
- : { highlightAll: true, workflowSearchHighlight }
- )}
-
-
{fieldState.showTags && (
+ <>
-
updateFilter(filter.id, 'tagName', value)}
@@ -397,7 +339,7 @@ export function KnowledgeTagFilters({
- updateFilter(filter.id, 'operator', value)}
@@ -418,24 +360,51 @@ export function KnowledgeTagFilters({
renderValueInput(filter, 'tagValue')
)}
-
+ >
)
}
return (
{filters.map((filter, index) => (
-
+ {FIELD_TYPE_LABELS[filter.fieldType] || 'Text'}
+
+ ) : undefined
+ }
+ actions={
+ <>
+
+
+ >
+ }
+ collapsed={filter.collapsed ?? false}
+ onToggleCollapse={() => toggleCollapse(filter.id)}
>
- {renderFilterHeader(filter, index)}
- {!filter.collapsed && renderFilterContent(filter)}
-
+ {renderFilterContent(filter)}
+
))}
)
diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/long-input/long-input.tsx b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/long-input/long-input.tsx
index 3c594cc1788..15d32007caf 100644
--- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/long-input/long-input.tsx
+++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/long-input/long-input.tsx
@@ -7,11 +7,11 @@ import {
useRef,
useState,
} from 'react'
-import { cn, Textarea } from '@sim/emcn'
+import { Button, cn } from '@sim/emcn'
import { ChevronsUpDown, Wand } from '@sim/emcn/icons'
import { createLogger } from '@sim/logger'
-import { Button } from '@/components/ui/button'
import { formatDisplayText } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/formatted-text'
+import { ReferenceTextarea } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/reference-text-control'
import { SubBlockInputController } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/sub-block-input-controller'
import { getActiveWorkflowSearchHighlight } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/workflow-search-highlight'
import { useSubBlockInput } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/hooks/use-sub-block-input'
@@ -220,14 +220,6 @@ export function LongInput({
}
}, [rows])
- // Sync scroll position between textarea and overlay
- const handleScroll = useCallback((e: React.UIEvent) => {
- if (overlayRef.current) {
- overlayRef.current.scrollTop = e.currentTarget.scrollTop
- overlayRef.current.scrollLeft = e.currentTarget.scrollLeft
- }
- }, [])
-
// Ensure overlay updates when content changes
useEffect(() => {
if (textareaRef.current && overlayRef.current) {
@@ -291,6 +283,8 @@ export function LongInput({
[wandHook]
)
+ const showWandButton = isWandEnabled && !isPreview && !wandHook.isStreaming && !hideInternalWand
+
return (
<>
{/* Wand Prompt Bar - positioned above the textarea */}
@@ -325,91 +319,66 @@ export function LongInput({
;(ref as React.MutableRefObject).current = el
}
return (
-