@@ -88,7 +88,6 @@ export function LongInput({
8888 const [ cursorPosition , setCursorPosition ] = useState ( 0 )
8989 const textareaRef = useRef < HTMLTextAreaElement > ( null )
9090 const overlayRef = useRef < HTMLDivElement > ( null )
91- const overlayInnerRef = useRef < HTMLDivElement > ( null )
9291 const [ activeSourceBlockId , setActiveSourceBlockId ] = useState < string | null > ( null )
9392 const containerRef = useRef < HTMLDivElement > ( null )
9493
@@ -141,35 +140,6 @@ export function LongInput({
141140 }
142141 } , [ rows ] )
143142
144- // Set overlay width to match textarea clientWidth
145- useLayoutEffect ( ( ) => {
146- if ( ! textareaRef . current || ! overlayRef . current ) return
147- const textarea = textareaRef . current
148- const overlay = overlayRef . current
149-
150- const applyWidth = ( ) => {
151- // Match overlay content width to the inner content area of the textarea
152- overlay . style . width = `${ textarea . clientWidth } px`
153- }
154-
155- applyWidth ( )
156-
157- const resizeObserver = new ResizeObserver ( ( ) => {
158- applyWidth ( )
159- } )
160- resizeObserver . observe ( textarea )
161-
162- return ( ) => {
163- resizeObserver . disconnect ( )
164- }
165- } , [ ] )
166-
167- // Initialize overlay transform to current scroll
168- useLayoutEffect ( ( ) => {
169- // Initialize overlay transform to current scroll
170- syncScrollPositions ( )
171- } , [ ] )
172-
173143 // Handle input changes
174144 const handleChange = ( e : React . ChangeEvent < HTMLTextAreaElement > ) => {
175145 // Don't allow changes if disabled or streaming
@@ -202,21 +172,19 @@ export function LongInput({
202172
203173 // Sync scroll position between textarea and overlay
204174 const handleScroll = ( e : React . UIEvent < HTMLTextAreaElement > ) => {
205- if ( ! overlayInnerRef . current ) return
206- const { scrollTop, scrollLeft } = e . currentTarget
207- overlayInnerRef . current . style . transform = `translate(${ - scrollLeft } px, ${ - scrollTop } px)`
208- }
209-
210- // Force synchronize scroll positions
211- const syncScrollPositions = ( ) => {
212- if ( ! textareaRef . current || ! overlayInnerRef . current ) return
213- const { scrollTop, scrollLeft } = textareaRef . current
214- overlayInnerRef . current . style . transform = `translate(${ - scrollLeft } px, ${ - scrollTop } px)`
175+ if ( overlayRef . current ) {
176+ overlayRef . current . scrollTop = e . currentTarget . scrollTop
177+ overlayRef . current . scrollLeft = e . currentTarget . scrollLeft
178+ }
215179 }
216180
217181 // Ensure overlay updates when content changes
218182 useEffect ( ( ) => {
219- syncScrollPositions ( )
183+ if ( textareaRef . current && overlayRef . current ) {
184+ // Ensure scrolling is synchronized
185+ overlayRef . current . scrollTop = textareaRef . current . scrollTop
186+ overlayRef . current . scrollLeft = textareaRef . current . scrollLeft
187+ }
220188 } , [ value ] )
221189
222190 // Handle resize functionality
@@ -240,8 +208,6 @@ export function LongInput({
240208 if ( containerRef . current ) {
241209 containerRef . current . style . height = `${ newHeight } px`
242210 }
243- // Keep overlay aligned with textarea scroll during live resize
244- syncScrollPositions ( )
245211 }
246212 }
247213
@@ -254,8 +220,6 @@ export function LongInput({
254220 isResizing . current = false
255221 document . removeEventListener ( 'mousemove' , handleMouseMove )
256222 document . removeEventListener ( 'mouseup' , handleMouseUp )
257- // After resizing completes, re-sync to ensure caret at end remains visually aligned
258- syncScrollPositions ( )
259223 }
260224
261225 document . addEventListener ( 'mousemove' , handleMouseMove )
@@ -371,7 +335,9 @@ export function LongInput({
371335 }
372336
373337 // For regular scrolling (without Ctrl/Cmd), let the default behavior happen
374- // No overlay scroll; overlay position is synced via transform on scroll handler
338+ if ( overlayRef . current ) {
339+ overlayRef . current . scrollTop = e . currentTarget . scrollTop
340+ }
375341 }
376342
377343 return (
@@ -399,7 +365,6 @@ export function LongInput({
399365 ref = { textareaRef }
400366 className = { cn (
401367 'allow-scroll min-h-full w-full resize-none text-transparent caret-foreground placeholder:text-muted-foreground/50' ,
402- '!text-[14px]' , // Force override any responsive text sizes from Textarea component
403368 isConnecting &&
404369 config ?. connectionDroppable !== false &&
405370 'ring-2 ring-blue-500 ring-offset-2 focus-visible:ring-blue-500' ,
@@ -426,69 +391,25 @@ export function LongInput({
426391 } }
427392 disabled = { isPreview || disabled }
428393 style = { {
429- // Explicit font properties for perfect alignment
430- fontFamily :
431- '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif' ,
432- fontSize : '14px' ,
433- fontWeight : '400' ,
434- // Match the fixed pixel line-height used on the textarea
435- lineHeight : '21px' ,
436- letterSpacing : 'normal' ,
394+ fontFamily : 'inherit' ,
395+ lineHeight : 'inherit' ,
437396 height : `${ height } px` ,
438- // Text wrapping properties
439397 wordBreak : 'break-word' ,
440398 whiteSpace : 'pre-wrap' ,
441- overflowWrap : 'break-word' ,
442- // Box sizing to ensure padding is calculated correctly
443- boxSizing : 'border-box' ,
444- // Remove text rendering optimizations that can affect layout
445- textRendering : 'auto' ,
446399 } }
447400 />
448401 < div
449402 ref = { overlayRef }
450- className = 'pointer-events-none absolute bg-transparent'
403+ className = 'pointer-events-none absolute inset-0 whitespace-pre-wrap break-words bg-transparent px-3 py-2 text-sm '
451404 style = { {
452- // Position exactly over the textarea content area
453- top : '0' ,
454- left : '0' ,
455- // width is set dynamically to match textarea clientWidth to ensure identical wrapping
456- // right is disabled to avoid conflicts with explicit width
457- right : 'auto' ,
458- // Padding: py-2 px-3 = top/bottom: 8px, left/right: 12px
459- paddingTop : '8px' ,
460- paddingBottom : '8px' ,
461- paddingLeft : '12px' ,
462- paddingRight : '12px' ,
463- // No border; border would shrink content width under border-box and break wrapping parity
464- // Exact same font properties as textarea
465- fontFamily :
466- '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif' ,
467- fontSize : '14px' ,
468- fontWeight : '400' ,
469- lineHeight : '21px' , // Use fixed pixel line-height to prevent subpixel rounding drift with overlay
470- letterSpacing : 'normal' ,
471- // Text wrapping properties - must match textarea exactly
472- wordBreak : 'break-word' ,
473- whiteSpace : 'pre-wrap' ,
474- overflowWrap : 'break-word' ,
475- // Hide overlay scrolling to avoid dual scroll offsets
405+ fontFamily : 'inherit' ,
406+ lineHeight : 'inherit' ,
407+ width : '100%' ,
408+ height : `${ height } px` ,
476409 overflow : 'hidden' ,
477- // Box sizing to ensure padding is calculated correctly
478- boxSizing : 'border-box' ,
479- // Match text rendering
480- textRendering : 'auto' ,
481410 } }
482411 >
483- < div
484- ref = { overlayInnerRef }
485- style = { {
486- willChange : 'transform' ,
487- lineHeight : '21px' ,
488- } }
489- >
490- { formatDisplayText ( value ?. toString ( ) ?? '' , true ) }
491- </ div >
412+ { formatDisplayText ( value ?. toString ( ) ?? '' , true ) }
492413 </ div >
493414
494415 { /* Wand Button */ }
0 commit comments