fix(ux): envvar dropdown on focus for api keys in subblock

This commit is contained in:
Emir Karabeg
2025-03-11 15:51:20 -07:00
parent 47f16cd8c8
commit f4fdda36df
2 changed files with 90 additions and 15 deletions
@@ -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}
+30 -9
View File
@@ -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: '' }
}