Skip to content

fix(card): keep share-asset stickers off the username pill#2352

Merged
Hugo0 merged 1 commit into
mainfrom
fix/share-asset-pill-coverage
Jul 3, 2026
Merged

fix(card): keep share-asset stickers off the username pill#2352
Hugo0 merged 1 commit into
mainfrom
fix/share-asset-pill-coverage

Conversation

@0xkkonrad

Copy link
Copy Markdown
Contributor

Summary

The card share asset ("I'M IN!") could render a badge sticker on top of the peanut.me/<handle> username pill, hiding the handle — which is the entire point of a shareable asset. Two compounding causes:

  • Layout: the final SEPARATION_PASSES cleanup in placeStamps dropped the pill keep-out (it only re-applied the card keep-outs), so pairwise sticker separation could shove a sticker onto the pill on the last pass. Measured up to ~65% of the pill covered across a username × badge-count sweep.
  • Render: the pill was drawn at z-index: 3, below the stickers at z-index: 4, so any sticker that landed over it painted on top.

Fix

  • Re-apply the pill keep-out in the final separation pass as a soft pull (0.5 factor, like the existing edge-margin nudge). A hard shove here re-creates the corner deadlock the final pass exists to break — the soft pull avoids that while keeping stickers off the pill.
  • Render the pill at z-index: 5, above the stickers, so the handle can never be covered even if a corner drifts under it.

Both are needed: the soft pull keeps the collage clean; the z-index is the hard guarantee.

Risks / blast radius

Small and self-contained — only the share-asset layout engine + its renderer. No API/contract changes, no shared-state or money paths. placeStamps stays deterministic (same seed → same layout). Only visible effect elsewhere: a badge sticker may now sit slightly higher/left near the bottom-right, and the pill paints over any residual overlap.

QA

  • 29 layout unit tests pass, incl. a new regression test asserting no sticker covers >40% of the rendered pill rect across a seed/count sweep (fails on the old code at ~52–65%, passes now at ≤3%).
  • Verified against the real <ShareAssetD3 /> rendered from the running app across 20 representative + 5 extreme configs (1-char / 12-char / emoji handles, 1–12 badges): every handle legible, pill on top.

The "I'M IN!" share asset could render a badge sticker on top of the
peanut.me/<handle> pill, hiding the handle — the whole point of the
shareable asset. Two compounding causes:

- the final SEPARATION_PASSES cleanup dropped the pill keep-out, so
  pairwise separation could shove a sticker onto the pill on the last
  pass (up to ~65% of the handle covered in the worst case);
- the pill rendered below the stickers (z-index 3 < 4), so any sticker
  that landed over it painted on top.

Restore the keep-out in the final pass as a *soft* pull — a hard shove
re-creates the corner deadlock that pass exists to break — and render
the pill above the stickers (z-index 5) so the handle can never be
covered. Add a regression test asserting no sticker covers >40% of the
rendered pill rect across a seed/count sweep.
@vercel

vercel Bot commented Jul 3, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
peanut-wallet Ready Ready Preview, Comment Jul 3, 2026 6:23pm

Request Review

@coderabbitai

coderabbitai Bot commented Jul 3, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Walkthrough

Updates the @username pill's z-index and description in ShareAssetD3.tsx so it renders above stickers, adds a soft pull-away adjustment in the sticker layout's final separation pass to avoid pill overlap, and adds a regression test validating sticker-to-pill overlap stays within a defined maximum.

Changes

Pill layering and overlap prevention

Layer / File(s) Summary
Pill z-index update
src/components/Card/share-asset/ShareAssetD3.tsx
Updates the pill's inline comment and changes zIndex from 3 to 5 so it renders above stickers.
Final pass soft pill pull
src/components/Card/share-asset/shareAssetLayout.ts
Adds hitsPill detection and a soft pull-away nudge (via exitLeft/exitUp) in the final separation pass alongside pairwise separation, keep-out enforcement, and clamping.
Pill overlap regression test
src/components/Card/share-asset/__tests__/shareAssetLayout.test.ts
Imports PILL_RIGHT, PILL_BOTTOM, and pillKeepoutBox, and adds a test checking sticker overlap with the pill keep-out area stays under MAX_PILL_COVER across badge counts and seeds.

