Repository navigation
fix(canvas): hide masks under previews again; tidy Layers panel visibility and nesting - #10111
Conversation
Inpaint masks, regional guidance and the regenerate tint stay hidden under a shown preview even when a mask is selected. They show only while a brush, eraser, move or shape drag edits the selected mask or region, then hide on release.
Layer, group and child rows end in an eye beside the lock, aligned with the stack headers' eye and add buttons. Child rows trade their leading dot for the same eye; the Layers panel docs and screenshot match.
The staging bar's result count drops its pill and is separated from the step controls by the bar's divider.
Groups connect to their members and layers to their adjustment and modifier rows with rounded branch lines. Each nesting level indents 28px so a branch fits between parent and child; the Layers panel docs and screenshot match.
Pile cards shrink only by the offsets they need, so a one-member group shows a full-size thumbnail.
Pfannkuchensack
left a comment
There was a problem hiding this comment.
Reviewed at ed3ab80. No blockers. The reveal logic matches the spec on every path I traced. Release, Escape, pointercancel, lost capture, reset() and a tool switch mid-drag all end in setGestureActive(false). Pan, zoom, picker, frame and selection never reveal, and a selected group reveals only for move. Invalidation now happens only on flag transitions while a preview is shown, which is cheaper than before. LayerActiveDot has no remaining users, and the golden image is a rename only.
Low
- The reveal is decided once at press time (
engine.ts~1526). An undo or remote selection change mid-drag keeps the reveal until release. This is documented in ARCHITECTURE.md and fine, just noting it. - The pipeline -> engine wiring is covered only indirectly through
engine.test.ts. A pipeline-level test for "middle-button pan does not fireonGestureChange" and "reset()mid-gesture clears the flag" would catch the regression that matters most: masks stuck visible over previews. - The child-row eye is
tabIndex={-1}(LayerChildRow.tsx~320), like the layer-row eye. Please confirm that keyboard users can toggle child rows through a row-level shortcut. LayerTreeGuidesuses hard-coded offsets tied to the row'spb="0.5"gap; a change to row height or gap would misalign the connectors silently.
CI
In frontend-webv2-tests every Vitest file passed. The job then failed in a later step on locator.waitFor: Timeout 30000ms exceeded. Please check whether that step is related to the Layers panel changes or is a flake.
…peline Assert onGestureChange fires once per transition on release, cancel, Escape, reset and tool switch, before the tool sees the press, and never for middle-button pans.
lstein
left a comment
There was a problem hiding this comment.
Approving at 6ac4774. The reveal logic held up against the paths I attacked: release, Escape, pointer cancel, tool switch, reset(), a preview published or cleared mid-gesture, and Alt/Space temporary tools. The findings below are nits and none of them block.
Nit 1: the selected mask's cache now depends on its Layers panel thumbnail
While a preview is shown, the mask stack is out of frame demand (compositeFrame.ts:214-228), so a selected mask is neither rasterized nor protected from eviction (rasterController.ts:172). If its cache is missing at press time, the brush refuses with the "still loading" toast (paintTool.ts:105, :202), while the masks still reveal for that press.
- In the normal layout this looks harmless: a mounted row's
LayerThumbnailrequests a thumbnail, and that rebuilds the cache. A browser run with the budget lowered to 1 MB showed no refusals, though that run may not have exceeded the budget. - An engine-level test with no panel did refuse with
layer-loading, and adding arequestLayerThumbnailcall made the refusal go away. - From reading the code, a refusal needs the row to be unmounted (collapsed stack, scrolled away, panel closed, degraded thumbnails) as well as a missing cache. I have not reproduced that in a browser.
If you want to close this off, keep the selected mask or region cached while it is hidden. Adding it to the working set is not enough on its own, because ensureLayerCaches only walks frame.leaves.
Nit 2: a Move click with no drag flashes every mask
moveTool.ts:209 decides at press time, so a click that never crosses the drag threshold shows all masks for the length of the press and edits nothing. engine.test.ts:16052 expects this, but the PR description says masks appear only while a drag is editing. Revealing at the drag threshold would match the description.
Nit 3: edits made outside a pointer gesture are not visible under a preview
With a mask selected and a preview shown, undoing a stroke or closing a polygon changes nothing visible, and the existing mask can't be seen until you press. The polygon case is documented; I traced the undo case in the code but did not run it. This may be an acceptable result of the requested behaviour, so I'm only flagging it in case it wasn't intended.
…ask loaded under previews Move shows masks over a preview when its drag crosses the threshold, not on a plain click. While a preview hides masks, the on-screen selected mask or region stays cached and protected from eviction, so a brush press no longer refuses as still loading.
Summary
+buttons at every nesting depth. Child rows replace their leading on/off dot with the same eye; a canvas-only hidden layer shows its closed eye just left of the eye.Related Issues / Discussions
User report: "when rendering with inpaint, the inpaint mask doesn't automatically hide while rendering."
QA Instructions
+above them.Checks:
pnpm check:releasepasses (lint, typecheck, unit, browser, fixtures, performance architecture/build/contracts/browser, project files, accessibility). Changed interactions verified in a browser against the mock backend; the progress-preview case was not seen live (the mock cannot generate) and is covered by engine tests. The later move-threshold reveal and the loaded-while-hidden mask are covered by engine and composite tests, not seen live.Review
Resolved findings: transform and gradient no longer claim to edit masks; brush and shape reveal masks only when the press will actually edit the selected mask, and move only once its drag passes the threshold (no flash on locked or hidden layers, none when the brush would create a new layer); tests now check a frame mid-gesture for pan, Space-hold and colour-picker presses; header-to-row column alignment shares constants and has a browser test. Remaining limits: the row's hover fill starts after the indent gutter (clicks in the gutter still select); dragging sideways to change depth takes 28px per level; groups nested beyond about five levels overflow a narrow panel. Moving a multi-selection whose primary layer is a raster moves an included mask while it is hidden. Edits that no revealing gesture carries, such as undoing a mask stroke or closing a polygon, change nothing visible while a preview hides the masks.
Checklist
What's Newcopy (if doing a release after this PR)