mirror of
https://github.com/simstudioai/sim.git
synced 2026-04-28 03:00:29 -04:00
fix(tag-dropdown): last char dropped bug (#945)
This commit is contained in:
committed by
GitHub
parent
a2dea384a4
commit
1a7de84c7a
@@ -158,19 +158,23 @@ export function IterationBadges({ nodeId, data, iterationType }: IterationBadges
|
|||||||
const handleEditorChange = useCallback(
|
const handleEditorChange = useCallback(
|
||||||
(value: string) => {
|
(value: string) => {
|
||||||
if (isPreview) return
|
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) {
|
if (textarea) {
|
||||||
textareaRef.current = 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
|
// Handle tag selection
|
||||||
|
|||||||
@@ -446,24 +446,25 @@ export function Code({
|
|||||||
value={code}
|
value={code}
|
||||||
onValueChange={(newCode) => {
|
onValueChange={(newCode) => {
|
||||||
if (!isCollapsed && !isAiStreaming && !isPreview && !disabled) {
|
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)
|
setCode(newCode)
|
||||||
setStoreValue(newCode)
|
setStoreValue(newCode)
|
||||||
|
|
||||||
const textarea = editorRef.current?.querySelector('textarea')
|
const tagTrigger = checkTagTrigger(newCode, pos)
|
||||||
if (textarea) {
|
setShowTags(tagTrigger.show)
|
||||||
const pos = textarea.selectionStart
|
if (!tagTrigger.show) {
|
||||||
setCursorPosition(pos)
|
setActiveSourceBlockId(null)
|
||||||
|
|
||||||
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 envVarTrigger = checkEnvVarTrigger(newCode, pos)
|
||||||
|
setShowEnvVars(envVarTrigger.show)
|
||||||
|
setSearchTerm(envVarTrigger.show ? envVarTrigger.searchTerm : '')
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
onKeyDown={(e) => {
|
onKeyDown={(e) => {
|
||||||
|
|||||||
@@ -150,13 +150,14 @@ export function ComboBox({
|
|||||||
const newValue = e.target.value
|
const newValue = e.target.value
|
||||||
const newCursorPosition = e.target.selectionStart ?? 0
|
const newCursorPosition = e.target.selectionStart ?? 0
|
||||||
|
|
||||||
|
// Update cursor first to reduce staleness for dropdown logic
|
||||||
|
setCursorPosition(newCursorPosition)
|
||||||
|
|
||||||
// Update store value immediately (allow free text)
|
// Update store value immediately (allow free text)
|
||||||
if (!isPreview) {
|
if (!isPreview) {
|
||||||
setStoreValue(newValue)
|
setStoreValue(newValue)
|
||||||
}
|
}
|
||||||
|
|
||||||
setCursorPosition(newCursorPosition)
|
|
||||||
|
|
||||||
// Check for environment variables trigger
|
// Check for environment variables trigger
|
||||||
const envVarTrigger = checkEnvVarTrigger(newValue, newCursorPosition)
|
const envVarTrigger = checkEnvVarTrigger(newValue, newCursorPosition)
|
||||||
setShowEnvVars(envVarTrigger.show)
|
setShowEnvVars(envVarTrigger.show)
|
||||||
|
|||||||
@@ -148,6 +148,9 @@ export function LongInput({
|
|||||||
const newValue = e.target.value
|
const newValue = e.target.value
|
||||||
const newCursorPosition = e.target.selectionStart ?? 0
|
const newCursorPosition = e.target.selectionStart ?? 0
|
||||||
|
|
||||||
|
// Update cursor first to minimize state staleness for dropdown selection logic
|
||||||
|
setCursorPosition(newCursorPosition)
|
||||||
|
|
||||||
// Update local content immediately
|
// Update local content immediately
|
||||||
setLocalContent(newValue)
|
setLocalContent(newValue)
|
||||||
|
|
||||||
@@ -158,8 +161,6 @@ export function LongInput({
|
|||||||
setStoreValue(newValue)
|
setStoreValue(newValue)
|
||||||
}
|
}
|
||||||
|
|
||||||
setCursorPosition(newCursorPosition)
|
|
||||||
|
|
||||||
// Check for environment variables trigger
|
// Check for environment variables trigger
|
||||||
const envVarTrigger = checkEnvVarTrigger(newValue, newCursorPosition)
|
const envVarTrigger = checkEnvVarTrigger(newValue, newCursorPosition)
|
||||||
setShowEnvVars(envVarTrigger.show)
|
setShowEnvVars(envVarTrigger.show)
|
||||||
|
|||||||
@@ -106,6 +106,9 @@ export function ShortInput({
|
|||||||
const newValue = e.target.value
|
const newValue = e.target.value
|
||||||
const newCursorPosition = e.target.selectionStart ?? 0
|
const newCursorPosition = e.target.selectionStart ?? 0
|
||||||
|
|
||||||
|
// Update cursor first to minimize state staleness for dropdown selection logic
|
||||||
|
setCursorPosition(newCursorPosition)
|
||||||
|
|
||||||
if (onChange) {
|
if (onChange) {
|
||||||
onChange(newValue)
|
onChange(newValue)
|
||||||
} else if (!isPreview) {
|
} else if (!isPreview) {
|
||||||
@@ -113,8 +116,6 @@ export function ShortInput({
|
|||||||
setStoreValue(newValue)
|
setStoreValue(newValue)
|
||||||
}
|
}
|
||||||
|
|
||||||
setCursorPosition(newCursorPosition)
|
|
||||||
|
|
||||||
// Check for environment variables trigger
|
// Check for environment variables trigger
|
||||||
const envVarTrigger = checkEnvVarTrigger(newValue, newCursorPosition)
|
const envVarTrigger = checkEnvVarTrigger(newValue, newCursorPosition)
|
||||||
|
|
||||||
|
|||||||
@@ -869,8 +869,25 @@ export const TagDropdown: React.FC<TagDropdownProps> = ({
|
|||||||
|
|
||||||
const handleTagSelect = useCallback(
|
const handleTagSelect = useCallback(
|
||||||
(tag: string, blockGroup?: BlockTagGroup) => {
|
(tag: string, blockGroup?: BlockTagGroup) => {
|
||||||
const textBeforeCursor = inputValue.slice(0, cursorPosition)
|
// Use the live DOM selection/value if available to avoid off-by-one state
|
||||||
const textAfterCursor = inputValue.slice(cursorPosition)
|
// 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('<')
|
const lastOpenBracket = textBeforeCursor.lastIndexOf('<')
|
||||||
if (lastOpenBracket === -1) return
|
if (lastOpenBracket === -1) return
|
||||||
|
|||||||
Reference in New Issue
Block a user