mirror of
https://github.com/simstudioai/sim.git
synced 2026-09-24 15:45:35 +08:00
fix(ux): envvar dropdown on focus for api keys in subblock
This commit is contained in:
+60
-6
@@ -1,4 +1,4 @@
|
||||
import { useEffect, useRef, useState } from 'react'
|
||||
import { useEffect, useMemo, useRef, useState } from 'react'
|
||||
import { useReactFlow } from 'reactflow'
|
||||
import { EnvVarDropdown, checkEnvVarTrigger } from '@/components/ui/env-var-dropdown'
|
||||
import { formatDisplayText } from '@/components/ui/formatted-text'
|
||||
@@ -45,6 +45,23 @@ export function ShortInput({
|
||||
// Use either controlled or uncontrolled value
|
||||
const value = propValue !== undefined ? propValue : storeValue
|
||||
|
||||
// Check if this input is API key related
|
||||
const isApiKeyField = useMemo(() => {
|
||||
const normalizedId = config?.id?.replace(/\s+/g, '').toLowerCase() || ''
|
||||
const normalizedTitle = config?.title?.replace(/\s+/g, '').toLowerCase() || ''
|
||||
|
||||
// Check for common API key naming patterns
|
||||
const apiKeyPatterns = ['apikey', 'api_key', 'api-key', 'secretkey', 'secret_key', 'secret-key']
|
||||
|
||||
return apiKeyPatterns.some(
|
||||
(pattern) =>
|
||||
normalizedId === pattern ||
|
||||
normalizedTitle === pattern ||
|
||||
normalizedId.includes(pattern) ||
|
||||
normalizedTitle.includes(pattern)
|
||||
)
|
||||
}, [config?.id, config?.title])
|
||||
|
||||
// Handle input changes
|
||||
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
const newValue = e.target.value
|
||||
@@ -60,8 +77,20 @@ export function ShortInput({
|
||||
|
||||
// Check for environment variables trigger
|
||||
const envVarTrigger = checkEnvVarTrigger(newValue, newCursorPosition)
|
||||
setShowEnvVars(envVarTrigger.show)
|
||||
setSearchTerm(envVarTrigger.show ? envVarTrigger.searchTerm : '')
|
||||
|
||||
// For API key fields, always show dropdown when typing (without requiring {{ trigger)
|
||||
if (isApiKeyField && isFocused) {
|
||||
// Only show dropdown if there's text to filter by or the field is empty
|
||||
const shouldShowDropdown = newValue.trim() !== '' || newValue === ''
|
||||
setShowEnvVars(shouldShowDropdown)
|
||||
// Use the entire input value as search term for API key fields,
|
||||
// but if {{ is detected, use the standard search term extraction
|
||||
setSearchTerm(envVarTrigger.show ? envVarTrigger.searchTerm : newValue)
|
||||
} else {
|
||||
// Normal behavior for non-API key fields
|
||||
setShowEnvVars(envVarTrigger.show)
|
||||
setSearchTerm(envVarTrigger.show ? envVarTrigger.searchTerm : '')
|
||||
}
|
||||
|
||||
// Check for tag trigger
|
||||
const tagTrigger = checkTagTrigger(newValue, newCursorPosition)
|
||||
@@ -190,6 +219,17 @@ export function ShortInput({
|
||||
if (e.key === 'Escape') {
|
||||
setShowEnvVars(false)
|
||||
setShowTags(false)
|
||||
return
|
||||
}
|
||||
|
||||
// For API key fields, show env vars when clearing with keyboard shortcuts
|
||||
if (
|
||||
isApiKeyField &&
|
||||
(e.key === 'Delete' || e.key === 'Backspace') &&
|
||||
inputRef.current?.selectionStart === 0 &&
|
||||
inputRef.current?.selectionEnd === value?.toString().length
|
||||
) {
|
||||
setTimeout(() => setShowEnvVars(true), 0)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -222,11 +262,25 @@ export function ShortInput({
|
||||
onChange={handleChange}
|
||||
onFocus={() => {
|
||||
setIsFocused(true)
|
||||
|
||||
// If this is an API key field, automatically show env vars dropdown
|
||||
if (isApiKeyField) {
|
||||
setShowEnvVars(true)
|
||||
setSearchTerm('')
|
||||
|
||||
// Set cursor position to the end of the input
|
||||
const inputLength = value?.toString().length ?? 0
|
||||
setCursorPosition(inputLength)
|
||||
} else {
|
||||
setShowEnvVars(false)
|
||||
setShowTags(false)
|
||||
setSearchTerm('')
|
||||
}
|
||||
}}
|
||||
onBlur={() => {
|
||||
setIsFocused(false)
|
||||
setShowEnvVars(false)
|
||||
setShowTags(false)
|
||||
setSearchTerm('')
|
||||
}}
|
||||
onBlur={() => setIsFocused(false)}
|
||||
onDrop={handleDrop}
|
||||
onDragOver={handleDragOver}
|
||||
onScroll={handleScroll}
|
||||
|
||||
@@ -44,18 +44,32 @@ export const EnvVarDropdown: React.FC<EnvVarDropdownProps> = ({
|
||||
// Find the start of the env var syntax (last '{{' before cursor)
|
||||
const lastOpenBraces = textBeforeCursor.lastIndexOf('{{')
|
||||
|
||||
// Get the text before the env var syntax
|
||||
const startText =
|
||||
lastOpenBraces !== -1 ? textBeforeCursor.slice(0, lastOpenBraces) : textBeforeCursor
|
||||
// Check if we're in a standard env var context (with braces) or direct typing mode
|
||||
const isStandardEnvVarContext = lastOpenBraces !== -1
|
||||
|
||||
// Find the end of any existing env var syntax after cursor
|
||||
const closeIndex = textAfterCursor.indexOf('}}')
|
||||
const endText = closeIndex !== -1 ? textAfterCursor.slice(closeIndex + 2) : textAfterCursor
|
||||
if (isStandardEnvVarContext) {
|
||||
// Standard behavior with {{ }} syntax
|
||||
const startText = textBeforeCursor.slice(0, lastOpenBraces)
|
||||
|
||||
// Construct the new value with proper env var syntax
|
||||
const newValue = startText + '{{' + envVar + '}}' + endText
|
||||
// Find the end of any existing env var syntax after cursor
|
||||
const closeIndex = textAfterCursor.indexOf('}}')
|
||||
const endText = closeIndex !== -1 ? textAfterCursor.slice(closeIndex + 2) : textAfterCursor
|
||||
|
||||
// Construct the new value with proper env var syntax
|
||||
const newValue = startText + '{{' + envVar + '}}' + endText
|
||||
onSelect(newValue)
|
||||
} else {
|
||||
// For direct typing mode (API key fields), check if we need to replace existing text
|
||||
// This handles the case where user has already typed part of a variable name
|
||||
if (inputValue.trim() !== '') {
|
||||
// Replace the entire input with the selected env var
|
||||
onSelect('{{' + envVar + '}}')
|
||||
} else {
|
||||
// Empty input, just insert the env var
|
||||
onSelect('{{' + envVar + '}}')
|
||||
}
|
||||
}
|
||||
|
||||
onSelect(newValue)
|
||||
onClose?.()
|
||||
}
|
||||
|
||||
@@ -141,10 +155,17 @@ export const checkEnvVarTrigger = (
|
||||
): { show: boolean; searchTerm: string } => {
|
||||
if (cursorPosition >= 2) {
|
||||
const textBeforeCursor = text.slice(0, cursorPosition)
|
||||
// Look for {{ pattern followed by optional text
|
||||
const match = textBeforeCursor.match(/\{\{(\w*)$/)
|
||||
if (match) {
|
||||
return { show: true, searchTerm: match[1] }
|
||||
}
|
||||
|
||||
// Also check for exact {{ without any text after it
|
||||
// This ensures all env vars show when user just types {{
|
||||
if (textBeforeCursor.endsWith('{{')) {
|
||||
return { show: true, searchTerm: '' }
|
||||
}
|
||||
}
|
||||
return { show: false, searchTerm: '' }
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user