Estimated code review effort: 2 (Simple) | ~10 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title accurately summarizes the main fix: keeping share-asset stickers off the username pill.
Description check ✅ Passed The description directly explains the sticker-over-pill bug and the matching layout/render fix.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions

github-actions Bot commented Jul 3, 2026

Copy link
Copy Markdown
Contributor

Code-analysis diff

Painscore total: 5872.71 → 5873.18 (+0.47)
Findings: 0 net (+9 new, -9 resolved)

🆕 New findings (9)

  • critical complexity — src/components/Card/share-asset/shareAssetLayout.ts — CC 57, MI 47.1, SLOC 266
  • medium high-mdd — src/components/Card/share-asset/shareAssetLayout.ts:155 — placeStamps: MDD 56.1 (uses across many lines from declarations)
  • medium high-mdd — src/components/Card/share-asset/ShareAssetD3.tsx:94 — ShareAssetD3: MDD 44.1 (uses across many lines from declarations)
  • medium structural-dup — components/Card/share-asset/shareAssetLayout.ts:194 — 43 duplicate lines / 346 tokens with components/Card/share-asset/shareAssetLayout.ts:270
  • medium method-complexity — src/components/Card/share-asset/shareAssetLayout.ts:155 — placeStamps CC 28 SLOC 136
  • medium high-mdd — src/components/Card/share-asset/ShareAssetD3.tsx:351 — HeroMessageEl: MDD 24.0 (uses across many lines from declarations)
  • medium hotspot — src/components/Card/share-asset/ShareAssetD3.tsx — 23 commits, +1228/-780 lines since 6 months ago
  • medium nextjs-raw-img — src/components/Card/share-asset/ShareAssetD3.tsx:327 — Use next/image
  • low high-mdd — src/components/Card/share-asset/ShareAssetD3.tsx:324 — StickerEl: MDD 14.5 (uses across many lines from declarations)

✅ Resolved (9)

  • src/components/Card/share-asset/shareAssetLayout.ts — CC 55, MI 47.56, SLOC 258
  • src/components/Card/share-asset/shareAssetLayout.ts:155 — placeStamps: MDD 50.4 (uses across many lines from declarations)
  • src/components/Card/share-asset/ShareAssetD3.tsx:94 — ShareAssetD3: MDD 44.0 (uses across many lines from declarations)
  • components/Card/share-asset/shareAssetLayout.ts:194 — 43 duplicate lines / 346 tokens with components/Card/share-asset/shareAssetLayout.ts:266
  • src/components/Card/share-asset/shareAssetLayout.ts:155 — placeStamps CC 26 SLOC 128
  • src/components/Card/share-asset/ShareAssetD3.tsx:350 — HeroMessageEl: MDD 24.0 (uses across many lines from declarations)
  • src/components/Card/share-asset/ShareAssetD3.tsx — 22 commits, +1223/-776 lines since 6 months ago
  • src/components/Card/share-asset/ShareAssetD3.tsx:326 — Use next/image
  • src/components/Card/share-asset/ShareAssetD3.tsx:323 — StickerEl: MDD 14.5 (uses across many lines from declarations)

@github-actions

github-actions Bot commented Jul 3, 2026

Copy link
Copy Markdown
Contributor

🧪 UI test report — ✅ all green

Suites

  • unit: 1679 ran, 0 failed, 0 skipped, 26.0s

📊 Coverage (unit)

