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 @@ -413,7 +413,7 @@ export function DeployForm({
setKeyType('personal')
if (createError) setCreateError(null)
}}
className='h-8'
className='h-8 data-[variant=outline]:border-border data-[variant=outline]:bg-background data-[variant=outline]:text-foreground data-[variant=outline]:hover:bg-muted dark:data-[variant=outline]:border-border dark:data-[variant=outline]:bg-background dark:data-[variant=outline]:text-foreground dark:data-[variant=outline]:hover:bg-muted/80'
>
Personal
</Button>
Expand All @@ -425,7 +425,7 @@ export function DeployForm({
setKeyType('workspace')
if (createError) setCreateError(null)
}}
className='h-8'
className='h-8 data-[variant=outline]:border-border data-[variant=outline]:bg-background data-[variant=outline]:text-foreground data-[variant=outline]:hover:bg-muted dark:data-[variant=outline]:border-border dark:data-[variant=outline]:bg-background dark:data-[variant=outline]:text-foreground dark:data-[variant=outline]:hover:bg-muted/80'
>
Workspace
</Button>
Expand All @@ -452,7 +452,7 @@ export function DeployForm({

<AlertDialogFooter className='flex'>
<AlertDialogCancel
className='h-9 w-full rounded-[8px]'
className='h-9 w-full rounded-[8px] border-border bg-background text-foreground hover:bg-muted dark:border-border dark:bg-background dark:text-foreground dark:hover:bg-muted/80'
onClick={() => {
setNewKeyName('')
setKeyType('personal')
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,24 +42,19 @@ export function ExampleCommand({
const [exampleType, setExampleType] = useState<ExampleType>('execute')
const isAsyncEnabled = isTruthy(getEnv('NEXT_PUBLIC_TRIGGER_DEV_ENABLED'))

// Format the curl command to use a placeholder for the API key
const formatCurlCommand = (command: string, apiKey: string) => {
if (!command.includes('curl')) return command

// Replace the actual API key with a placeholder in the command
const sanitizedCommand = command.replace(apiKey, '$SIM_API_KEY')

// Format the command with line breaks for better readability
return sanitizedCommand
.replace(' -H ', '\n -H ')
.replace(' -d ', '\n -d ')
.replace(' http', '\n http')
}

// Get the command with placeholder for copying (single line, no line breaks)
const getActualCommand = () => {
const displayCommand = getDisplayCommand()
// Remove line breaks and extra whitespace for copying
return displayCommand
.replace(/\\\n\s*/g, ' ') // Remove backslash + newline + whitespace
.replace(/\n\s*/g, ' ') // Remove any remaining newlines + whitespace
Expand All @@ -70,51 +65,56 @@ export function ExampleCommand({
const getDisplayCommand = () => {
const baseEndpoint = endpoint.replace(apiKey, '$SIM_API_KEY')
const inputExample = getInputFormatExample
? getInputFormatExample(false) // No streaming for sync/async modes
? getInputFormatExample(false)
: ' -d \'{"input": "your data here"}\''

const addStreamingParams = (dashD: string) => {
const match = dashD.match(/-d\s*'([\s\S]*)'/)
if (!match) {
const payload: Record<string, any> = { stream: true }
if (selectedStreamingOutputs && selectedStreamingOutputs.length > 0) {
payload.selectedOutputs = selectedStreamingOutputs
}
return ` -d '${JSON.stringify(payload)}'`
}
try {
const payload = JSON.parse(match[1]) as Record<string, any>
payload.stream = true
if (selectedStreamingOutputs && selectedStreamingOutputs.length > 0) {
payload.selectedOutputs = selectedStreamingOutputs
}
return ` -d '${JSON.stringify(payload)}'`
} catch {
return dashD
}
}

switch (mode) {
case 'sync':
// For sync mode, use basic example without streaming
if (getInputFormatExample) {
const syncInputExample = getInputFormatExample(false)
return `curl -X POST \\
-H "X-API-Key: $SIM_API_KEY" \\
-H "Content-Type: application/json"${syncInputExample} \\
${baseEndpoint}`
return `curl -X POST \\\n -H "X-API-Key: $SIM_API_KEY" \\\n -H "Content-Type: application/json"${syncInputExample} \\\n ${baseEndpoint}`
}
return formatCurlCommand(command, apiKey)

case 'stream':
// For stream mode, include streaming params
if (getInputFormatExample) {
const streamInputExample = getInputFormatExample(true)
return `curl -X POST \\
-H "X-API-Key: $SIM_API_KEY" \\
-H "Content-Type: application/json"${streamInputExample} \\
${baseEndpoint}`
}
return formatCurlCommand(command, apiKey)
case 'stream': {
const streamDashD = addStreamingParams(inputExample)
return `curl -X POST \\\n -H "X-API-Key: $SIM_API_KEY" \\\n -H "Content-Type: application/json"${streamDashD} \\\n ${baseEndpoint}`
}

case 'async':
switch (exampleType) {
case 'execute':
return `curl -X POST \\
-H "X-API-Key: $SIM_API_KEY" \\
-H "Content-Type: application/json" \\
-H "X-Execution-Mode: async"${inputExample} \\
${baseEndpoint}`
return `curl -X POST \\\n -H "X-API-Key: $SIM_API_KEY" \\\n -H "Content-Type: application/json" \\\n -H "X-Execution-Mode: async"${inputExample} \\\n ${baseEndpoint}`

case 'status': {
const baseUrl = baseEndpoint.split('/api/workflows/')[0]
return `curl -H "X-API-Key: $SIM_API_KEY" \\
${baseUrl}/api/jobs/JOB_ID_FROM_EXECUTION`
return `curl -H "X-API-Key: $SIM_API_KEY" \\\n ${baseUrl}/api/jobs/JOB_ID_FROM_EXECUTION`
}

case 'rate-limits': {
const baseUrlForRateLimit = baseEndpoint.split('/api/workflows/')[0]
return `curl -H "X-API-Key: $SIM_API_KEY" \\
${baseUrlForRateLimit}/api/users/me/usage-limits`
return `curl -H "X-API-Key: $SIM_API_KEY" \\\n ${baseUrlForRateLimit}/api/users/me/usage-limits`
}

default:
Expand Down Expand Up @@ -231,6 +231,7 @@ export function ExampleCommand({
selectedOutputs={selectedStreamingOutputs}
onOutputSelect={onSelectedStreamingOutputsChange}
placeholder='Select outputs for streaming'
valueMode='label'
/>
</div>
)}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ interface OutputSelectProps {
onOutputSelect: (outputIds: string[]) => void
disabled?: boolean
placeholder?: string
valueMode?: 'id' | 'label'
}

export function OutputSelect({
Expand All @@ -22,6 +23,7 @@ export function OutputSelect({
onOutputSelect,
disabled = false,
placeholder = 'Select output sources',
valueMode = 'id',
}: OutputSelectProps) {
const [isOutputDropdownOpen, setIsOutputDropdownOpen] = useState(false)
const dropdownRef = useRef<HTMLDivElement>(null)
Expand Down Expand Up @@ -201,28 +203,31 @@ export function OutputSelect({
return outputs
}, [workflowBlocks, workflowId, isShowingDiff, isDiffReady, diffWorkflow, blocks, subBlockValues])

// Utility to check selected by id or label
const isSelectedValue = (o: { id: string; label: string }) =>
selectedOutputs.includes(o.id) || selectedOutputs.includes(o.label)

// Get selected outputs display text
const selectedOutputsDisplayText = useMemo(() => {
if (!selectedOutputs || selectedOutputs.length === 0) {
return placeholder
}

// Ensure all selected outputs exist in the workflowOutputs array
const validOutputs = selectedOutputs.filter((id) => workflowOutputs.some((o) => o.id === id))
// Ensure all selected outputs exist in the workflowOutputs array by id or label
const validOutputs = selectedOutputs.filter((val) =>
workflowOutputs.some((o) => o.id === val || o.label === val)
)

if (validOutputs.length === 0) {
return placeholder
}

if (validOutputs.length === 1) {
const output = workflowOutputs.find((o) => o.id === validOutputs[0])
const output = workflowOutputs.find(
(o) => o.id === validOutputs[0] || o.label === validOutputs[0]
)
if (output) {
// Add defensive check for output.blockName
const blockNameText =
output.blockName && typeof output.blockName === 'string'
? output.blockName.replace(/\s+/g, '').toLowerCase()
: `block-${output.blockId}`
return `${blockNameText}.${output.path}`
return output.label
}
return placeholder
}
Expand All @@ -234,10 +239,14 @@ export function OutputSelect({
const selectedOutputInfo = useMemo(() => {
if (!selectedOutputs || selectedOutputs.length === 0) return null

const validOutputs = selectedOutputs.filter((id) => workflowOutputs.some((o) => o.id === id))
const validOutputs = selectedOutputs.filter((val) =>
workflowOutputs.some((o) => o.id === val || o.label === val)
)
if (validOutputs.length === 0) return null

const output = workflowOutputs.find((o) => o.id === validOutputs[0])
const output = workflowOutputs.find(
(o) => o.id === validOutputs[0] || o.label === validOutputs[0]
)
if (!output) return null

return {
Expand Down Expand Up @@ -355,33 +364,41 @@ export function OutputSelect({
}

let attachedScrollTargets: (HTMLElement | Window)[] = []
let rafId: number | null = null
if (isOutputDropdownOpen) {
updatePosition()
window.addEventListener('resize', updatePosition)
// Attach to all scrollable ancestors (including the modal's scroll container)
attachedScrollTargets = getScrollableAncestors(dropdownRef.current)
attachedScrollTargets.forEach((target) =>
target.addEventListener('scroll', updatePosition, { passive: true })
)
const loop = () => {
updatePosition()
rafId = requestAnimationFrame(loop)
}
rafId = requestAnimationFrame(loop)
}

return () => {
window.removeEventListener('resize', updatePosition)
attachedScrollTargets.forEach((target) =>
target.removeEventListener('scroll', updatePosition)
)
if (rafId) cancelAnimationFrame(rafId)
}
}, [isOutputDropdownOpen])

// Handle output selection - toggle selection
const handleOutputSelection = (value: string) => {
const emittedValue =
valueMode === 'label' ? value : workflowOutputs.find((o) => o.label === value)?.id || value
let newSelectedOutputs: string[]
const index = selectedOutputs.indexOf(value)
const index = selectedOutputs.indexOf(emittedValue)

if (index === -1) {
newSelectedOutputs = [...new Set([...selectedOutputs, value])]
newSelectedOutputs = [...new Set([...selectedOutputs, emittedValue])]
} else {
newSelectedOutputs = selectedOutputs.filter((id) => id !== value)
newSelectedOutputs = selectedOutputs.filter((id) => id !== emittedValue)
}

onOutputSelect(newSelectedOutputs)
Expand Down Expand Up @@ -434,7 +451,7 @@ export function OutputSelect({
ref={portalRef}
style={{
position: 'fixed',
top: portalStyle.top,
top: portalStyle.top - 1, // overlap border by 1px to avoid visible gap
left: portalStyle.left,
width: portalStyle.width,
zIndex: 2147483647,
Expand Down Expand Up @@ -462,7 +479,7 @@ export function OutputSelect({
<button
type='button'
key={output.id}
onClick={() => handleOutputSelection(output.id)}
onClick={() => handleOutputSelection(output.label)}
className={cn(
'flex w-full items-center gap-2 px-3 py-1.5 text-left font-normal text-sm',
'hover:bg-accent hover:text-accent-foreground',
Expand All @@ -480,7 +497,7 @@ export function OutputSelect({
</span>
</div>
<span className='flex-1 truncate'>{output.path}</span>
{selectedOutputs.includes(output.id) && (
{isSelectedValue(output) && (
<Check className='h-4 w-4 flex-shrink-0 text-muted-foreground' />
)}
</button>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -510,7 +510,7 @@ export function ApiKeys({ onOpenChange, registerCloseHandler }: ApiKeysProps) {
setKeyType('personal')
if (createError) setCreateError(null)
}}
className='h-8'
className='h-8 data-[variant=outline]:border-border data-[variant=outline]:bg-background data-[variant=outline]:text-foreground data-[variant=outline]:hover:bg-muted dark:data-[variant=outline]:border-border dark:data-[variant=outline]:bg-background dark:data-[variant=outline]:text-foreground dark:data-[variant=outline]:hover:bg-muted/80'
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

style: This extensive inline styling could be extracted to a reusable CSS class or component variant for better maintainability

Prompt To Fix With AI
This is a comment left during a code review.
Path: apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/components/api-keys/api-keys.tsx
Line: 513:513

Comment:
**style:** This extensive inline styling could be extracted to a reusable CSS class or component variant for better maintainability

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

>
Personal
</Button>
Expand All @@ -522,7 +522,7 @@ export function ApiKeys({ onOpenChange, registerCloseHandler }: ApiKeysProps) {
setKeyType('workspace')
if (createError) setCreateError(null)
}}
className='h-8'
className='h-8 data-[variant=outline]:border-border data-[variant=outline]:bg-background data-[variant=outline]:text-foreground data-[variant=outline]:hover:bg-muted dark:data-[variant=outline]:border-border dark:data-[variant=outline]:bg-background dark:data-[variant=outline]:text-foreground dark:data-[variant=outline]:hover:bg-muted/80'
>
Workspace
</Button>
Expand All @@ -549,7 +549,7 @@ export function ApiKeys({ onOpenChange, registerCloseHandler }: ApiKeysProps) {

<AlertDialogFooter className='flex'>
<AlertDialogCancel
className='h-9 w-full rounded-[8px]'
className='h-9 w-full rounded-[8px] border-border bg-background text-foreground hover:bg-muted dark:border-border dark:bg-background dark:text-foreground dark:hover:bg-muted/80'
onClick={() => {
setNewKeyName('')
setKeyType('personal')
Expand Down