Skip to content

feat(clipping): accept [top,right,bottom,left] margin tuple#46

Merged
chiefcll merged 3 commits into
mainfrom
feat/clipping-margins
May 27, 2026
Merged

feat(clipping): accept [top,right,bottom,left] margin tuple#46
chiefcll merged 3 commits into
mainfrom
feat/clipping-margins

Conversation

@chiefcll
Copy link
Copy Markdown
Contributor

Summary

  • Widen the clipping prop type to boolean | [number, number, number, number] so callers can let children spill outward by a per-side pixel amount before the scissor clips them. Negative values inset the clip rect.
  • Setter normalizes the tuple into four scalar margin fields and keeps props.clipping a plain boolean, so hot-path === true/=== false checks and the per-frame calculateClippingRect math stay branch-free and allocation-free. Optimized for the common case where clipping is set once at construction — the setter early-returns on identical re-assignment.
  • Expands the node's strictBound by the same margins so descendants sitting inside the margin region aren't culled as out-of-bounds before the scissor sees them.

Why

Today clipping: true is all-or-nothing — useful for hard edges, but several common UI patterns (focus halos, drop shadows, hover scales) want a clip that's slightly looser than the node's geometry. Reusing the [top, right, bottom, left] shape from boundsMargin keeps the API consistent.

Reviewer notes

  • No backend code changes — both WebGL (SdfRenderOp scissor) and Canvas2D (ctx.clip) read the existing clippingRect, which is the only thing the new code adjusts.
  • The visual regression snapshot for examples/tests/clipping-margin.ts is not committed yet — needs pnpm test:visual:update --ci (or CI on this PR) to generate the certified PNG. Please verify the diff before merging.

Test plan

  • pnpm build — clean
  • pnpm test — 209 passed (13 new tests for the clipping property)
  • pnpm lint — no new warnings on touched files
  • Visual regression snapshot generated and reviewed (clipping-margin-1.png)

🤖 Generated with Claude Code

chiefcll and others added 3 commits May 26, 2026 22:20
Widen the `clipping` prop type to `boolean | [number, number, number, number]`
so callers can opt into clipping that lets children spill outward by a per-side
pixel amount before the scissor cuts them off (negative values inset).

The setter normalizes the tuple into four scalar margin fields and keeps
`props.clipping` a plain boolean, so the hot-path `=== true`/`=== false`
checks and the per-frame `calculateClippingRect` math stay branch-free and
allocation-free. Optimized for the common case where clipping is set once at
construction: the setter early-returns on identical re-assignment.

Also expands the node's `strictBound` by the same margins so descendants
sitting inside the margin region aren't culled as out-of-bounds before the
scissor sees them.

Adds unit tests for setter normalization, the clip-rect math, parent-clip
intersection, and the rotation guard, plus a `clipping-margin` visual
regression example covering boolean, per-side, all-sides, and inset variants.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
… fields

The four `_clipMarginT/R/B/L` fields were sized for a hot path that never
materializes — clipping margins are an uncommon opt-in, not a per-frame
read. Storing the tuple directly on `props.clipping` removes four fields
per CoreNode instance and the constructor's save-and-restore plumbing.

Hot-path comparisons that previously checked `props.clipping === true`
now use `!== false`; the two places that need the per-side values
(`calculateClippingRect`, `createRenderBounds`) extract them inline with
an `Array.isArray` check, which only runs when the corresponding update
bit is set.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@chiefcll chiefcll merged commit 308d204 into main May 27, 2026
1 check passed
@chiefcll chiefcll deleted the feat/clipping-margins branch May 27, 2026 02:39
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.

1 participant