metric %
statements 55.1%
branches 38.1%
functions 43.0%
lines 55.0%
⏱ 10 slowest test cases
time test
3.5s src/components/Card/share-asset/__tests__/shareAssetLayout.test.ts › never places two stickers in heavy overlap (broad seed sweep)
0.4s src/components/Card/share-asset/__tests__/shareAssetLayout.test.ts › every sticker stays within canvas at any count
0.4s src/app/actions/__tests__/api-headers.test.ts › should include Content-Type in updateUserById
0.4s src/app/actions/__tests__/api-headers-extended.test.ts › should not include apiKey in updateUserById body
0.2s src/components/Card/share-asset/__tests__/shareAssetLayout.test.ts › keeps stickers off the username pill (final pass respects the keep-out)
0.1s src/app/(mobile-ui)/qr-pay/__tests__/qr-pay-states.test.tsx › Perk claimed shows shake class + go home button
0.1s src/components/Global/GeneralRecipientInput/__tests__/GeneralRecipientInput.test.tsx › should handle valid 9-digit US account
0.1s src/components/Global/GeneralRecipientInput/__tests__/GeneralRecipientInput.test.tsx › should handle too long for US account
0.1s src/components/Request/__tests__/request-states.test.tsx › renders request form with nav header, action card, QR code, amount input, and create button
0.1s src/components/Global/GeneralRecipientInput/__tests__/GeneralRecipientInput.test.tsx › should handle valid ETH address in lowercase
📍 Inline annotations are in the **Unit test report** check above. Coverage artifact: `coverage-unit`. Generated by `.github/workflows/tests.yml`.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
src/components/Card/share-asset/shareAssetLayout.ts (1)

246-252: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

LGTM! Soft pull is correctly scaled from the existing hard-push logic and matches the documented deadlock-avoidance rationale.

Optional: the two hitsPill/exit blocks (main loop vs final pass) are near-identical except for the 0.5 scale factor — could be extracted into a small pillPull(p, half, pill, strength) helper to avoid future drift between the hard and soft variants.

♻️ Example extraction
+function pillPull(p: { x: number; y: number }, half: number, pill: { x0: number; y0: number }, strength: number) {
+    if (!hitsPill(p.x, p.y, half, pill)) return
+    const exitLeft = p.x + half - (pill.x0 - PILL_PAD)
+    const exitUp = p.y + half - (pill.y0 - PILL_PAD)
+    if (exitLeft < exitUp) p.x -= exitLeft * strength
+    else p.y -= exitUp * strength
+}

Then call pillPull(p, half, pill, 1) in the main loop and pillPull(p, half, pill, 0.5) in the final pass.

Also applies to: 308-318

🤖 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/components/Card/share-asset/shareAssetLayout.ts` around lines 246 - 252,
The `hitsPill`/exit adjustment logic is duplicated between the main loop and the
final pass in `shareAssetLayout`, differing only by the pull strength, so
extract that block into a small `pillPull(p, half, pill, strength)` helper and
reuse it from both places. Keep the existing `hitsPill`, `PILL_PAD`, and
exit-axis choice logic inside the helper, then call it with the appropriate
strength values to prevent the hard/soft variants from drifting apart.
🤖 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.

Nitpick comments:
In `@src/components/Card/share-asset/shareAssetLayout.ts`:
- Around line 246-252: The `hitsPill`/exit adjustment logic is duplicated
between the main loop and the final pass in `shareAssetLayout`, differing only
by the pull strength, so extract that block into a small `pillPull(p, half,
pill, strength)` helper and reuse it from both places. Keep the existing
`hitsPill`, `PILL_PAD`, and exit-axis choice logic inside the helper, then call
it with the appropriate strength values to prevent the hard/soft variants from
drifting apart.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: eb555b18-efbc-4a20-8416-0fa04efea825

📥 Commits

Reviewing files that changed from the base of the PR and between bbd8b6d and 02cd574.

📒 Files selected for processing (3)
  • src/components/Card/share-asset/ShareAssetD3.tsx
  • src/components/Card/share-asset/__tests__/shareAssetLayout.test.ts
  • src/components/Card/share-asset/shareAssetLayout.ts

@0xkkonrad 0xkkonrad marked this pull request as ready for review July 3, 2026 18:29
@Hugo0 Hugo0 changed the base branch from dev to main July 3, 2026 18:42
@Hugo0 Hugo0 merged commit cd0e0a3 into main Jul 3, 2026
18 of 20 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants