fix(tag-dropdown): last char dropped bug (#945)

This commit is contained in:
Vikhyath Mondreti
2025-08-12 11:48:34 -05:00
committed by GitHub
parent a2dea384a4
commit 1a7de84c7a
6 changed files with 55 additions and 30 deletions

View File

@@ -158,19 +158,23 @@ export function IterationBadges({ nodeId, data, iterationType }: IterationBadges
const handleEditorChange = useCallback(
(value: string) => {
if (isPreview) return
collaborativeUpdateIterationCollection(nodeId, iterationType, value)
const textarea = editorContainerRef.current?.querySelector('textarea')
// Capture cursor first to minimize staleness in dropdown logic
const textarea = editorContainerRef.current?.querySelector(
'textarea'
) as HTMLTextAreaElement | null
const cursorPos = textarea?.selectionStart ?? cursorPosition
if (textarea) {
textareaRef.current = textarea
const cursorPos = textarea.selectionStart || 0
setCursorPosition(cursorPos)
const triggerCheck = checkTagTrigger(value, cursorPos)
setShowTagDropdown(triggerCheck.show)
}
setCursorPosition(cursorPos)
collaborativeUpdateIterationCollection(nodeId, iterationType, value)
const triggerCheck = checkTagTrigger(value, cursorPos)
setShowTagDropdown(triggerCheck.show)
},
[nodeId, iterationType, collaborativeUpdateIterationCollection, isPreview]
[nodeId, iterationType, collaborativeUpdateIterationCollection, isPreview, cursorPosition]
)
// Handle tag selection

View File

@@ -446,24 +446,25 @@ export function Code({
value={code}
onValueChange={(newCode) => {
if (!isCollapsed && !isAiStreaming && !isPreview && !disabled) {
// Capture cursor first to minimize staleness in dropdown logic
const textarea = editorRef.current?.querySelector(
'textarea'
) as HTMLTextAreaElement | null
const pos = textarea?.selectionStart ?? cursorPosition
setCursorPosition(pos)
setCode(newCode)
setStoreValue(newCode)
const textarea = editorRef.current?.querySelector('textarea')
if (textarea) {
const pos = textarea.selectionStart
setCursorPosition(pos)
const tagTrigger = checkTagTrigger(newCode, pos)
setShowTags(tagTrigger.show)
if (!tagTrigger.show) {
setActiveSourceBlockId(null)
}
const envVarTrigger = checkEnvVarTrigger(newCode, pos)
setShowEnvVars(envVarTrigger.show)
setSearchTerm(envVarTrigger.show ? envVarTrigger.searchTerm : '')
const tagTrigger = checkTagTrigger(newCode, pos)
setShowTags(tagTrigger.show)
if (!tagTrigger.show) {
setActiveSourceBlockId(null)
}
const envVarTrigger = checkEnvVarTrigger(newCode, pos)
setShowEnvVars(envVarTrigger.show)
setSearchTerm(envVarTrigger.show ? envVarTrigger.searchTerm : '')
}
}}
onKeyDown={(e) => {

View File

@@ -150,13 +150,14 @@ export function ComboBox({
const newValue = e.target.value
const newCursorPosition = e.target.selectionStart ?? 0
// Update cursor first to reduce staleness for dropdown logic
setCursorPosition(newCursorPosition)
// Update store value immediately (allow free text)
if (!isPreview) {
setStoreValue(newValue)
}
setCursorPosition(newCursorPosition)
// Check for environment variables trigger
const envVarTrigger = checkEnvVarTrigger(newValue, newCursorPosition)
setShowEnvVars(envVarTrigger.show)

View File

@@ -148,6 +148,9 @@ export function LongInput({
const newValue = e.target.value
const newCursorPosition = e.target.selectionStart ?? 0
// Update cursor first to minimize state staleness for dropdown selection logic
setCursorPosition(newCursorPosition)
// Update local content immediately
setLocalContent(newValue)
@@ -158,8 +161,6 @@ export function LongInput({
setStoreValue(newValue)
}
setCursorPosition(newCursorPosition)
// Check for environment variables trigger
const envVarTrigger = checkEnvVarTrigger(newValue, newCursorPosition)
setShowEnvVars(envVarTrigger.show)

View File

@@ -106,6 +106,9 @@ export function ShortInput({
const newValue = e.target.value
const newCursorPosition = e.target.selectionStart ?? 0
// Update cursor first to minimize state staleness for dropdown selection logic
setCursorPosition(newCursorPosition)
if (onChange) {
onChange(newValue)
} else if (!isPreview) {
@@ -113,8 +116,6 @@ export function ShortInput({
setStoreValue(newValue)
}
setCursorPosition(newCursorPosition)
// Check for environment variables trigger
const envVarTrigger = checkEnvVarTrigger(newValue, newCursorPosition)

View File

@@ -869,8 +869,25 @@ export const TagDropdown: React.FC<TagDropdownProps> = ({
const handleTagSelect = useCallback(
(tag: string, blockGroup?: BlockTagGroup) => {
const textBeforeCursor = inputValue.slice(0, cursorPosition)
const textAfterCursor = inputValue.slice(cursorPosition)
// Use the live DOM selection/value if available to avoid off-by-one state
// when users type and immediately confirm a selection.
let liveCursor = cursorPosition
let liveValue = inputValue
if (typeof window !== 'undefined' && document?.activeElement) {
const activeEl = document.activeElement as HTMLInputElement | HTMLTextAreaElement | null
if (activeEl && typeof activeEl.selectionStart === 'number') {
liveCursor = activeEl.selectionStart ?? cursorPosition
// Prefer the active element value if present. This ensures we include the most
// recently typed character(s) that might not yet be reflected in React state.
if (typeof (activeEl as any).value === 'string') {
liveValue = (activeEl as any).value
}
}
}
const textBeforeCursor = liveValue.slice(0, liveCursor)
const textAfterCursor = liveValue.slice(liveCursor)
const lastOpenBracket = textBeforeCursor.lastIndexOf('<')
if (lastOpenBracket === -1) return