Rewrite landing around keep → share → see - #142
Conversation
Lead with personal clip library, then frictionless share and DuckerWeb inspect/diff, with light in-page demos instead of the review-first case cards. Co-authored-by: Cursor <cursoragent@cursor.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
📝 WalkthroughWalkthroughThe landing route is redesigned around new clip-library and inspection visuals, with updated metadata, animated section content, copy/share interactions, calls to action, and the existing bottom fade integrated into the new composition. ChangesLanding Page Redesign
Estimated code review effort: 3 (Moderate) | ~25 minutes 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@src/app/components/landing/clip-library-demo.tsx`:
- Around line 42-52: Update handleCopy and handleShare to store each mode’s
timeout handle, clear the existing handle before scheduling a replacement, and
retain the latest handle. Add unmount cleanup to clear any pending copied and
shared timers so stale callbacks cannot update state after the component is
removed.
In `@src/app/components/landing/inspect-diff-visual.tsx`:
- Around line 183-188: Update the animated motion.g group in the inspect-diff
visual so its existing translate(362, 128) positioning is preserved: wrap it in
a static SVG g carrying the translation, and move the animation props to the
inner motion.g. Keep the current animation behavior and wire endpoint offset
unchanged.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: 43798fe0-a50c-4bb8-b701-d11bad4a305f
📒 Files selected for processing (3)
src/app/components/landing/clip-library-demo.tsxsrc/app/components/landing/inspect-diff-visual.tsxsrc/routes/index.tsx
| const handleCopy = (id: string) => { | ||
| setSharedId(null); | ||
| setCopiedId(id); | ||
| window.setTimeout(() => setCopiedId((cur) => (cur === id ? null : cur)), 1800); | ||
| }; | ||
|
|
||
| const handleShare = (id: string) => { | ||
| setCopiedId(null); | ||
| setSharedId(id); | ||
| window.setTimeout(() => setSharedId((cur) => (cur === id ? null : cur)), 2800); | ||
| }; |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
Prevent stale feedback timers from clearing a later action.
Clicking the same button again leaves its earlier timeout active, so it can clear the newer “copied”/“shared” feedback at the original deadline. Cancel and replace each mode’s existing timeout before scheduling a new one; also clear pending timers on unmount.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/app/components/landing/clip-library-demo.tsx` around lines 42 - 52,
Update handleCopy and handleShare to store each mode’s timeout handle, clear the
existing handle before scheduling a replacement, and retain the latest handle.
Add unmount cleanup to clear any pending copied and shared timers so stale
callbacks cannot update state after the component is removed.
| <motion.g | ||
| transform="translate(362, 128)" | ||
| initial={{ opacity: 0, y: 6 }} | ||
| whileInView={{ opacity: 1, y: 0 }} | ||
| viewport={{ once: true }} | ||
| transition={{ duration: 0.5, delay: 0.75 }} |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Locate SVG elements that combine a transform attribute with Motion x/y transforms.
rg -n -U 'transform=.*\n(?:.*\n){0,8}(?:initial|whileInView)=\{\{[^}]*\b[xy]:' \
src/app/components/landing/inspect-diff-visual.tsxRepository: tsoumdoa/hopperclip
Length of output: 157
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Inspect the target component and relevant package metadata without running repo code.
echo "== candidate file excerpt =="
sed -n '160,225p' src/app/components/landing/inspect-diff-visual.tsx
echo
echo "== motion/framer package versions from package manifests =="
for f in package.json pnpm-lock.yaml package-lock.json yarn.lock bun.lockb; do
if [ -f "$f" ]; then
echo "-- $f --"
if [ "$f" = "package.json" ]; then
rg -n '"(framer-motion|motion|react-dom)"' "$f" -C 1 || true
else
rg -n '(framer-motion|motion)\W|/framer-motion@|/motion@' "$f" -C 1 | head -80 || true
fi
fi
done
echo
echo "== all transform+motion svg groups near each other =="
python3 - <<'PY'
from pathlib import Path
p=Path('src/app/components/landing/inspect-diff-visual.tsx')
text=p.read_text().splitlines()
for i,line in enumerate(text,1):
if '<motion.g' in line.lower() or '<motion.path' in line.lower() or 'transform=' in line:
if any('initial' in l or 'whileInView' in l or 'transform=' in l for l in text[max(1,i-12):i+12]):
print(f"-- around line {i} --")
for j in range(max(1,i-8), min(len(text),i+14)+1):
print(f"{j:4}: {text[j-1]}")
print()
PYRepository: tsoumdoa/hopperclip
Length of output: 10863
🌐 Web query:
Motion docs f12 0.27.2 SVG transforms style transforms transform attribute
💡 Result:
In Motion (formerly Framer Motion), SVG transforms are handled specifically to overcome limitations of standard SVG behavior [1][2]. Key behaviors regarding SVG transforms, styles, and attributes in version 0.27.2 and the broader Motion 12+ ecosystem include: Transform Origins: By default, SVG elements apply transforms relative to the top-left corner of the viewBox [1][2]. Motion automatically changes this behavior for motion components to match CSS, making transforms relative to the element's center [1][2]. This is achieved by defaulting to transform-box: fill-box [1][3]. You can restore the default SVG behavior or customize it by explicitly setting the transformBox style (e.g., transformBox: "view-box") [1][2]. Styles vs. Attributes: - Motion generally prefers animating values via CSS styles (transform property) rather than the SVG transform attribute, as this provides better performance and consistent behavior across modern browsers [4][3]. - When animating properties like x, y, or scale on SVG elements, Motion components will default to using CSS transforms (translateX/translateY) [5]. - If you specifically need to animate positional attributes (like the x or y attributes of a or ), you should use the attr-prefixed props: attrX, attrY, and attrScale [1][6][5]. Recent Pipeline Unification: As of recent updates (part of the unified Motion architecture), SVG values that exist as valid CSS properties (such as fill, opacity, and others) are rendered as inline styles rather than presentation attributes [3]. This approach ensures deterministic specificity (inline styles take priority) and improves performance by avoiding attribute-mutation overhead [3]. For scenarios where you must animate the SVG transform attribute directly (e.g., complex strings or legacy compatibility), Motion does not provide a native shorthand for animating the raw string. You may need to use external parsing logic or utilize Motion's mix function to interpolate transform matrices manually before updating the attribute via a ref [7]. Top results: [1][2][3][5]
Citations:
- 1: https://motion.dev/docs/react-svg-animation
- 2: https://motion.dev/docs/svg-effect
- 3: Replace VisualElement value management and renderer with effects motiondivision/motion#3749
- 4: https://motion.dev/docs/upgrade-guide
- 5: [BUG] SVG rect X/Y sets translateX/translateY, not x/y attributes motiondivision/motion#1733
- 6: [BUG] It isn't possible to animate the
xandycoordinates of an SVG element usinganimatemotiondivision/motion#2596 - 7: https://stackoverflow.com/questions/79459054/how-do-you-animate-an-svg-transform-string-with-framer-motion
Preserve the panel’s SVG translation while animating it.
Motion renders SVG y as a CSS transform, so this group loses the transform="translate(362, 128)" position during animation. Wrap the animated group in a static translated <g> so it keeps the wire endpoint offset.
Proposed fix
- <motion.g
- transform="translate(362, 128)"
+ <g transform="translate(362, 128)">
+ <motion.g
initial={{ opacity: 0, y: 4 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true }}
transition={{ duration: 0.5, delay: 0.75 }}
- >
+ >
...
- </motion.g>
+ </motion.g>
+ </g>🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/app/components/landing/inspect-diff-visual.tsx` around lines 183 - 188,
Update the animated motion.g group in the inspect-diff visual so its existing
translate(362, 128) positioning is preserved: wrap it in a static SVG g carrying
the translation, and move the animation props to the inner motion.g. Keep the
current animation behavior and wire endpoint offset unchanged.
Use green-300 Shared badge, tag chips, and matching CTAs so the landing feels closer to the product UI. Co-authored-by: Cursor <cursoragent@cursor.com>
Let the header own the brand; hero leads with the job line instead. Co-authored-by: Cursor <cursoragent@cursor.com>
Summary
green-300CTAs, section labels, Shared badge) without leaving the dark themeTest plan
/signed out — brand only in header; hero is the job line + library preview/duckerweb/ghcards