Skip to content

Commit 1a7de84

Browse files
fix(tag-dropdown): last char dropped bug (#945)
1 parent a2dea38 commit 1a7de84

File tree

6 files changed

+55
-30
lines changed

6 files changed

+55
-30
lines changed

apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/subflows/components/iteration-badges/iteration-badges.tsx

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -158,19 +158,23 @@ export function IterationBadges({ nodeId, data, iterationType }: IterationBadges
158158
const handleEditorChange = useCallback(
159159
(value: string) => {
160160
if (isPreview) return
161-
collaborativeUpdateIterationCollection(nodeId, iterationType, value)
162161

163-
const textarea = editorContainerRef.current?.querySelector('textarea')
162+
// Capture cursor first to minimize staleness in dropdown logic
163+
const textarea = editorContainerRef.current?.querySelector(
164+
'textarea'
165+
) as HTMLTextAreaElement | null
166+
const cursorPos = textarea?.selectionStart ?? cursorPosition
164167
if (textarea) {
165168
textareaRef.current = textarea
166-
const cursorPos = textarea.selectionStart || 0
167-
setCursorPosition(cursorPos)
168-
169-
const triggerCheck = checkTagTrigger(value, cursorPos)
170-
setShowTagDropdown(triggerCheck.show)
171169
}
170+
setCursorPosition(cursorPos)
171+
172+
collaborativeUpdateIterationCollection(nodeId, iterationType, value)
173+
174+
const triggerCheck = checkTagTrigger(value, cursorPos)
175+
setShowTagDropdown(triggerCheck.show)
172176
},
173-
[nodeId, iterationType, collaborativeUpdateIterationCollection, isPreview]
177+
[nodeId, iterationType, collaborativeUpdateIterationCollection, isPreview, cursorPosition]
174178
)
175179

176180
// Handle tag selection

apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/workflow-block/components/sub-block/components/code.tsx

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -446,24 +446,25 @@ export function Code({
446446
value={code}
447447
onValueChange={(newCode) => {
448448
if (!isCollapsed && !isAiStreaming && !isPreview && !disabled) {
449+
// Capture cursor first to minimize staleness in dropdown logic
450+
const textarea = editorRef.current?.querySelector(
451+
'textarea'
452+
) as HTMLTextAreaElement | null
453+
const pos = textarea?.selectionStart ?? cursorPosition
454+
setCursorPosition(pos)
455+
449456
setCode(newCode)
450457
setStoreValue(newCode)
451458

452-
const textarea = editorRef.current?.querySelector('textarea')
453-
if (textarea) {
454-
const pos = textarea.selectionStart
455-
setCursorPosition(pos)
456-
457-
const tagTrigger = checkTagTrigger(newCode, pos)
458-
setShowTags(tagTrigger.show)
459-
if (!tagTrigger.show) {
460-
setActiveSourceBlockId(null)
461-
}
462-
463-
const envVarTrigger = checkEnvVarTrigger(newCode, pos)
464-
setShowEnvVars(envVarTrigger.show)
465-
setSearchTerm(envVarTrigger.show ? envVarTrigger.searchTerm : '')
459+
const tagTrigger = checkTagTrigger(newCode, pos)
460+
setShowTags(tagTrigger.show)
461+
if (!tagTrigger.show) {
462+
setActiveSourceBlockId(null)
466463
}
464+
465+
const envVarTrigger = checkEnvVarTrigger(newCode, pos)
466+
setShowEnvVars(envVarTrigger.show)
467+
setSearchTerm(envVarTrigger.show ? envVarTrigger.searchTerm : '')
467468
}
468469
}}
469470
onKeyDown={(e) => {

apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/workflow-block/components/sub-block/components/combobox.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -150,13 +150,14 @@ export function ComboBox({
150150
const newValue = e.target.value
151151
const newCursorPosition = e.target.selectionStart ?? 0
152152

153+
// Update cursor first to reduce staleness for dropdown logic
154+
setCursorPosition(newCursorPosition)
155+
153156
// Update store value immediately (allow free text)
154157
if (!isPreview) {
155158
setStoreValue(newValue)
156159
}
157160

158-
setCursorPosition(newCursorPosition)
159-
160161
// Check for environment variables trigger
161162
const envVarTrigger = checkEnvVarTrigger(newValue, newCursorPosition)
162163
setShowEnvVars(envVarTrigger.show)

apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/workflow-block/components/sub-block/components/long-input.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,9 @@ export function LongInput({
148148
const newValue = e.target.value
149149
const newCursorPosition = e.target.selectionStart ?? 0
150150

151+
// Update cursor first to minimize state staleness for dropdown selection logic
152+
setCursorPosition(newCursorPosition)
153+
151154
// Update local content immediately
152155
setLocalContent(newValue)
153156

@@ -158,8 +161,6 @@ export function LongInput({
158161
setStoreValue(newValue)
159162
}
160163

161-
setCursorPosition(newCursorPosition)
162-
163164
// Check for environment variables trigger
164165
const envVarTrigger = checkEnvVarTrigger(newValue, newCursorPosition)
165166
setShowEnvVars(envVarTrigger.show)

apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/workflow-block/components/sub-block/components/short-input.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -106,15 +106,16 @@ export function ShortInput({
106106
const newValue = e.target.value
107107
const newCursorPosition = e.target.selectionStart ?? 0
108108

109+
// Update cursor first to minimize state staleness for dropdown selection logic
110+
setCursorPosition(newCursorPosition)
111+
109112
if (onChange) {
110113
onChange(newValue)
111114
} else if (!isPreview) {
112115
// Only update store when not in preview mode
113116
setStoreValue(newValue)
114117
}
115118

116-
setCursorPosition(newCursorPosition)
117-
118119
// Check for environment variables trigger
119120
const envVarTrigger = checkEnvVarTrigger(newValue, newCursorPosition)
120121

apps/sim/components/ui/tag-dropdown.tsx

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -869,8 +869,25 @@ export const TagDropdown: React.FC<TagDropdownProps> = ({
869869

870870
const handleTagSelect = useCallback(
871871
(tag: string, blockGroup?: BlockTagGroup) => {
872-
const textBeforeCursor = inputValue.slice(0, cursorPosition)
873-
const textAfterCursor = inputValue.slice(cursorPosition)
872+
// Use the live DOM selection/value if available to avoid off-by-one state
873+
// when users type and immediately confirm a selection.
874+
let liveCursor = cursorPosition
875+
let liveValue = inputValue
876+
877+
if (typeof window !== 'undefined' && document?.activeElement) {
878+
const activeEl = document.activeElement as HTMLInputElement | HTMLTextAreaElement | null
879+
if (activeEl && typeof activeEl.selectionStart === 'number') {
880+
liveCursor = activeEl.selectionStart ?? cursorPosition
881+
// Prefer the active element value if present. This ensures we include the most
882+
// recently typed character(s) that might not yet be reflected in React state.
883+
if (typeof (activeEl as any).value === 'string') {
884+
liveValue = (activeEl as any).value
885+
}
886+
}
887+
}
888+
889+
const textBeforeCursor = liveValue.slice(0, liveCursor)
890+
const textAfterCursor = liveValue.slice(liveCursor)
874891

875892
const lastOpenBracket = textBeforeCursor.lastIndexOf('<')
876893
if (lastOpenBracket === -1) return

0 commit comments

Comments
 (0)