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 @@ -44,10 +44,20 @@ export function Dropdown({

const inputRef = useRef<HTMLInputElement>(null)
const dropdownRef = useRef<HTMLDivElement>(null)
const previousModeRef = useRef<string | null>(null)

// For response dataMode conversion - get builderData and data sub-blocks
const [builderData] = useSubBlockValue<any[]>(blockId, 'builderData')
const [, setData] = useSubBlockValue<string>(blockId, 'data')
const [builderData, setBuilderData] = useSubBlockValue<any[]>(blockId, 'builderData')
const [data, setData] = useSubBlockValue<string>(blockId, 'data')

// Keep refs with latest values to avoid stale closures
const builderDataRef = useRef(builderData)
const dataRef = useRef(data)

useEffect(() => {
builderDataRef.current = builderData
dataRef.current = data
}, [builderData, data])

// Use preview value when in preview mode, otherwise use store value or prop value
const value = isPreview ? previewValue : propValue !== undefined ? propValue : storeValue
Expand Down Expand Up @@ -103,23 +113,89 @@ export function Dropdown({
}
}, [storeInitialized, value, defaultOptionValue, setStoreValue])

// Helper function to normalize variable references in JSON strings
const normalizeVariableReferences = (jsonString: string): string => {
// Replace unquoted variable references with quoted ones
// Pattern: <variable.name> -> "<variable.name>"
return jsonString.replace(/([^"]<[^>]+>)/g, '"$1"')
}
Comment on lines +117 to +121
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

logic: regex pattern incorrectly captures the character before <, causing malformed JSON (e.g., : <var> becomes ": <var>""": <var>")

Suggested change
const normalizeVariableReferences = (jsonString: string): string => {
// Replace unquoted variable references with quoted ones
// Pattern: <variable.name> -> "<variable.name>"
return jsonString.replace(/([^"]<[^>]+>)/g, '"$1"')
}
const normalizeVariableReferences = (jsonString: string): string => {
// Replace unquoted variable references with quoted ones
// Pattern: <variable.name> -> "<variable.name>"
return jsonString.replace(/(?<!")(<[^>]+>)(?!")/g, '"$1"')
}
Prompt To Fix With AI
This is a comment left during a code review.
Path: apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/workflow-block/components/sub-block/components/dropdown.tsx
Line: 117:121

Comment:
**logic:** regex pattern incorrectly captures the character before `<`, causing malformed JSON (e.g., `: <var>` becomes `": <var>"``"": <var>"`)

```suggestion
  const normalizeVariableReferences = (jsonString: string): string => {
    // Replace unquoted variable references with quoted ones
    // Pattern: <variable.name> -> "<variable.name>"
    return jsonString.replace(/(?<!")(<[^>]+>)(?!")/g, '"$1"')
  }
```

How can I resolve this? If you propose a fix, please make it concise.


// Helper function to convert JSON string to builder data format
const convertJsonToBuilderData = (jsonString: string): any[] => {
try {
// Always normalize variable references first
const normalizedJson = normalizeVariableReferences(jsonString)
const parsed = JSON.parse(normalizedJson)

if (typeof parsed === 'object' && parsed !== null && !Array.isArray(parsed)) {
return Object.entries(parsed).map(([key, value]) => {
const fieldType = inferType(value)
const fieldValue =
fieldType === 'object' || fieldType === 'array' ? JSON.stringify(value, null, 2) : value

return {
id: crypto.randomUUID(),
name: key,
type: fieldType,
value: fieldValue,
collapsed: false,
}
})
}

return []
} catch (error) {
return []
}
}

// Helper function to infer field type from value
const inferType = (value: any): 'string' | 'number' | 'boolean' | 'object' | 'array' => {
if (typeof value === 'boolean') return 'boolean'
if (typeof value === 'number') return 'number'
if (Array.isArray(value)) return 'array'
if (typeof value === 'object' && value !== null) return 'object'
return 'string'
}

// Handle data conversion when dataMode changes
useEffect(() => {
if (subBlockId !== 'dataMode' || isPreview || disabled) return

const currentMode = storeValue
const previousMode = previousModeRef.current

// Only convert if the mode actually changed
if (previousMode !== null && previousMode !== currentMode) {
// Builder to Editor mode (structured → json)
if (currentMode === 'json' && previousMode === 'structured') {
const currentBuilderData = builderDataRef.current
if (
currentBuilderData &&
Array.isArray(currentBuilderData) &&
currentBuilderData.length > 0
) {
const jsonString = ResponseBlockHandler.convertBuilderDataToJsonString(currentBuilderData)
setData(jsonString)
}
}
// Editor to Builder mode (json → structured)
else if (currentMode === 'structured' && previousMode === 'json') {
const currentData = dataRef.current
if (currentData && typeof currentData === 'string' && currentData.trim().length > 0) {
const builderArray = convertJsonToBuilderData(currentData)
setBuilderData(builderArray)
}
}
}

// Update the previous mode ref
previousModeRef.current = currentMode
}, [storeValue, subBlockId, isPreview, disabled, setData, setBuilderData])

// Event handlers
const handleSelect = (selectedValue: string) => {
if (!isPreview && !disabled) {
// Handle conversion when switching from Builder to Editor mode in response blocks
if (
subBlockId === 'dataMode' &&
storeValue === 'structured' &&
selectedValue === 'json' &&
builderData &&
Array.isArray(builderData) &&
builderData.length > 0
) {
// Convert builderData to JSON string for editor mode
const jsonString = ResponseBlockHandler.convertBuilderDataToJsonString(builderData)
setData(jsonString)
}

setStoreValue(selectedValue)
}
setOpen(false)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,9 +83,8 @@ export function FieldFormat({
const [activeSourceBlockId, setActiveSourceBlockId] = useState<string | null>(null)
const accessiblePrefixes = useAccessibleReferencePrefixes(blockId)

// Use preview value when in preview mode, otherwise use store value
const value = isPreview ? previewValue : storeValue
const fields: Field[] = value || []
const fields: Field[] = Array.isArray(value) ? value : []
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this prevents nuking diff store if copilot outputs invalid fields i.e. not an array


useEffect(() => {
const initial: Record<string, string> = {}
Expand Down Expand Up @@ -547,7 +546,7 @@ export function ResponseFormat(
emptyMessage='No response fields defined'
showType={false}
showValue={true}
valuePlaceholder='Enter test value'
valuePlaceholder='Enter return value'
/>
)
}
Expand Down
2 changes: 1 addition & 1 deletion apps/sim/blocks/blocks/response.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ export const ResponseBlock: BlockConfig<ResponseBlockOutput> = {
docsLink: 'https://docs.sim.ai/blocks/response',
bestPractices: `
- Only use this if the trigger block is the API Trigger.
- Prefer the editor mode over the builder mode.
- Prefer the builder mode over the editor mode.
- This is usually used as the last block in the workflow.
`,
category: 'blocks',
Expand Down