Skip to content

V3: reusable layer masks (gradient, ellipse, rectangle, brush) - #165

Merged
git-chad merged 36 commits into
git-chad/shader-lab-v3-planfrom
git-chad/v3-layer-masks
Oct 1, 2026
Merged

git-chad merged 36 commits into
git-chad/shader-lab-v3-planfrom
git-chad/v3-layer-masks

Conversation

@git-chad

Copy link
Copy Markdown
Collaborator

Every layer and group now has an optional Mask with four tools: linear/radial gradient, ellipse, rectangle and brush. No pen tool. Effects choose Limit effect (outside the mask the image stays untouched) or Cut content (outside becomes transparent and reveals layers outside the isolated group); sources and groups always cut their own contribution. Invert, Enabled, center/size/rotation and Feather (ellipse/rectangle) are exposed, and geometric shapes have direct canvas handles: center, X axis (rotation + width), Y axis (height); the linear gradient uses start/end handles. Each drag or brush stroke is one undo step.

The brush reuses the accepted Cells paint interaction (Reveal/Erase, Brush Size, Clear, automatic edit entry, Esc/Done) and its compact pc1 coverage codec through a shared paint store with a target, without touching Cells geometry or existing paintings. Coordinates are centered shorter-edge composition units, identical to Cells paint; the mask follows the viewport until the stable artboard (roadmap 2.8).

Rendering: a single renderer/layer-mask.ts (byte-identical in editor and runtime) is applied by PassNode after blending. Shape/scope/invert/enabled rebuild the material; center/size/rotation/feather are uniforms; the brush texture is created lazily and re-uploaded only when the painted string changes. Coverage is interpolated premultiplied with a guarded division, so transparent inputs produce no dark halos. The legacy Mode → Mask path and its frozen samples are unchanged. Groups carry the mask through the composition tree and pipeline signatures.

Persistence and surfaces: optional mask in .lab files (older files unchanged), duplication, history, shader export, runtime ShaderLabLayerConfig.mask (partial values normalized), MCP update_layer.mask. Roadmap 1.4, test README and V3-VISUAL-REFERENCES updated; references 17 and 20 were reopened.

Validation:

  • bun run check — passes; the same three preexisting lint warnings.
  • bun run test:composition — full suite passes, including 147 new editor/runtime GPU cases in tests/composition/layer-masks.mjs: every shape, invert, feather, rotation, scope, disabled state, transparent inputs, brush row parity with Cells paint, group cutouts with an untouched external layer, uniform-only moves versus structural rebuilds, hydration, history merge, duplication, draft previews, save/reopen, export and runtime pixel parity. Legacy baselines untouched.
  • bun tests/composition/layer-masks-ui.mjs — actual editor: mask section per layer, handle drag/rotate/Escape cancel, one-undo-per-drag, scope, group mask without scope control, brush strokes/undo/redo/clear, save/reopen. cell-paint-ui.mjs regression passes after the paint store refactor.
  • Verified in the real editor that the rendered ellipse and the canvas handles coincide (.context/layer-masks-orientation.png). Browser GPU tests use SwiftShader; this is not native performance validation.

Not in this slice (recorded in roadmap 1.4): animatable mask parameters, using a shape layer as a mask (2.7), stable artboard (2.8).

Stacked on #164 (git-chad/v3-clean-projects), within integration #150. Nothing merges to main before V3 is complete. Focused user test: tests/composition/LAYER-MASKS-MANUAL-QA.md. Local Gradient Map (2.5.2) follows after acceptance.

@vercel

vercel Bot commented Sep 21, 2026 •

Copy link
Copy Markdown
Contributor

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

Project Deployment Actions Updated
shader-lab Ready Ready Preview Oct 1, 2026 7:05am UTC

@greptile-apps

greptile-apps Bot commented Sep 21, 2026

Copy link
Copy Markdown

RetriggerConfidence Score: 2/5

The PR is not yet safe to merge because asynchronous rebuilds can discard mask changes, undo during handle dragging can restore stale state, and viewport-shaped masks can differ in export.

Summary

This PR adds reusable gradient, geometric, and painted masks across the editor and runtime, including persistence, MCP updates, direct canvas controls, group support, export, and extensive composition tests.

  • Shares mask rendering behavior between editor and runtime.
  • Refactors Cells painting into a target-aware paint workflow.
  • Adds mask persistence, duplication, history, runtime configuration, and shader export.
  • Introduces blocking lifecycle and coordinate-parity issues around asynchronous rebuilds, handle cancellation, and export framing.

Reviews (1) · Last reviewed commit: "Add reusable layer masks with gradient, ..."

Comment thread src/components/editor/editor-canvas-viewport.tsx
Comment thread src/components/editor/mask-handles-overlay.tsx
…roup failures

Media load callbacks now ignore replaced or removed passes and pending loads are cleared on disposal. Strict-mode pass failures are reported once at the originating pass instead of at every group boundary.
Publishing now drops every descendant of a hidden group and clears the
parentId of any retained layer (the audio source) whose parent was removed,
so the published scene passes hierarchy validation on reopen.
Cap the neighbor row and column search to two cells each side so the
worst-case cellAt count per fragment is 25 instead of unbounded in the
packed minimum-size, maximum-stroke configuration. Mirrored in the
runtime package.
…e on buttons

Clear the active paint layer when the layer stops being paintable (selection,
mode, visibility or lock change) so Paint never resumes without Edit Paint.
Only preventDefault on Space for non-interactive targets so focused buttons
and links still activate.
Create the .context artifact directory up front and pick the history shortcut modifier from the platform.
Importing a project now disarms the armed remix draft and clears the remix origin alongside the active draft, so later edits cannot autosave under the previous remix title.
…handle unmount

Mask coordinates are now measured against the composition crop frame in the
renderer and the handles overlay, so preview and export agree when the
viewport and composition aspect ratios differ.
Unmounting the handles mid-drag no longer writes the drag-start mask back,
which overwrote the result of an undo.
@git-chad
git-chad force-pushed the git-chad/v3-clean-projects branch from 2ebaeb5 to 9ba9c63 Compare October 1, 2026 07:04
@git-chad
git-chad force-pushed the git-chad/v3-layer-masks branch from 79ea3a6 to 3573157 Compare October 1, 2026 07:04
@git-chad
git-chad changed the base branch from git-chad/v3-clean-projects to git-chad/shader-lab-v3-plan October 1, 2026 15:12
@git-chad
git-chad merged commit 3573157 into git-chad/shader-lab-v3-plan Oct 1, 2026
5 checks passed

This branch was successfully deployed

1 active deployment
Preview — 35731573 Deployed Oct 1, 2026 by vercel[bot]
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