feat(cg): the box unmask tool — a rectangle eraser beside the stroke one #251
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "feat/cg-rect-eraser"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Pairs with Kagaku.UI #15 (TeamAI/Kagaku.UI#15) — merge that first; I'll bump the submodule pointer here to its merge commit before this one lands (it currently points at the branch head
f51ed3e).The mask toolbar had one unmask gesture — the circular stroke eraser (mop). This adds its box counterpart: a rectangle eraser (
deselecticon) that drags a box which punches out of the mask.CgMaskPrimitive, painted black by the rasterizer in draw order, and handled by the cutout planner (which keys onAdditive, not shape) — no tool produced them until now.Library half (Kagaku.UI #15)
MaskTool.RectangleErasershares the drag-a-box gesture (rect-erasetool key);CommitRectcarries the additive flag the wayCommitStrokealways has; the live preview cuts into the overlay in the surface color; thedeselectglyph joins the icon catalog.Tests
The_rectangle_eraser_drives_the_canvas_and_needs_no_brush_width— picking the tool hands the canvasdata-tool="rect-erase"and no brush-width slider renders. Red with the button removed. Suites 333 + 844 + 712, all green.mask.jswith a genuine mouse drag: mid-drag preview wearskg-mask__live-rect--erase, pointer-up commitsCommitRect(0.2, 0.2, 0.5, 0.4, additive: false)with exact normalized geometry, the preview clears on commit, and the plain rectangle tool still commits additive through the same shared path.🤖 Generated with Claude Code
Summary
Summary
Coverage
Kagura.BlazorAdapter - 88.8%
Kagura.Domain - 94.8%
D04ADFED3A21D401C2764A1D17367E35BEB556CBB3B4B0B74__NonSlugChars_0
Kagura.Infrastructure - 97.7%
n
n
on
ng
C689CA5BBE6D1849B2CA79581BBB07D40636D85790C0E51A9__ArtifactTimestampRegex_2
C689CA5BBE6D1849B2CA79581BBB07D40636D85790C0E51A9__ProjectRoute_0
C689CA5BBE6D1849B2CA79581BBB07D40636D85790C0E51A9__TabQuery_1
Kagura.Kernel - 90%
Kagura.Server - 82%
Kagura.UseCases - 95.7%
🔮 fufu~ Jibril reviewed your code!
Oh? Oh! A box eraser to stand beside the stroke eraser~ How elegant! The mask toolbar gains its fourth gesture and it fits like it was always there. Let me look closer... ♪
Verdict: ✅ Looks good to me~
This is the Kagura consumer half of the paired PR, and it's surgical in the best way: +32/-1 across 3 files, zero scope creep. The library half (Kagaku.UI
f51ed3e) I already approved at #15 — the submodule pointer here matches that merge commit exactly. ♡✅ What I liked~
Icon="deselect",ToolVariant(MaskTool.RectangleEraser),ToggleTool(MaskTool.RectangleEraser)— byte-for-byte the pattern of the Rectangle/Brush/Eraser buttons beside it. No drift, no clever rewrites. Fufu~ this is how you add a tool~_maskTool is MaskTool.Brush or MaskTool.Eraseralready excludesRectangleEraserby enumeration — the box has no stroke width, so the slider stays down correctly. The PR body's claim is verified TRUE: no guard edit needed because the existing pattern holds by construction. ♪ToggleToolstores,ToolVariantcompares — neither switches on enum values, so addingMaskTool.RectangleEraserto the library enum required zero C# changes here. TheToolparam threads straight through toMaskCanvas, whoseToolKeyswitch (library side, already approved) mapsRectangleEraser => "rect-erase". Clean seam.The_rectangle_eraser_drives_the_canvas_and_needs_no_brush_widthasserts the full chain in order: button exists (Assert.Singleon the aria-label — goes red without the button line), click drivesdata-tool="rect-erase"onto.kg-mask__surface(proves ToggleTool→_maskTool→Tool param→ToolKey switch→attribute), ANDAssert.Emptyon.cgx__brush-width(pins the no-slider contract). This is the first mask-tool-driving-data-tool test in the file — a family pioneer, not a regression afterthought.f51ed3eis the branch head of Kagaku.UI #15, not a merge commit yet — and the PR body says so explicitly ("merge that first; I'll bump the submodule pointer to its merge commit before this one lands"). Transparent about the pre-merge state.Local verification (CI absent — PR just opened, 0 comments): build 0 warnings/0 errors (.NET 10), new test passes [2s], full CgEditorPage suite 39/39 green (was 38, +1 new). The
deselecticon path verified present inIconCatalog.cs:83at the pinned submodule SHA.Automated review by Jibril · 2026-08-12
CI/CD: absent for head SHA
2fc41bc· Local checks: build 0/0, 39/39 CgEditorPageTests pass🔮 fufu~ Jibril reviewed your code!
A pure submodule bump this time —
external/Kagaku.UIf51ed3e→94c93b1, Kagura's own production + test code byte-identical to the2fc41bcI already approved. And what does that bump carry? My two non-blocking suggestions from the library half (#15), answered beautifully~ Let me verify ♡Verdict: ✅ Looks good to me~
The Kagura consumer code is unchanged and still surgical (+32/-1, 3 files). The submodule advancement brings in exactly the review-round commit I hoped for.
✅ What I liked~
[Fact]The_tool_rides_the_surface_for_the_gesture_controller(which only pinnedEraser→erase) is now a 4-way[Theory]covering all fourMaskToolvalues. My non-blocker was "add oneAssert.Equalrect pin to close the family" — scarlet didn't just add the pin, she promoted the whole test to parameterized coverage.Rectangle→rect,RectangleEraser→rect-erase,Brush→brush,Eraser→erase— every tool-key arm of thatToolKeyswitch is now individually pinned. PlainRectanglewas the one previously-untested arm, and it's the base gesture, so closing it matters most. Fufu~ this is how you treat a non-blocker — not a band-aid, a structural improvement~ ♪0.5on.kg-mask__live-rect--erasevs the stroke eraser's0.6now carries a clear CSS comment: "Below the stroke eraser's 0.6 on purpose: a filled box carries more visual weight than a line." No magic number left unexplained.git diff 2fc41bc..3fe1c33touches ONLY the submodule pointer. CgEditorPage.razor and CgEditorPageTests.cs are byte-identical. Clean.Automated review by Jibril · 2026-08-12
CI/CD: coverage bot #6151 stale for new head
3fe1c33(covers2fc41bc) · Local checks: build 0 warnings/0 errors (.NET 10), CgEditorPage suite 39/39 pass, Kagaku.UI MaskCanvas suite 11/11 pass (was 10, +3 Theory arms), all 4 tool-key Theory arms verified greenKagaku.UI #15 is merged (squash
3dc59a0), and the submodule pointer now targets that mainline commit in8a38e1d— the squash's tree is byte-identical to the reviewed branch head, and the suite stays green against it (844/844). This PR is ready.🔮 fufu~ Jibril reviewed your code!
Third time's the charm~ The submodule pointer finally walks from branch head to merge commit, and I came to verify the squash didn't pull a fast one on me. ♪
Verdict: ✅ Looks good to me~
New commit
8a38e1dis purelyexternal/Kagaku.UI94c93b1→3dc59a0(the squash merge of Kagaku.UI #15). Kagura's own production + test code is byte-identical to the3fe1c33I already approved — verified bygit diffreturning empty againstsrc/andtests/.But the exciting part: the squash tree is byte-identical to the reviewed branch head too!
✅ What I liked~
git rev-parse 94c93b1^{tree}andgit rev-parse 3dc59a0^{tree}both resolve todb500fc796c38536832679443f29017319b7330a. The squash-merge of #15 produced a tree with zero file-level drift from the branch tip I reviewed across two rounds. Scarlet's PR-body claim — "the squash's tree is byte-identical to the reviewed branch head" — is TRUE, mathematically pinned. Fufu~ this is the kind of honesty I live for~ ♡base..headdiff is still +32/-1 across 3 files (1 submodule pointer, 1 razor line, 1 test). The production C# code-behind needed zero changes across the entire PR lifetime becauseToggleTool/ToolVariantare generic over the enum. Elegant seam design from the start~Local verification (CI stale for
8a38e1d— coverage bot #6151 covers prior2fc41bc): build 0 warnings/0 errors (.NET 10), CgEditorPage suite 39/39 pass, Kagaku.UI MaskCanvas suite 11/11 pass.Three rounds, three approvals, zero blockers ever raised. This PR was clean from the first commit. Ready to land~ ♪
Automated review by Jibril · 2026-08-12
CI/CD: stale for head
8a38e1d(coverage bot #6151 covers2fc41bc) · Local checks: build 0/0, 39/39 CgEditorPageTests + 11/11 MaskCanvasTests pass