fix(curl-example): fixed curl example in deploy modal to reflect selected option (#1573)

This commit is contained in:
Waleed
2025-10-07 18:46:12 -07:00
committed by GitHub
parent 8e43774b5e
commit 7bdf0e94d7
4 changed files with 72 additions and 54 deletions

View File

@@ -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>
@@ -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>
@@ -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')

View File

@@ -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
@@ -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:
@@ -231,6 +231,7 @@ export function ExampleCommand({
selectedOutputs={selectedStreamingOutputs}
onOutputSelect={onSelectedStreamingOutputsChange}
placeholder='Select outputs for streaming'
valueMode='label'
/>
</div>
)}

View File

@@ -14,6 +14,7 @@ interface OutputSelectProps {
onOutputSelect: (outputIds: string[]) => void
disabled?: boolean
placeholder?: string
valueMode?: 'id' | 'label'
}
export function OutputSelect({
@@ -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)
@@ -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
}
@@ -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 {
@@ -355,14 +364,19 @@ 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 () => {
@@ -370,18 +384,21 @@ export function OutputSelect({
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)
@@ -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,
@@ -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',
@@ -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>

View File

@@ -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'
>
Personal
</Button>
@@ -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>
@@ -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')