@@ -79,33 +79,29 @@ export function ShortInput({
7979 wandControlRef,
8080 hideInternalWand = false ,
8181} : ShortInputProps ) {
82- // Local state for immediate UI updates during streaming
8382 const [ localContent , setLocalContent ] = useState < string > ( '' )
8483 const [ isFocused , setIsFocused ] = useState ( false )
8584 const [ copied , setCopied ] = useState ( false )
8685 const persistSubBlockValueRef = useRef < ( value : string ) => void > ( ( ) => { } )
8786
88- // Always call the hook - hooks must be called unconditionally
87+ const justPastedRef = useRef ( false )
88+
8989 const webhookManagement = useWebhookManagement ( {
9090 blockId,
9191 triggerId : undefined ,
9292 isPreview,
9393 } )
9494
95- // Wand functionality - always call the hook unconditionally
9695 const wandHook = useWand ( {
9796 wandConfig : config . wandConfig ,
9897 currentValue : localContent ,
9998 onStreamStart : ( ) => {
100- // Clear the content when streaming starts
10199 setLocalContent ( '' )
102100 } ,
103101 onStreamChunk : ( chunk ) => {
104- // Update local content with each chunk as it arrives
105102 setLocalContent ( ( current ) => current + chunk )
106103 } ,
107104 onGeneratedContent : ( content ) => {
108- // Final content update
109105 setLocalContent ( content )
110106 if ( ! isPreview && ! disabled && ! readOnly ) {
111107 persistSubBlockValueRef . current ( content )
@@ -123,23 +119,18 @@ export function ShortInput({
123119 }
124120 } , [ setSubBlockValue ] )
125121
126- // Check if wand is actually enabled
127122 const isWandEnabled = config . wandConfig ?. enabled ?? false
128123
129- const inputRef = useRef < HTMLInputElement > ( null )
130124 const overlayRef = useRef < HTMLDivElement > ( null )
131125
132- // Get ReactFlow instance for zoom control
133126 const reactFlowInstance = useReactFlow ( )
134127
135128 const accessiblePrefixes = useAccessibleReferencePrefixes ( blockId )
136129
137- // Check if this input is API key related - memoized to prevent recalculation
138130 const isApiKeyField = useMemo ( ( ) => {
139131 const normalizedId = config ?. id ?. replace ( / \s + / g, '' ) . toLowerCase ( ) || ''
140132 const normalizedTitle = config ?. title ?. replace ( / \s + / g, '' ) . toLowerCase ( ) || ''
141133
142- // Check for common API key naming patterns
143134 const apiKeyPatterns = [
144135 'apikey' ,
145136 'api_key' ,
@@ -173,11 +164,23 @@ export function ShortInput({
173164 event : 'change' | 'focus' | 'deleteAll'
174165 } ) => {
175166 if ( ! isApiKeyField || isPreview || disabled || readOnly ) return { show : false }
167+
168+ if ( justPastedRef . current ) {
169+ return { show : false }
170+ }
171+
176172 if ( event === 'focus' ) {
173+ if ( value . length > 20 && ! value . includes ( '{{' ) ) {
174+ return { show : false }
175+ }
177176 return { show : true , searchTerm : '' }
178177 }
179178 if ( event === 'change' ) {
180- // For API key fields, show env vars while typing without requiring '{{'
179+ const looksLikeRawApiKey =
180+ value . length > 30 && ! value . includes ( '{{' ) && ! value . match ( / ^ [ A - Z _ ] [ A - Z 0 - 9 _ ] * $ / i)
181+ if ( looksLikeRawApiKey ) {
182+ return { show : false }
183+ }
181184 return { show : true , searchTerm : value }
182185 }
183186 if ( event === 'deleteAll' ) {
@@ -188,17 +191,13 @@ export function ShortInput({
188191 [ isApiKeyField , isPreview , disabled , readOnly ]
189192 )
190193
191- // Use preview value when in preview mode, otherwise use store value or prop value
192194 const baseValue = isPreview ? previewValue : propValue !== undefined ? propValue : undefined
193195
194- // During streaming, use local content; otherwise use base value
195- // Only use webhook URL when useWebhookUrl flag is true
196196 const effectiveValue =
197197 useWebhookUrl && webhookManagement . webhookUrl ? webhookManagement . webhookUrl : baseValue
198198
199199 const value = wandHook ?. isStreaming ? localContent : effectiveValue
200200
201- // Sync local content with base value when not streaming
202201 useEffect ( ( ) => {
203202 if ( ! wandHook . isStreaming ) {
204203 const baseValueString = baseValue ?. toString ( ) ?? ''
@@ -208,108 +207,41 @@ export function ShortInput({
208207 }
209208 } , [ baseValue , wandHook . isStreaming , localContent ] )
210209
211- /**
212- * Scrolls the input to show the cursor position
213- * Uses canvas for efficient text width measurement instead of DOM manipulation
214- */
215- const scrollToCursor = useCallback ( ( ) => {
216- if ( ! inputRef . current ) return
217-
218- // Use requestAnimationFrame to ensure DOM has updated
219- requestAnimationFrame ( ( ) => {
220- if ( ! inputRef . current ) return
221-
222- const cursorPos = inputRef . current . selectionStart ?? 0
223- const inputWidth = inputRef . current . offsetWidth
224- const scrollWidth = inputRef . current . scrollWidth
225-
226- // Get approximate cursor position in pixels using canvas (more efficient)
227- const textBeforeCursor = inputRef . current . value . substring ( 0 , cursorPos )
228- const computedStyle = window . getComputedStyle ( inputRef . current )
229-
230- // Use canvas context for text measurement (more efficient than creating DOM elements)
231- const canvas = document . createElement ( 'canvas' )
232- const context = canvas . getContext ( '2d' )
233- if ( context ) {
234- context . font = computedStyle . font
235- const cursorPixelPos = context . measureText ( textBeforeCursor ) . width
236-
237- // Calculate optimal scroll position to center the cursor
238- const targetScroll = Math . max ( 0 , cursorPixelPos - inputWidth / 2 )
239-
240- // Only scroll if cursor is not visible
241- if (
242- cursorPixelPos < inputRef . current . scrollLeft ||
243- cursorPixelPos > inputRef . current . scrollLeft + inputWidth
244- ) {
245- inputRef . current . scrollLeft = Math . min ( targetScroll , scrollWidth - inputWidth )
246- }
247-
248- // Sync overlay scroll
249- if ( overlayRef . current ) {
250- overlayRef . current . scrollLeft = inputRef . current . scrollLeft
251- }
252- }
253- } )
254- } , [ ] )
255-
256- // Sync scroll position between input and overlay
257210 const handleScroll = useCallback ( ( e : React . UIEvent < HTMLInputElement > ) => {
258211 if ( overlayRef . current ) {
259212 overlayRef . current . scrollLeft = e . currentTarget . scrollLeft
260213 }
261214 } , [ ] )
262215
263- // Remove the auto-scroll effect that forces cursor position and replace with natural scrolling
264- useEffect ( ( ) => {
265- if ( inputRef . current && overlayRef . current ) {
266- overlayRef . current . scrollLeft = inputRef . current . scrollLeft
267- }
268- } , [ value ] )
269-
270- // Handle paste events to ensure long values are handled correctly
271216 const handlePaste = useCallback ( ( _e : React . ClipboardEvent < HTMLInputElement > ) => {
272- // Let the paste happen normally
273- // Then ensure scroll positions are synced after the content is updated
217+ justPastedRef . current = true
274218 setTimeout ( ( ) => {
275- if ( inputRef . current && overlayRef . current ) {
276- overlayRef . current . scrollLeft = inputRef . current . scrollLeft
277- }
278- } , 0 )
219+ justPastedRef . current = false
220+ } , 100 )
279221 } , [ ] )
280222
281- // Handle wheel events to control ReactFlow zoom
282223 const handleWheel = useCallback (
283224 ( e : React . WheelEvent < HTMLInputElement > ) => {
284- // Only handle zoom when Ctrl/Cmd key is pressed
285225 if ( e . ctrlKey || e . metaKey ) {
286226 e . preventDefault ( )
287227 e . stopPropagation ( )
288228
289- // Get current zoom level and viewport
290229 const currentZoom = reactFlowInstance . getZoom ( )
291230 const { x : viewportX , y : viewportY } = reactFlowInstance . getViewport ( )
292231
293- // Calculate zoom factor based on wheel delta
294- // Use a smaller factor for smoother zooming that matches ReactFlow's native behavior
295232 const delta = e . deltaY > 0 ? 1 : - 1
296- // Using 0.98 instead of 0.95 makes the zoom much slower and more gradual
297233 const zoomFactor = 0.96 ** delta
298234
299- // Calculate new zoom level with min/max constraints
300235 const newZoom = Math . min ( Math . max ( currentZoom * zoomFactor , 0.1 ) , 1 )
301236
302- // Get the position of the cursor in the page
303237 const { x : pointerX , y : pointerY } = reactFlowInstance . screenToFlowPosition ( {
304238 x : e . clientX ,
305239 y : e . clientY ,
306240 } )
307241
308- // Calculate the new viewport position to keep the cursor position fixed
309242 const newViewportX = viewportX + ( pointerX * currentZoom - pointerX * newZoom )
310243 const newViewportY = viewportY + ( pointerY * currentZoom - pointerY * newZoom )
311244
312- // Set the new viewport with the calculated position and zoom
313245 reactFlowInstance . setViewport (
314246 {
315247 x : newViewportX ,
@@ -322,8 +254,6 @@ export function ShortInput({
322254 return false
323255 }
324256
325- // For regular scrolling (without Ctrl/Cmd), let the default behavior happen
326- // Don't interfere with normal scrolling
327257 return true
328258 } ,
329259 [ reactFlowInstance ]
@@ -341,33 +271,6 @@ export function ShortInput({
341271 }
342272 } , [ useWebhookUrl , webhookManagement ?. webhookUrl , value ] )
343273
344- // Value display logic - memoize to avoid unnecessary string operations
345- const displayValue = useMemo (
346- ( ) =>
347- password && ! isFocused
348- ? '•' . repeat ( value ?. toString ( ) . length ?? 0 )
349- : ( value ?. toString ( ) ?? '' ) ,
350- [ password , isFocused , value ]
351- )
352-
353- // Memoize formatted text to avoid recalculation on every render
354- const formattedText = useMemo ( ( ) => {
355- const textValue = value ?. toString ( ) ?? ''
356- if ( password && ! isFocused ) {
357- return '•' . repeat ( textValue . length )
358- }
359- return formatDisplayText ( textValue , {
360- accessiblePrefixes,
361- highlightAll : ! accessiblePrefixes ,
362- } )
363- } , [ value , password , isFocused , accessiblePrefixes ] )
364-
365- // Memoize focus handler to prevent unnecessary re-renders
366- const handleFocus = useCallback ( ( ) => {
367- setIsFocused ( true )
368- } , [ ] )
369-
370- // Memoize blur handler to prevent unnecessary re-renders
371274 const handleBlur = useCallback ( ( ) => {
372275 setIsFocused ( false )
373276 } , [ ] )
@@ -422,7 +325,6 @@ export function ShortInput({
422325 onDragOver,
423326 onFocus,
424327 } ) => {
425- // Use controller's value for input, but apply local transformations
426328 const actualValue = wandHook . isStreaming
427329 ? localContent
428330 : useWebhookUrl && webhookManagement . webhookUrl
0 commit comments