Skip to content

Commit 7a5f66b

Browse files
fix(workflow-editor): correlate drag-release palette selection with a token
1 parent 8da6b27 commit 7a5f66b

4 files changed

Lines changed: 42 additions & 14 deletions

File tree

apps/sim/app/workspace/[workspaceId]/w/[workflowId]/workflow.tsx

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,7 @@ interface AddBlockFromToolbarDetail {
209209
type?: unknown
210210
enableTriggerMode?: unknown
211211
presetOperation?: unknown
212+
connectToken?: unknown
212213
}
213214

214215
/**
@@ -539,6 +540,7 @@ const WorkflowContent = React.memo(
539540
source: { nodeId: string; handleId: string }
540541
screenX: number
541542
screenY: number
543+
token: string
542544
} | null>(null)
543545

544546
/** Stores start positions for multi-node drag undo/redo recording. */
@@ -1984,18 +1986,21 @@ const WorkflowContent = React.memo(
19841986
return
19851987
}
19861988

1987-
const { type, enableTriggerMode, presetOperation } = event.detail
1989+
const { type, enableTriggerMode, presetOperation, connectToken } = event.detail
19881990

19891991
if (typeof type !== 'string' || !type) return
19901992
if (type === 'connectionBlock') return
19911993

1992-
// Consume a pending drag-release only while the restricted connect-palette
1993-
// it opened is still up — never let an unrelated add-block event (toolbar,
1994-
// sidebar, command list) inherit its position/source.
1995-
const searchModal = useSearchModalStore.getState()
1994+
// Consume a pending drag-release only when THIS event is the palette
1995+
// selection it opened, matched by correlation token. Any other
1996+
// add-block event (toolbar, sidebar, command list) carries no matching
1997+
// token and cannot inherit the pending position/source.
1998+
const pendingRef = pendingConnectRef.current
19961999
const pending =
1997-
searchModal.isOpen && searchModal.sections ? pendingConnectRef.current : null
1998-
pendingConnectRef.current = null
2000+
pendingRef && typeof connectToken === 'string' && connectToken === pendingRef.token
2001+
? pendingRef
2002+
: null
2003+
if (pending) pendingConnectRef.current = null
19992004

20002005
let basePosition = getViewportCenter()
20012006
if (pending) {
@@ -3205,13 +3210,16 @@ const WorkflowContent = React.memo(
32053210
// Released on empty canvas: open the command palette and remember the
32063211
// drag origin + drop point so the chosen block lands here, wired from
32073212
// this source handle.
3213+
const connectToken = generateId()
32083214
pendingConnectRef.current = {
32093215
source: { nodeId: source.nodeId, handleId: source.handleId },
32103216
screenX: clientPos.clientX,
32113217
screenY: clientPos.clientY,
3218+
token: connectToken,
32123219
}
32133220
useSearchModalStore.getState().open({
32143221
sections: ['blocks', 'tools', 'toolOperations'],
3222+
connectToken,
32153223
})
32163224
}
32173225

apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/search-modal/search-modal.tsx

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -351,6 +351,7 @@ export function SearchModal({
351351
detail: {
352352
type: block.type,
353353
enableTriggerMode,
354+
connectToken: useSearchModalStore.getState().connectToken,
354355
},
355356
})
356357
)
@@ -368,7 +369,11 @@ export function SearchModal({
368369
(op: SearchToolOperationItem) => {
369370
window.dispatchEvent(
370371
new CustomEvent('add-block-from-toolbar', {
371-
detail: { type: op.blockType, presetOperation: op.operationId },
372+
detail: {
373+
type: op.blockType,
374+
presetOperation: op.operationId,
375+
connectToken: useSearchModalStore.getState().connectToken,
376+
},
372377
})
373378
)
374379
captureEvent(posthogRef.current, 'search_result_selected', {

apps/sim/stores/modals/search/store.ts

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -68,18 +68,23 @@ export const useSearchModalStore = create<SearchModalState>()(
6868
(set, _) => ({
6969
isOpen: false,
7070
sections: null,
71+
connectToken: null,
7172
data: initialData,
7273

7374
setOpen: (open: boolean) => {
74-
set({ isOpen: open, sections: null })
75+
set({ isOpen: open, sections: null, connectToken: null })
7576
},
7677

7778
open: (options) => {
78-
set({ isOpen: true, sections: options?.sections ?? null })
79+
set({
80+
isOpen: true,
81+
sections: options?.sections ?? null,
82+
connectToken: options?.connectToken ?? null,
83+
})
7984
},
8085

8186
close: () => {
82-
set({ isOpen: false, sections: null })
87+
set({ isOpen: false, sections: null, connectToken: null })
8388
},
8489

8590
initializeData: (filterBlocks) => {

apps/sim/stores/modals/search/types.ts

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -91,20 +91,30 @@ export interface SearchModalState {
9191
*/
9292
sections: SearchSection[] | null
9393

94+
/**
95+
* Correlation token for the open session. When the palette is opened to
96+
* complete a specific action (e.g. an edge drag-release), the opener passes a
97+
* token that a selection stamps onto its event, so only that selection — not
98+
* an unrelated concurrent add — can consume the pending action. `null` for
99+
* ordinary opens.
100+
*/
101+
connectToken: string | null
102+
94103
/** Pre-computed search data. */
95104
data: SearchData
96105

97106
/**
98107
* Explicitly set the open state of the modal. Always resets to the full
99-
* palette (no section restriction).
108+
* palette (no section restriction, no correlation token).
100109
*/
101110
setOpen: (open: boolean) => void
102111

103112
/**
104113
* Convenience method to open the modal. Pass `sections` to restrict the
105-
* palette to a subset of result groups.
114+
* palette to a subset of result groups, and `connectToken` to correlate a
115+
* selection with a pending action.
106116
*/
107-
open: (options?: { sections?: SearchSection[] }) => void
117+
open: (options?: { sections?: SearchSection[]; connectToken?: string }) => void
108118

109119
/**
110120
* Convenience method to close the modal.

0 commit comments

Comments
 (0)