fix(envvar): fix envvar dropdown positioning, remove dead code (#2196)

This commit is contained in:
Waleed
2025-12-04 14:35:25 -08:00
committed by GitHub
parent 3f84ed9b72
commit dc5a2b1ad1

View File

@@ -79,33 +79,29 @@ export function ShortInput({
wandControlRef,
hideInternalWand = false,
}: ShortInputProps) {
// Local state for immediate UI updates during streaming
const [localContent, setLocalContent] = useState<string>('')
const [isFocused, setIsFocused] = useState(false)
const [copied, setCopied] = useState(false)
const persistSubBlockValueRef = useRef<(value: string) => void>(() => {})
// Always call the hook - hooks must be called unconditionally
const justPastedRef = useRef(false)
const webhookManagement = useWebhookManagement({
blockId,
triggerId: undefined,
isPreview,
})
// Wand functionality - always call the hook unconditionally
const wandHook = useWand({
wandConfig: config.wandConfig,
currentValue: localContent,
onStreamStart: () => {
// Clear the content when streaming starts
setLocalContent('')
},
onStreamChunk: (chunk) => {
// Update local content with each chunk as it arrives
setLocalContent((current) => current + chunk)
},
onGeneratedContent: (content) => {
// Final content update
setLocalContent(content)
if (!isPreview && !disabled && !readOnly) {
persistSubBlockValueRef.current(content)
@@ -123,23 +119,18 @@ export function ShortInput({
}
}, [setSubBlockValue])
// Check if wand is actually enabled
const isWandEnabled = config.wandConfig?.enabled ?? false
const inputRef = useRef<HTMLInputElement>(null)
const overlayRef = useRef<HTMLDivElement>(null)
// Get ReactFlow instance for zoom control
const reactFlowInstance = useReactFlow()
const accessiblePrefixes = useAccessibleReferencePrefixes(blockId)
// Check if this input is API key related - memoized to prevent recalculation
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',
@@ -173,11 +164,23 @@ export function ShortInput({
event: 'change' | 'focus' | 'deleteAll'
}) => {
if (!isApiKeyField || isPreview || disabled || readOnly) return { show: false }
if (justPastedRef.current) {
return { show: false }
}
if (event === 'focus') {
if (value.length > 20 && !value.includes('{{')) {
return { show: false }
}
return { show: true, searchTerm: '' }
}
if (event === 'change') {
// For API key fields, show env vars while typing without requiring '{{'
const looksLikeRawApiKey =
value.length > 30 && !value.includes('{{') && !value.match(/^[A-Z_][A-Z0-9_]*$/i)
if (looksLikeRawApiKey) {
return { show: false }
}
return { show: true, searchTerm: value }
}
if (event === 'deleteAll') {
@@ -188,17 +191,13 @@ export function ShortInput({
[isApiKeyField, isPreview, disabled, readOnly]
)
// Use preview value when in preview mode, otherwise use store value or prop value
const baseValue = isPreview ? previewValue : propValue !== undefined ? propValue : undefined
// During streaming, use local content; otherwise use base value
// Only use webhook URL when useWebhookUrl flag is true
const effectiveValue =
useWebhookUrl && webhookManagement.webhookUrl ? webhookManagement.webhookUrl : baseValue
const value = wandHook?.isStreaming ? localContent : effectiveValue
// Sync local content with base value when not streaming
useEffect(() => {
if (!wandHook.isStreaming) {
const baseValueString = baseValue?.toString() ?? ''
@@ -208,108 +207,41 @@ export function ShortInput({
}
}, [baseValue, wandHook.isStreaming, localContent])
/**
* Scrolls the input to show the cursor position
* Uses canvas for efficient text width measurement instead of DOM manipulation
*/
const scrollToCursor = useCallback(() => {
if (!inputRef.current) return
// Use requestAnimationFrame to ensure DOM has updated
requestAnimationFrame(() => {
if (!inputRef.current) return
const cursorPos = inputRef.current.selectionStart ?? 0
const inputWidth = inputRef.current.offsetWidth
const scrollWidth = inputRef.current.scrollWidth
// Get approximate cursor position in pixels using canvas (more efficient)
const textBeforeCursor = inputRef.current.value.substring(0, cursorPos)
const computedStyle = window.getComputedStyle(inputRef.current)
// Use canvas context for text measurement (more efficient than creating DOM elements)
const canvas = document.createElement('canvas')
const context = canvas.getContext('2d')
if (context) {
context.font = computedStyle.font
const cursorPixelPos = context.measureText(textBeforeCursor).width
// Calculate optimal scroll position to center the cursor
const targetScroll = Math.max(0, cursorPixelPos - inputWidth / 2)
// Only scroll if cursor is not visible
if (
cursorPixelPos < inputRef.current.scrollLeft ||
cursorPixelPos > inputRef.current.scrollLeft + inputWidth
) {
inputRef.current.scrollLeft = Math.min(targetScroll, scrollWidth - inputWidth)
}
// Sync overlay scroll
if (overlayRef.current) {
overlayRef.current.scrollLeft = inputRef.current.scrollLeft
}
}
})
}, [])
// Sync scroll position between input and overlay
const handleScroll = useCallback((e: React.UIEvent<HTMLInputElement>) => {
if (overlayRef.current) {
overlayRef.current.scrollLeft = e.currentTarget.scrollLeft
}
}, [])
// Remove the auto-scroll effect that forces cursor position and replace with natural scrolling
useEffect(() => {
if (inputRef.current && overlayRef.current) {
overlayRef.current.scrollLeft = inputRef.current.scrollLeft
}
}, [value])
// Handle paste events to ensure long values are handled correctly
const handlePaste = useCallback((_e: React.ClipboardEvent<HTMLInputElement>) => {
// Let the paste happen normally
// Then ensure scroll positions are synced after the content is updated
justPastedRef.current = true
setTimeout(() => {
if (inputRef.current && overlayRef.current) {
overlayRef.current.scrollLeft = inputRef.current.scrollLeft
}
}, 0)
justPastedRef.current = false
}, 100)
}, [])
// Handle wheel events to control ReactFlow zoom
const handleWheel = useCallback(
(e: React.WheelEvent<HTMLInputElement>) => {
// Only handle zoom when Ctrl/Cmd key is pressed
if (e.ctrlKey || e.metaKey) {
e.preventDefault()
e.stopPropagation()
// Get current zoom level and viewport
const currentZoom = reactFlowInstance.getZoom()
const { x: viewportX, y: viewportY } = reactFlowInstance.getViewport()
// Calculate zoom factor based on wheel delta
// Use a smaller factor for smoother zooming that matches ReactFlow's native behavior
const delta = e.deltaY > 0 ? 1 : -1
// Using 0.98 instead of 0.95 makes the zoom much slower and more gradual
const zoomFactor = 0.96 ** delta
// Calculate new zoom level with min/max constraints
const newZoom = Math.min(Math.max(currentZoom * zoomFactor, 0.1), 1)
// Get the position of the cursor in the page
const { x: pointerX, y: pointerY } = reactFlowInstance.screenToFlowPosition({
x: e.clientX,
y: e.clientY,
})
// Calculate the new viewport position to keep the cursor position fixed
const newViewportX = viewportX + (pointerX * currentZoom - pointerX * newZoom)
const newViewportY = viewportY + (pointerY * currentZoom - pointerY * newZoom)
// Set the new viewport with the calculated position and zoom
reactFlowInstance.setViewport(
{
x: newViewportX,
@@ -322,8 +254,6 @@ export function ShortInput({
return false
}
// For regular scrolling (without Ctrl/Cmd), let the default behavior happen
// Don't interfere with normal scrolling
return true
},
[reactFlowInstance]
@@ -341,33 +271,6 @@ export function ShortInput({
}
}, [useWebhookUrl, webhookManagement?.webhookUrl, value])
// Value display logic - memoize to avoid unnecessary string operations
const displayValue = useMemo(
() =>
password && !isFocused
? '•'.repeat(value?.toString().length ?? 0)
: (value?.toString() ?? ''),
[password, isFocused, value]
)
// Memoize formatted text to avoid recalculation on every render
const formattedText = useMemo(() => {
const textValue = value?.toString() ?? ''
if (password && !isFocused) {
return '•'.repeat(textValue.length)
}
return formatDisplayText(textValue, {
accessiblePrefixes,
highlightAll: !accessiblePrefixes,
})
}, [value, password, isFocused, accessiblePrefixes])
// Memoize focus handler to prevent unnecessary re-renders
const handleFocus = useCallback(() => {
setIsFocused(true)
}, [])
// Memoize blur handler to prevent unnecessary re-renders
const handleBlur = useCallback(() => {
setIsFocused(false)
}, [])
@@ -422,7 +325,6 @@ export function ShortInput({
onDragOver,
onFocus,
}) => {
// Use controller's value for input, but apply local transformations
const actualValue = wandHook.isStreaming
? localContent
: useWebhookUrl && webhookManagement.webhookUrl