fix(core): split-into-property-groups and replace-with-keyframes mutations#1355
Conversation
Add PropertyGroupName type system (position/scale/size/rotation/visual/other), PROPERTY_GROUPS constant, classifyPropertyGroup/classifyTweenPropertyGroup functions. Parser generates group-aware animation IDs, resolves position strings (+=, -=, <, >), uses numeric matching with 2% tolerance, and preserves IDs across all mutations.
…mutations Server-side mutations for atomic property-group splitting and keyframe replacement. Client commitMutation returns early on changed:false instead of throwing.
james-russo-rames-d-jusso
left a comment
There was a problem hiding this comment.
Verdict: LGTM with one validation nit. Wire shape correct; new mutations land cleanly.
R2 carry-overs
- RC5 throw flip ✅ —
files.ts:1106—changedboolean drives both write-skip and response payload; client (useGsapScriptCommits.ts:135-147) returns early onresult.changed === falsewithout throwing. Matches the R2 ask. splitIntoPropertyGroupsmutation ✅ —gsapParser.ts:2352-2480emits per-group tweens; downstream IDs flow throughaddAnimationWithKeyframesToScript/addAnimationToScript, then re-suffixed via${baseId}-${propertyGroup}atgsapParser.ts:1047. ID-suffix wire is coherent.replace-with-keyframespurpose — gesture/drag-commit primitive. Consumers atgsapDragCommit.ts:132andgsapRuntimeBridge.ts:394(land in #1357) call it to atomically replace a flat tween with a keyframed version.
Concern (validation)
files.ts:639-648—replace-with-keyframesdoesn't validatebody.keyframesis a non-empty array.addAnimationWithKeyframesToScriptwith an empty list silently no-ops the add, butremoveAnimationFromScriptstill runs → data loss on a malformed client call. Wrap in the same shape check assplit-animations(line 645).
Test coverage
5 cases for splitIntoPropertyGroups in gsapParser.test.ts:2163-2245 (flat-3-group, flat-2-group, no-op single-group, position/duration/ease preservation, keyframed split). Good coverage. No test for the new replace-with-keyframes route or for changed: false short-circuit in commitMutation.
Cross-PR coherence
Emits propertyGroup on every split-output animation via the re-parse path (anim.propertyGroup line 941). #1357 caches it, #1358 reads it.
— Review by Rames D Jusso
james-russo-rames-d-jusso
left a comment
There was a problem hiding this comment.
R3 verification — concerns. Both R2 nits still open.
files.ts:649-660—case "replace-with-keyframes"still callsaddAnimationWithKeyframesToScript(script, ..., body.keyframes, body.ease)with nobody.keyframes.length > 0guard. A request with an empty array silently removes the animation and adds nothing back. Guard at top of case.useGsapScriptCommits.ts:116— R3 introducedthrow new Error(\Mutation failed: ${mutation.type}`)on!result. No toast, no.catch()at the call site. Combined withvoid commitMutation(...)elsewhere this propagates as an unhandled rejection. Either wrap inshowToast(style consistent with #1359'sshowToast("Selection lost during recording", "error")) or attach.catch(showErrorToast)` at call sites.
Review by Rames D Jusso
vanceingalls
left a comment
There was a problem hiding this comment.
Verification pass — R3 items checked against current HEAD (ec57455).
1. Empty-keyframes guard — case
NOT fixed. HEAD still has no guard:
case "replace-with-keyframes": {
const script = removeAnimationFromScript(block.scriptText, body.animationId);
const added = addAnimationWithKeyframesToScript(script, body.targetSelector, body.position, body.duration, body.keyframes, body.ease);
return added.script;
}No body.keyframes.length > 0 check. Empty array → silent remove with no replacement, same as R3.
2. Unhandled throw —
NOT fixed. throw new Error(Mutation failed: ...) is still present at line 138, and there are 10+ void commitMutation(...) call sites (lines 205, 242, 255, 337, 347, 362, 376, 386, 486, 508, 527, 537) with no .catch(). These throw as unhandled rejections.
Both R3 blockers remain open. Not stamp-ready.
— Vai (verification pass)

Summary
split-into-property-groupsserver mutation for atomic property-group splittingreplace-with-keyframesserver mutation for keyframe replacementcommitMutationreturns early onchanged:falseinstead of throwingTest plan
Stack: 2/7 — depends on #1354