feat(editor): Snap on and surroundings fill by default - #113
Open
lukejmorrison wants to merge 8 commits into
Open
feat(editor): Snap on and surroundings fill by default#113lukejmorrison wants to merge 8 commits into
lukejmorrison wants to merge 8 commits into
Conversation
Select already grows the canvas when you drag a layer off the screenshot; this lets the screenshot itself be the source of that layer. An empty marquee locks a pixel clip; drag it to lift those pixels, punch a hole (backdrop shows through), and commit one Clip operation. Repeat to pull another region. Ctrl+Z undoes the hole and the layer together. Covered by runClipSmoke (copy/punch/mapping) and runClipMappingSmoke (empty marquee, lift, hole, undo, repeat). The suite fails without the change. make check is green.
After an empty Select marquee locks a region, a swatch row appears under the rect: transparent (checkerboard, default), the palette colours, and custom. 1–8 and the eyedropper set the infill while the clip is armed. The fill is stored on Operation::Clip so undo/replay keep the hole.
Ctrl+Z during a lift cancels only the in-progress drag — QTest (and real keyboards) send Control before Z, so a modifier-only press must not drop the lift and then let Z undo the previous op. Empty marquees need a 12px floor, crop chrome stays until lift, and the hole-fill fly-out docks to the side. Replay keeps a present clip tile, PNG loads only on Clip, and live lift paints a downsampled pixmap with snap hysteresis. Co-authored-by: Luke Morrison <lukejmorrison@users.noreply.github.com>
Rectangle, ellipse, and lasso masks with click-to-snap, keyboard hole fill, and drag-to-lift. Spec for the follow-on to rectangular clip-out.
Select cycles clip shape with V and a chip strip. Empty-canvas drags that miss layers still lock a mask; R/E/F keep arming drawing tools. Snap ray-casts to an axis-aligned ellipse. Lifted tiles carry alpha outside the path. Fable and Sol P0s applied to the key layer.
Cycling clip shape now updates the path already on screen (rect to ellipse, and Snap ray-casts from the mask centre). Installed to ~/.local/bin so Print no longer launches last morning's build.
…e hole Snap treats the drag as a search window: 8-connected flood with a 1px close, an enclosing circle for Ellipse (pins stay in), and a rounded rect for Rect when two corners agree on a radius. Crop chrome hides while a mask is locked. The hole-fill fly-out adds match-surroundings and the same Sample from image eyedropper as the toolbar.
Select starts with Snap armed. A locked clip fills with the page colour around the mask. T cycles surroundings → Sample from image → transparent while the mask is locked; idle T is still Text.
There was a problem hiding this comment.
🟡 Changes recommended
A confirmed logic bug in the click-snap path clears snapped lasso points, causing lasso click-snap to fall back to a rectangle mask.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Pull request overview
This PR adjusts the new Select-based clip-out workflow defaults so it’s immediately optimized for “grab the object” usage: Snap starts enabled, hole fill defaults to match-surroundings when possible, and T cycles fill modes while a mask is locked (without changing global configurability).
Changes:
- Adds a clip-out engine (
ClipOp, masked tile copy, hole fill/punch, snap helpers) and persists clip ops + clip tiles through the operation log/JSON. - Integrates clip-out into
CaptureEditor(shape chips, Snap toggle, mask lock/resize/lift, fill fly-out +Tcycling, snap-trace animation). - Extends smoke coverage and docs to reflect the new default behaviors and keybindings.
File summaries
| File | Description |
|---|---|
| tests/editor-smoke.cpp | Wires new clip smoke suites into the main smoke runner. |
| tests/clip-smoke.hpp | Declares clip-engine smoke entrypoint. |
| tests/clip-smoke.cpp | Adds pixel-level engine tests (copy/punch/fill/mapping/snap/PNG gate). |
| tests/clip-mapping-smoke.hpp | Declares editor clip mapping smoke entrypoint. |
| tests/clip-mapping-smoke.cpp | Adds editor-level interaction tests for locking/lifting/fill cycling/snap defaults. |
| src/editor.hpp | Introduces clip-related editor state, helpers, and test hooks. |
| src/editor.cpp | Implements clip UI/interaction flow, operation replay integration, and snap-trace visuals. |
| src/clip.hpp | Defines clip model + APIs (shapes, path, masked copy, fill, snap helpers). |
| src/clip.cpp | Implements clip-out engine and snap detection algorithms. |
| src/capture.hpp | Adds Annotation::Kind::Clip + Operation::Type::Clip data plumbing. |
| src/capture.cpp | Renders clip layers and serializes/deserializes clip tiles + clip ops in JSON. |
| README.md | Documents clip-out behavior, defaults, and updated keybinding semantics. |
| docs/superpowers/specs/2026-08-28-clip-shape-masks-design.md | Adds design spec describing intended UX/model/serialization. |
| docs/editing-model.md | Updates operation-log model documentation to include Clip ops. |
| CMakeLists.txt | Adds new clip sources and tests to the build. |
| AGENTS.md | Updates repository layout to include the new clip engine files. |
| .gitignore | Ignores .superpowers/ directory. |
Review details
- Files reviewed: 16/18 changed files
- Comments generated: 2
- Review effort level: Lite
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Comment on lines
+5960
to
+5967
| if (matches.isEmpty() && !pixelClipLargeEnough(area) && | ||
| pixelClipSnapEnabled_) { | ||
| trySnapAt(dragStart_); | ||
| pixelClipPoints_.clear(); | ||
| updatePointerCursor(); | ||
| update(); | ||
| return; | ||
| } |
Comment on lines
+278
to
+293
| void paintCheckerboard(QPainter &painter, const QRectF &bounds, qreal cell) { | ||
| painter.fillRect(bounds, QColor(245, 245, 247)); | ||
| painter.setPen(Qt::NoPen); | ||
| painter.setBrush(QColor(170, 172, 178)); | ||
| const int columns = std::max(1, static_cast<int>(std::ceil(bounds.width() / cell))); | ||
| const int rows = std::max(1, static_cast<int>(std::ceil(bounds.height() / cell))); | ||
| for (int row = 0; row < rows; ++row) { | ||
| for (int column = 0; column < columns; ++column) { | ||
| if (((row + column) & 1) == 0) | ||
| continue; | ||
| painter.fillRect(QRectF(bounds.left() + column * cell, | ||
| bounds.top() + row * cell, cell, cell), | ||
| QColor(170, 172, 178)); | ||
| } | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Depends on #111. Unique commit vs clip-out: lukejmorrison/omasnap@feat/select-clip-out...feat/clip-snap-defaults
Until #111 merges, GitHub will show the full clip-out stack in the Files tab. The change this PR adds is the last commit only. Independent of Ctrl+Cut insert-band (#114).
What this changes
On top of clip-out from Select:
Twhile a mask is locked cycles surroundings → Sample from image → transparent. IdleTis still Text.No new config key. Defaults are the product (Omarchy).
Why it is a separate PR
Clip-out in #111 keeps Snap off and transparent holes, matching the first version Copilot reviewed. This is the opinionated default we actually use day to day. Easy to take clip-out without taking the defaults.
Tests
runClipMappingSmokecovers Snap-on and surroundings fill as the starting state, plus theTcycle.make checkis green on the stacked branch.