feat(cdn): sync blink animation with React constants; update billing copy#427
Conversation
Replace pay-as-you-go pricing copy with subscription tier messaging (Standard 20/week at $20/mo, Pro unlimited at $200/mo) in both the quota-reached error and the billing dashboard description. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
… enter - Import BLINK_* constants from constants.ts instead of redefining locally; CDN and React now share identical timing/easing values. - Implement three-stage enter (enter-a instant → enter-b settle → steady) matching useBlinkMotionStage, using translate3d + will-change for GPU compositor hints. - Add cancelBlink() to guard against overlapping open/close sequences. - Call cancelBlink() in hidePopoverCleanup() to prevent orphaned timers. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Extract blinkTransition() to eliminate 3 duplicate template-literal transition strings in animateOpen/animateClose. - Type blinkRafId as number | null for consistency with settle/final timers. - Clear willChange in hidePopoverCleanup() for complete style reset. - Null blinkRafId in cancelBlink() after cancelling (mirrors timer pattern). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub. 4 Skipped Deployments
|
✅ Playwright Test ReportStatus: Tests passed 📊 Download Report & Snapshots (see Artifacts section) What's in the Visual SnapshotsThe gallery includes visual snapshots for:
Run ID: 24430050432 |
Code ReviewOverall this is a solid PR — the constant consolidation is the right call and the three-stage blink pattern mirrors 🔴 Failing tests (must fix)The billing copy changes in
expect(result).toContain("Payment required"); // now says "Quota reached"
expect(result).toContain("$0.05/doc"); // removed from output
expect(result).toContain("Pay-as-you-go"); // removed
expect(result).toContain("spend cap"); // removedThese need to be updated to match the new strings ( 🟡
|
…copy Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ion target When a page-expand or page-collapse ghost transition fires, isViewTransitioning() becomes true which defers CDN reposition. But waitForPage*Target polls for the keyhole/annotation rect before the CDN repositions, finding a stable rect at the stale (wrong) wrapper position. The ghost then animates toward the wrong target. Fix: useLayoutEffect in CdnPopoverWrapper calls reposition() synchronously on viewState.current changes. useLayoutEffect fires inside flushSync (after React DOM update, before flushSync returns), so the wrapper is at the correct position before the transition code reads any target rects. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Summary
BLINK_*timing/easing/opacity/scale constants fromsrc/react/constants.tsinstead of local redefinitions. CDN and React now share identical values — the old CDN hadBLINK_ENTER_DURATION_MS = 180msvs React's correct120ms.useBlinkMotionStage, usingtranslate3d+will-changefor GPU compositor hints. Replaces the previous single-transition approach.cancelBlink()guard: Cancels any in-flight RAF + timers before starting a new open/close, preventing overlapping sequences. Also called inhidePopoverCleanup()to prevent orphaned timers.blinkTransition()helper to deduplicate three identicalopacity Xms E, transform Xms Etemplate strings. TypesblinkRafIdasnumber | nullfor consistency. ClearswillChangein cleanup.Test plan
npx deepcitation billing— verify updated dashboard descriptionbun run lintpasses (verified locally)