mirror of
https://github.com/simstudioai/sim.git
synced 2026-01-21 12:58:07 -05:00
Compare commits
2 Commits
v0.5.66
...
fix/ring-h
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7411095e43 | ||
|
|
d900267cc3 |
@@ -11,7 +11,7 @@ import { preprocessExecution } from '@/lib/execution/preprocessing'
|
||||
import { LoggingSession } from '@/lib/logs/execution/logging-session'
|
||||
import { normalizeInputFormatValue } from '@/lib/workflows/input-format'
|
||||
import { createStreamingResponse } from '@/lib/workflows/streaming/streaming'
|
||||
import { isValidStartBlockType } from '@/lib/workflows/triggers/start-block-types'
|
||||
import { isInputDefinitionTrigger } from '@/lib/workflows/triggers/input-definition-triggers'
|
||||
import { setFormAuthCookie, validateFormAuth } from '@/app/api/form/utils'
|
||||
import { createErrorResponse, createSuccessResponse } from '@/app/api/workflows/utils'
|
||||
|
||||
@@ -36,7 +36,7 @@ async function getWorkflowInputSchema(workflowId: string): Promise<any[]> {
|
||||
.from(workflowBlocks)
|
||||
.where(eq(workflowBlocks.workflowId, workflowId))
|
||||
|
||||
const startBlock = blocks.find((block) => isValidStartBlockType(block.type))
|
||||
const startBlock = blocks.find((block) => isInputDefinitionTrigger(block.type))
|
||||
|
||||
if (!startBlock) {
|
||||
return []
|
||||
|
||||
@@ -2,7 +2,7 @@ import { memo, useCallback } from 'react'
|
||||
import { ArrowLeftRight, ArrowUpDown, Circle, CircleOff, LogOut } from 'lucide-react'
|
||||
import { Button, Copy, Tooltip, Trash2 } from '@/components/emcn'
|
||||
import { cn } from '@/lib/core/utils/cn'
|
||||
import { isValidStartBlockType } from '@/lib/workflows/triggers/start-block-types'
|
||||
import { isInputDefinitionTrigger } from '@/lib/workflows/triggers/input-definition-triggers'
|
||||
import { useUserPermissionsContext } from '@/app/workspace/[workspaceId]/providers/workspace-permissions-provider'
|
||||
import { useCollaborativeWorkflow } from '@/hooks/use-collaborative-workflow'
|
||||
import { useWorkflowRegistry } from '@/stores/workflows/registry/store'
|
||||
@@ -90,7 +90,7 @@ export const ActionBar = memo(
|
||||
|
||||
const userPermissions = useUserPermissionsContext()
|
||||
|
||||
const isStartBlock = isValidStartBlockType(blockType)
|
||||
const isStartBlock = isInputDefinitionTrigger(blockType)
|
||||
const isResponseBlock = blockType === 'response'
|
||||
const isNoteBlock = blockType === 'note'
|
||||
const isSubflowBlock = blockType === 'loop' || blockType === 'parallel'
|
||||
|
||||
@@ -8,7 +8,7 @@ import {
|
||||
PopoverDivider,
|
||||
PopoverItem,
|
||||
} from '@/components/emcn'
|
||||
import { isValidStartBlockType } from '@/lib/workflows/triggers/start-block-types'
|
||||
import { TriggerUtils } from '@/lib/workflows/triggers/triggers'
|
||||
|
||||
/**
|
||||
* Block information for context menu actions
|
||||
@@ -74,12 +74,16 @@ export function BlockMenu({
|
||||
const allEnabled = selectedBlocks.every((b) => b.enabled)
|
||||
const allDisabled = selectedBlocks.every((b) => !b.enabled)
|
||||
|
||||
const hasStarterBlock = selectedBlocks.some((b) => isValidStartBlockType(b.type))
|
||||
const hasSingletonBlock = selectedBlocks.some(
|
||||
(b) =>
|
||||
TriggerUtils.requiresSingleInstance(b.type) || TriggerUtils.isSingleInstanceBlockType(b.type)
|
||||
)
|
||||
const hasTriggerBlock = selectedBlocks.some((b) => TriggerUtils.isTriggerBlock(b))
|
||||
const allNoteBlocks = selectedBlocks.every((b) => b.type === 'note')
|
||||
const isSubflow =
|
||||
isSingleBlock && (selectedBlocks[0]?.type === 'loop' || selectedBlocks[0]?.type === 'parallel')
|
||||
|
||||
const canRemoveFromSubflow = showRemoveFromSubflow && !hasStarterBlock
|
||||
const canRemoveFromSubflow = showRemoveFromSubflow && !hasTriggerBlock
|
||||
|
||||
const getToggleEnabledLabel = () => {
|
||||
if (allEnabled) return 'Disable'
|
||||
@@ -127,7 +131,7 @@ export function BlockMenu({
|
||||
<span>Paste</span>
|
||||
<span className='ml-auto opacity-70 group-hover:opacity-100'>⌘V</span>
|
||||
</PopoverItem>
|
||||
{!hasStarterBlock && (
|
||||
{!hasSingletonBlock && (
|
||||
<PopoverItem
|
||||
disabled={disableEdit}
|
||||
onClick={() => {
|
||||
|
||||
@@ -17,7 +17,7 @@ import { Skeleton } from '@/components/ui'
|
||||
import { isDev } from '@/lib/core/config/feature-flags'
|
||||
import { cn } from '@/lib/core/utils/cn'
|
||||
import { getBaseUrl, getEmailDomain } from '@/lib/core/utils/urls'
|
||||
import { isValidStartBlockType } from '@/lib/workflows/triggers/start-block-types'
|
||||
import { isInputDefinitionTrigger } from '@/lib/workflows/triggers/input-definition-triggers'
|
||||
import {
|
||||
type FieldConfig,
|
||||
useCreateForm,
|
||||
@@ -147,7 +147,7 @@ export function FormDeploy({
|
||||
|
||||
useEffect(() => {
|
||||
const blocks = Object.values(useWorkflowStore.getState().blocks)
|
||||
const startBlock = blocks.find((b) => isValidStartBlockType(b.type))
|
||||
const startBlock = blocks.find((b) => isInputDefinitionTrigger(b.type))
|
||||
|
||||
if (startBlock) {
|
||||
const inputFormat = useSubBlockStore.getState().getValue(startBlock.id, 'inputFormat')
|
||||
|
||||
@@ -14,7 +14,7 @@ import {
|
||||
Textarea,
|
||||
} from '@/components/emcn'
|
||||
import { normalizeInputFormatValue } from '@/lib/workflows/input-format'
|
||||
import { isValidStartBlockType } from '@/lib/workflows/triggers/start-block-types'
|
||||
import { isInputDefinitionTrigger } from '@/lib/workflows/triggers/input-definition-triggers'
|
||||
import type { InputFormatField } from '@/lib/workflows/types'
|
||||
import { useWorkflowRegistry } from '@/stores/workflows/registry/store'
|
||||
import { useSubBlockStore } from '@/stores/workflows/subblock/store'
|
||||
@@ -52,7 +52,7 @@ export function ApiInfoModal({ open, onOpenChange, workflowId }: ApiInfoModalPro
|
||||
for (const [blockId, block] of Object.entries(blocks)) {
|
||||
if (!block || typeof block !== 'object') continue
|
||||
const blockType = (block as { type?: string }).type
|
||||
if (blockType && isValidStartBlockType(blockType)) {
|
||||
if (blockType && isInputDefinitionTrigger(blockType)) {
|
||||
return blockId
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ import {
|
||||
import { Skeleton } from '@/components/ui'
|
||||
import { generateToolInputSchema, sanitizeToolName } from '@/lib/mcp/workflow-tool-schema'
|
||||
import { normalizeInputFormatValue } from '@/lib/workflows/input-format'
|
||||
import { isValidStartBlockType } from '@/lib/workflows/triggers/start-block-types'
|
||||
import { isInputDefinitionTrigger } from '@/lib/workflows/triggers/input-definition-triggers'
|
||||
import type { InputFormatField } from '@/lib/workflows/types'
|
||||
import {
|
||||
useAddWorkflowMcpTool,
|
||||
@@ -107,7 +107,7 @@ export function McpDeploy({
|
||||
for (const [blockId, block] of Object.entries(blocks)) {
|
||||
if (!block || typeof block !== 'object') continue
|
||||
const blockType = (block as { type?: string }).type
|
||||
if (blockType && isValidStartBlockType(blockType)) {
|
||||
if (blockType && isInputDefinitionTrigger(blockType)) {
|
||||
return blockId
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ import { useMemo } from 'react'
|
||||
import { useShallow } from 'zustand/react/shallow'
|
||||
import { BlockPathCalculator } from '@/lib/workflows/blocks/block-path-calculator'
|
||||
import { SYSTEM_REFERENCE_PREFIXES } from '@/lib/workflows/sanitization/references'
|
||||
import { isValidStartBlockType } from '@/lib/workflows/triggers/start-block-types'
|
||||
import { isInputDefinitionTrigger } from '@/lib/workflows/triggers/input-definition-triggers'
|
||||
import { normalizeName } from '@/executor/constants'
|
||||
import { useWorkflowStore } from '@/stores/workflows/workflow/store'
|
||||
import type { Loop, Parallel } from '@/stores/workflows/workflow/types'
|
||||
@@ -27,7 +27,7 @@ export function useAccessibleReferencePrefixes(blockId?: string | null): Set<str
|
||||
const accessibleIds = new Set<string>(ancestorIds)
|
||||
accessibleIds.add(blockId)
|
||||
|
||||
const starterBlock = Object.values(blocks).find((block) => isValidStartBlockType(block.type))
|
||||
const starterBlock = Object.values(blocks).find((block) => isInputDefinitionTrigger(block.type))
|
||||
if (starterBlock && ancestorIds.includes(starterBlock.id)) {
|
||||
accessibleIds.add(starterBlock.id)
|
||||
}
|
||||
|
||||
@@ -180,6 +180,21 @@ function mapEdgesByNode(edges: Edge[], nodeIds: Set<string>): Map<string, Edge[]
|
||||
return result
|
||||
}
|
||||
|
||||
/**
|
||||
* Syncs the panel editor with the current selection state.
|
||||
* Shows block details when exactly one block is selected, clears otherwise.
|
||||
*/
|
||||
function syncPanelWithSelection(selectedIds: string[]) {
|
||||
const { currentBlockId, clearCurrentBlock, setCurrentBlockId } = usePanelEditorStore.getState()
|
||||
if (selectedIds.length === 1 && selectedIds[0] !== currentBlockId) {
|
||||
setCurrentBlockId(selectedIds[0])
|
||||
} else if (selectedIds.length === 0 && currentBlockId) {
|
||||
clearCurrentBlock()
|
||||
} else if (selectedIds.length > 1 && currentBlockId) {
|
||||
clearCurrentBlock()
|
||||
}
|
||||
}
|
||||
|
||||
/** Custom node types for ReactFlow. */
|
||||
const nodeTypes: NodeTypes = {
|
||||
workflowBlock: WorkflowBlock,
|
||||
@@ -2075,7 +2090,10 @@ const WorkflowContent = React.memo(() => {
|
||||
...node,
|
||||
selected: pendingSet.has(node.id),
|
||||
}))
|
||||
setDisplayNodes(resolveParentChildSelectionConflicts(withSelection, blocks))
|
||||
const resolved = resolveParentChildSelectionConflicts(withSelection, blocks)
|
||||
setDisplayNodes(resolved)
|
||||
const selectedIds = resolved.filter((node) => node.selected).map((node) => node.id)
|
||||
syncPanelWithSelection(selectedIds)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -2175,13 +2193,7 @@ const WorkflowContent = React.memo(() => {
|
||||
})
|
||||
const selectedIds = selectedIdsRef.current as string[] | null
|
||||
if (selectedIds !== null) {
|
||||
const { currentBlockId, clearCurrentBlock, setCurrentBlockId } =
|
||||
usePanelEditorStore.getState()
|
||||
if (selectedIds.length === 1 && selectedIds[0] !== currentBlockId) {
|
||||
setCurrentBlockId(selectedIds[0])
|
||||
} else if (selectedIds.length === 0 && currentBlockId) {
|
||||
clearCurrentBlock()
|
||||
}
|
||||
syncPanelWithSelection(selectedIds)
|
||||
}
|
||||
},
|
||||
[blocks]
|
||||
|
||||
@@ -17,7 +17,7 @@ import {
|
||||
type GetBlockUpstreamReferencesResultType,
|
||||
} from '@/lib/copilot/tools/shared/schemas'
|
||||
import { BlockPathCalculator } from '@/lib/workflows/blocks/block-path-calculator'
|
||||
import { isValidStartBlockType } from '@/lib/workflows/triggers/start-block-types'
|
||||
import { isInputDefinitionTrigger } from '@/lib/workflows/triggers/input-definition-triggers'
|
||||
import { useWorkflowRegistry } from '@/stores/workflows/registry/store'
|
||||
import { useWorkflowStore } from '@/stores/workflows/workflow/store'
|
||||
import type { Loop, Parallel } from '@/stores/workflows/workflow/types'
|
||||
@@ -141,7 +141,7 @@ export class GetBlockUpstreamReferencesClientTool extends BaseClientTool {
|
||||
const accessibleIds = new Set<string>(ancestorIds)
|
||||
accessibleIds.add(blockId)
|
||||
|
||||
const starterBlock = Object.values(blocks).find((b) => isValidStartBlockType(b.type))
|
||||
const starterBlock = Object.values(blocks).find((b) => isInputDefinitionTrigger(b.type))
|
||||
if (starterBlock && ancestorIds.includes(starterBlock.id)) {
|
||||
accessibleIds.add(starterBlock.id)
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { z } from 'zod'
|
||||
import { normalizeInputFormatValue } from '@/lib/workflows/input-format'
|
||||
import { isValidStartBlockType } from '@/lib/workflows/triggers/start-block-types'
|
||||
import { isInputDefinitionTrigger } from '@/lib/workflows/triggers/input-definition-triggers'
|
||||
import type { InputFormatField } from '@/lib/workflows/types'
|
||||
import type { McpToolSchema } from './types'
|
||||
|
||||
@@ -217,7 +217,7 @@ export function extractInputFormatFromBlocks(
|
||||
const blockObj = block as Record<string, unknown>
|
||||
const blockType = blockObj.type as string
|
||||
|
||||
if (isValidStartBlockType(blockType)) {
|
||||
if (isInputDefinitionTrigger(blockType)) {
|
||||
// Try to get inputFormat from subBlocks.inputFormat.value
|
||||
const subBlocks = blockObj.subBlocks as Record<string, { value?: unknown }> | undefined
|
||||
const subBlockValue = subBlocks?.inputFormat?.value
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { isValidStartBlockType } from '@/lib/workflows/triggers/start-block-types'
|
||||
import { isInputDefinitionTrigger } from '@/lib/workflows/triggers/input-definition-triggers'
|
||||
import type { InputFormatField } from '@/lib/workflows/types'
|
||||
|
||||
/**
|
||||
@@ -25,7 +25,7 @@ export function extractInputFieldsFromBlocks(
|
||||
// Find trigger block
|
||||
const triggerEntry = Object.entries(blocks).find(([, block]) => {
|
||||
const b = block as Record<string, unknown>
|
||||
return typeof b.type === 'string' && isValidStartBlockType(b.type)
|
||||
return typeof b.type === 'string' && isInputDefinitionTrigger(b.type)
|
||||
})
|
||||
|
||||
if (!triggerEntry) return []
|
||||
|
||||
28
apps/sim/lib/workflows/triggers/input-definition-triggers.ts
Normal file
28
apps/sim/lib/workflows/triggers/input-definition-triggers.ts
Normal file
@@ -0,0 +1,28 @@
|
||||
/**
|
||||
* Trigger types that define workflow input parameters (inputFormat).
|
||||
* These are triggers where users can configure input schema for the workflow.
|
||||
*
|
||||
* This module is kept lightweight with no dependencies to avoid circular imports.
|
||||
*
|
||||
* Note: External triggers like webhook/schedule are NOT included here because
|
||||
* they receive input from external event payloads, not user-defined inputFormat.
|
||||
*/
|
||||
export const INPUT_DEFINITION_TRIGGER_TYPES = [
|
||||
'starter',
|
||||
'start',
|
||||
'start_trigger',
|
||||
'api_trigger',
|
||||
'input_trigger',
|
||||
] as const
|
||||
|
||||
export type InputDefinitionTriggerType = (typeof INPUT_DEFINITION_TRIGGER_TYPES)[number]
|
||||
|
||||
/**
|
||||
* Check if a block type is a trigger that defines workflow input parameters.
|
||||
* Used to find blocks that have inputFormat subblock for workflow input schema.
|
||||
*/
|
||||
export function isInputDefinitionTrigger(
|
||||
blockType: string
|
||||
): blockType is InputDefinitionTriggerType {
|
||||
return INPUT_DEFINITION_TRIGGER_TYPES.includes(blockType as InputDefinitionTriggerType)
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
/**
|
||||
* Valid start block types that can trigger a workflow
|
||||
* This module is kept lightweight with no dependencies to avoid circular imports
|
||||
*/
|
||||
export const VALID_START_BLOCK_TYPES = [
|
||||
'starter',
|
||||
'start',
|
||||
'start_trigger',
|
||||
'api',
|
||||
'api_trigger',
|
||||
'input_trigger',
|
||||
] as const
|
||||
|
||||
export type ValidStartBlockType = (typeof VALID_START_BLOCK_TYPES)[number]
|
||||
|
||||
/**
|
||||
* Check if a block type is a valid start block type
|
||||
*/
|
||||
export function isValidStartBlockType(blockType: string): blockType is ValidStartBlockType {
|
||||
return VALID_START_BLOCK_TYPES.includes(blockType as ValidStartBlockType)
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
import { createLogger } from '@sim/logger'
|
||||
import { isValidStartBlockType } from '@/lib/workflows/triggers/start-block-types'
|
||||
import { isInputDefinitionTrigger } from '@/lib/workflows/triggers/input-definition-triggers'
|
||||
import {
|
||||
type StartBlockCandidate,
|
||||
StartBlockPath,
|
||||
@@ -22,7 +22,7 @@ export function hasValidStartBlockInState(state: WorkflowState | null | undefine
|
||||
|
||||
const startBlock = Object.values(state.blocks).find((block: BlockState) => {
|
||||
const blockType = block?.type
|
||||
return isValidStartBlockType(blockType)
|
||||
return isInputDefinitionTrigger(blockType)
|
||||
})
|
||||
|
||||
return !!startBlock
|
||||
|
||||
Reference in New Issue
Block a user