Skip to content

Commit 098af07

Browse files
Aymericrcursoragent
andcommitted
fix(site): settle hero cards when the cycle interrupts their typing animation
Background-tab timer throttling let the parent cycle deactivate a card mid-animation; cleanup only cleared the timer, leaving the card stuck on "Typing..." (and the fix button stuck pressed). Track in-flight animation with a ref and snap to the full example value on interruption, without clobbering manual or auto-fixed values. Co-authored-by: Cursor <cursoragent@cursor.com>
1 parent 6667c4c commit 098af07

1 file changed

Lines changed: 29 additions & 8 deletions

File tree

apps/site/src/components/site/universal-input.tsx

Lines changed: 29 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -313,6 +313,7 @@ function FloatingInputCard({
313313
const focusedRef = useRef(false)
314314
const timerRef = useRef<number | null>(null)
315315
const fixTimerRef = useRef<number | null>(null)
316+
const animatingRef = useRef(false)
316317

317318
const stopTyping = useCallback(() => {
318319
if (timerRef.current !== null) {
@@ -321,6 +322,12 @@ function FloatingInputCard({
321322
}
322323
}, [])
323324

325+
const cancelTyping = useCallback(() => {
326+
stopTyping()
327+
animatingRef.current = false
328+
setTyping(false)
329+
}, [stopTyping])
330+
324331
const stopFix = useCallback(() => {
325332
if (fixTimerRef.current !== null) {
326333
window.clearTimeout(fixTimerRef.current)
@@ -351,6 +358,7 @@ function FloatingInputCard({
351358
}
352359

353360
timerRef.current = window.setTimeout(() => {
361+
animatingRef.current = true
354362
setTyping(true)
355363
setValue('')
356364
let index = 0
@@ -361,6 +369,7 @@ function FloatingInputCard({
361369

362370
if (index >= example.value.length) {
363371
timerRef.current = window.setTimeout(() => {
372+
animatingRef.current = false
364373
setTyping(false)
365374
setFixReady(example.autoFix === true)
366375
timerRef.current = null
@@ -373,7 +382,19 @@ function FloatingInputCard({
373382

374383
timerRef.current = window.setTimeout(tick, typingDelayFor(example.value, 0))
375384
}, TYPE_START_DELAY_MS)
376-
return stopTyping
385+
386+
// Throttled background-tab timers can let the parent cycle deactivate the
387+
// card mid-animation; settle to the full value so it never sticks on
388+
// "Typing...". Skipped when the animation already finished, so a manual or
389+
// auto-fixed value is never clobbered.
390+
return () => {
391+
stopTyping()
392+
if (animatingRef.current) {
393+
animatingRef.current = false
394+
setTyping(false)
395+
setValue(example.value)
396+
}
397+
}
377398
}, [active, example.autoFix, example.value, manual, reduceMotion, stopFix, stopTyping])
378399

379400
const result = useMemo(
@@ -400,17 +421,19 @@ function FloatingInputCard({
400421
}, FIX_CURSOR_SETTLE_DELAY_MS)
401422
}, FIX_REVEAL_DELAY_MS)
402423

403-
return stopFix
424+
return () => {
425+
stopFix()
426+
setAutoFixing(false)
427+
}
404428
}, [active, fixReady, fixValue, manual, reduceMotion, stopFix])
405429

406430
const applyFix = () => {
407431
if (!fixValue) {
408432
return
409433
}
410434

411-
stopTyping()
435+
cancelTyping()
412436
stopFix()
413-
setTyping(false)
414437
setAutoFixing(false)
415438
setFixReady(false)
416439
setManual(true)
@@ -470,19 +493,17 @@ function FloatingInputCard({
470493
}
471494
}}
472495
onChange={(event) => {
473-
stopTyping()
496+
cancelTyping()
474497
stopFix()
475-
setTyping(false)
476498
setAutoFixing(false)
477499
setFixReady(false)
478500
setManual(true)
479501
setValue(event.target.value)
480502
}}
481503
onFocus={() => {
482504
focusedRef.current = true
483-
stopTyping()
505+
cancelTyping()
484506
stopFix()
485-
setTyping(false)
486507
setAutoFixing(false)
487508
setFixReady(false)
488509
}}

0 commit comments

Comments
 (0)