mirror of
https://github.com/penpot/penpot.git
synced 2026-10-29 14:00:37 +00:00
Decision (f) of the component contract, with a fix it needed and a check of decision (g) (Phase 3c). - Return empty modifiers for a scale constraint axis. The constraint of each axis of a child gives the modifiers that the parent's resize adds to the child; :scale has no method of its own and took the default, which returned an empty vector. Adding a vector to modifiers reads its :last-order as nil: JavaScript adds nil as 0, the JVM throws, so a child scaled on one axis and constrained on the other (scale and top, say) could not be resized on the JVM. The default now returns empty modifiers. geom-shapes-constraints-test checks the default is empty and adds to modifiers. - Keep the children of a transformed copy shape derived. Decision (f) of the component contract: transforming a copy shape touches that shape only; its children change as a consequence and stay untouched, and sync gives them their expected geometry, their main's placed against their parent and resized through their constraints. Touch. The placement step decided per shape whether a transform touched it by comparing it with its copy root, so the children of a resized root that stretched (scale, left and right) were touched (F10), a derived width or height always touched (F22), and a resize that scales the content touched the strokes, radii and the like of every child (F39). The editor now tells the placement step which shapes the user transformed (the keys of the modifiers given, or those the stored modifiers were set for); any other shape of the transform changes only as a consequence of theirs (a child through its constraints, a board that hugs them) and touches nothing. Sync. Sync placed every main shape by the offset between the roots only, so the children of a resized copy root that a resize leaves untouched (center) went back to their main's place at the next sync of the main (F10). Now, before it walks the children of a copy shape that keeps its own geometry, sync resizes, turns and moves the main shape's subtree to match it, its children following through their constraints and layouts, and walks those: each child gets that geometry unless it has its own. comp-expected-geometry-test: a copy root resized as the editor does places a child constrained left, right, center, scale and left-right, leaves it untouched and keeps it there through a sync; a resize that scales the content scales the child's stroke and touches only the root. The frontend golden master records, in cases AI and the touched child under a main rotation, the children the resize or rotation of a root no longer touches. - Check a copy child resize leaves its hug board untouched. Decision (g) of the component contract: the size of a board that hugs its content is the layout's, never an override. The layout reflow writes it as derived, and a transform touches only the shapes the user transformed (decision (f)), so a resize of a copy child that grows the board it sits in touches the child only. workspace-layout-owned-sizes-test pins it. Benchmark (lower quantile, ms; baseline / now / ratio); the machine was loaded, and the code before this change, run in between, gave the same spread (0.44 to 0.59): deepest-edit-through-three-levels 35.2 / 16.7 / 0.47 edit-through-a-chain-of-dependents 68.9 / 33.4 / 0.48 library-update-into-copies 83.1 / 40.3 / 0.49 main-edit-among-many-shapes 4.8 / 2.4 / 0.49 main-edit-into-10-copies 1.8 / 1.0 / 0.54 main-edit-into-100-copies 15.4 / 8.8 / 0.57 main-edit-into-1000-copies 153.1 / 92.7 / 0.61 variant-switch-into-copies 32.1 / 19.6 / 0.61 AI-assisted-by: claude-opus-5-5