mirror of
https://github.com/simstudioai/sim.git
synced 2026-01-09 15:07:55 -05:00
fix(curl-example): fixed curl example in deploy modal to reflect selected option (#1573)
This commit is contained in:
@@ -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')
|
||||
|
||||
@@ -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>
|
||||
)}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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')
|
||||
|
||||
Reference in New Issue
Block a user