Let a progress ring be resized, gapped and given a bitmap - #25
Merged
Conversation
A ring created in the editor is procedural (0x5a, no frames), so it fell through every path that assumes pixels: no w/h in the inspector, no corner handles, and nothing to click to give it art. Its geometry is the arc spec, so edit that instead — size (radius + meta.w/h, growing around its own centre), stroke, and the start/end sweep, which is what leaves a gap for an icon to sit in. The gauge range moves to the source section, where the metric it reads already lives. Corner drags and arrow keys route to ringResized for a ring with no art: one factor, applied to radius and stroke together so the drop lands where the preview's ctx.scale showed it. A ring with art had the opposite bug: resizing rescaled the bitmap but left meta.w/h alone, and that is the circle drawSector pivots the filled sector around — so the arc tore as soon as the ring was resized. It now follows the same factors. Rounded caps stay out: the arc spec is 17/19 fixed bytes with only a 3-byte trailer undecoded, that trailer disagrees between visually identical sibling rings, and all four firmware-drawn partial-sweep rings in the corpus bake flat radial ends. See github.com//issues/22. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QLHPEnYV5UMkne4WBTCk5s
Pre-existing on main — the button's attributes sit on one over-long line and `oxfmt --check` has been failing on it, which is why CI is red here too. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QLHPEnYV5UMkne4WBTCk5s
Review of the ring work turned up ten defects, most of them the same mistake in different places: the rule for what a ring's size IS was written out three times. It now lives once, in edits.ts's scaleRing, and the standalone resize, the group resize and the inspector's size field all go through it. The bugs that rule fixes: * meta.w/h compounded. resizeImageFx scaled it by the factors rescaleFrames reports, but those are measured against the PINNED ORIGINAL bitmap, so the second resize multiplied an already-scaled meta by a total factor — halving a 284px ring twice landed its circle on 36 instead of 71, and the sector tore around a centre the art no longer had. The meta factor is now taken against the asset's current size. * A circle can't scale per axis. ringResized collapsed the drag's two factors with max(), which the corner-drag anchor was never computed for, and which froze every shrink (max(0.9, 1) === 1) — arrow-key shrink did nothing at all. The canvas now scales a bare ring uniformly, like a shift-drag, and the model reads the axis that actually moved. * Nothing capped the radius, so a drag could cross drawProceduralArc's full-bleed threshold, which draws from the screen centre and ignores x/y — the ring teleported mid-drag and lost its position. That threshold is now a named constant the editor stays below. * resizeGroupFx never applied any of this, so a ring inside a resized group tore exactly the way a standalone one used to, and a bare one didn't resize. * addRingImageFx left spec.radius at its old value, exporting a diameter the widget no longer had, and its failData reached no error store — an undecodable file failed silently. * A resize that rounded to no change still rebuilt the document, eating an undo. * meta.auto (0x8000, a group's "size me from my content" sentinel) was overwritten as if it were a diameter. Also: the size field fires ringResized instead of rebuilding the layer in the view, so typing a size and dragging to it now agree (stroke scales either way), and the ring-art picker uses the shared Button over a hidden input, like the tree panel, instead of its own copy of the button chrome. Four regression tests, each checked to fail against the old code. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QLHPEnYV5UMkne4WBTCk5s
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.
Closes the resize/replace half of #22, and answers the rounded-corner half with corpus evidence.
What was broken
A ring the editor creates is procedural —
0x5a, no frames — and every sizing path assumes pixels:geometry.sveltereads w/h off the first frame, so a bare ring showed no size fields at all;hasArt()gated the canvas corner handles, so it couldn't be dragged either;Separately, a ring with art resized its bitmap but not
meta.w/h— and that is the circledrawSectorpivots the filled sector around, so the arc tore the moment the ring was resized.What's here
size(bare rings:spec.radius+meta.w/h, growing around its own centre),stroke,start/end, andgauge min–maxin the source section next to the metric it reads.start/endare what Can not resize Progress Ring #22's second point asks for:20 → 340leaves a 40° notch atthe top for an icon, and the fill still spans the whole sweep.
ringResized: one factor onradius and stroke together, so the drop lands where the preview's
ctx.scaleshowed it.ringImageAddedgives a bare ring a bitmap; the tag stays0x5a(the corpus has0x5arings with an image), andmeta.w/htake the bitmap's size.meta.w/hnow scale with its art on resize.x/y,w/h,alignand thepivot row sized to their text, so every row started its inputs somewhere different.
Rounded caps — not possible, and I checked properly
100 corpus faces, 79 arc widgets. Every one is
0x80/0x81with exactly two children (struct +spec) — no style node exists. The spec is a fixed 19/17 bytes with everything decoded but a 3-byte
trailer, and that trailer disagrees between visually identical sibling rings in 8 faces. 17 rings
carry no bitmap (so firmware strokes them); the 4 with a partial sweep show both ends in their baked
preview, at three different trailer values — all flat radial caps. Details in
#22 (comment).
Verification
pnpm check0 errors,pnpm lintclean, 164 tests pass including three new ones intests/editor-ring.browser.test.ts(spec-driven resize,meta.w/hfollowing the art, giving a barering a bitmap). Also driven by hand in a real browser: add ring → drag a corner (200 → 260, stroke
10 → 13) → gap at 20/340 → drop a ring PNG on it.
🤖 Generated with Claude Code
https://claude.ai/code/session_01QLHPEnYV5UMkne4WBTCk5s