Commit fc3ab76
fix(studio,core): persist manual position edits for GSAP-owned elements (#1346)
* feat(sdk): scaffold @hyperframes/sdk — engine layer (model, RFC 6902 patches, mutate, apply-patches)
* fix(sdk): make engine-layer PR self-contained — trim index.ts, guard indexed access
- index.ts no longer exports document/session/history/persist-queue (those
modules land in the next stacked PR); branch now typechecks standalone
- setOwnText: optional-chain children[i] access (TS2532 under
noUncheckedIndexedAccess)
- fallow suppressions for buildPatchEvent + adapters/types.ts — consumers
arrive in #1325
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix(sdk): fail loudly on Phase 3b ops; add sdk to root build pipeline
- applyOp throws UnsupportedOpError (code E_UNSUPPORTED_OP) for the 9
parser-backed ops instead of silently no-opping — callers must never
believe an animation edit succeeded when nothing was mutated
- validateOp returns false for Phase 3b ops so can() feature-detects
- root package.json build filter now includes @hyperframes/sdk (package is
dist-only; top-level build previously produced no SDK artifacts).
publish.yml intentionally NOT updated — sdk stays unpublished until
Phase 3 completes.
Adversarial-review findings F3 + F4.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix(sdk): cross-realm origin sentinel, dual width/height channel, contract docs
Round-2 review (Rames/Miguel) on the engine layer:
- ORIGIN_APPLY_PATCHES: unique symbol → namespaced string
('@hyperframes/sdk:applyPatches'). Symbols are realm-local — they don't
survive postMessage/structured-clone, which T3 embedded hosts may forward
patch events across. Namespaced string keeps collision risk negligible.
- setCompositionMetadata width/height: runtime treats data-width/data-height
as a forced override of inline style (init.ts applyCompositionSizing).
Style is always written; the data-* attr is updated when already present
so the edit isn't clobbered on load. Absent attrs stay absent — inverses
stay exact. Mirrored in the patch applier; 3 new tests.
- JsonPatchOp documented as the emit-only RFC 6902 subset
(add/remove/replace); applier header notes move/copy/test are ignored.
- SdkDocument.html documented as a build-time snapshot (serialize() is the
live state).
- patches.ts path-grammar comment fixed: timing/{start|end|trackIndex}.
NOT changed (with reasons, see PR reply): moveElement left/top matches
Studio's own inline-style commit convention (sourcePatcher); package version
follows the repo-wide single-version policy.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix(sdk): moveElement writes data-x/data-y, not left/top CSS
HF elements use data-x/data-y for positioning (read by htmlParser.ts,
emitted by hyperframes generator). CSS left/top is not the runtime convention.
Adds inverse round-trip test for prior position restore.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* chore: update bun.lock after sdk package registration
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat(sdk): session API, optional history + persist-queue, adapters — Phase 3a complete
* fix(sdk): address review — live-DOM query cache, single parse, style parse dedup
- getElements/getElement/find now walk the live linkedom DOM via buildRoots
with a lazily-built cache invalidated on dispatch/applyPatches — no
serialize→ensureHfIds→parseHTML round trip per query
- openComposition parses once (parseMutable); dropped discarded _doc
constructor param and the redundant buildDocument call
- document.ts buildElement reuses model.ts getElementStyles — removes
duplicated parseInlineStyles (also fixes custom-prop camelCase mangling)
- JSDoc note: empty batch() still fires change handlers
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix(sdk): restore full public exports now session/document modules exist
index.ts re-exports document/session/history/persist-queue (trimmed in the
engine-layer PR to keep it self-contained); drops the temporary fallow
suppressions whose consumers now exist.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix(sdk): coalesce history by patch paths; replay override-set on open
Adversarial-review findings F1 + F2:
- history: coalescing now requires identical patch paths in addition to
op types + origin + window. Previously two rapid setStyle calls on
DIFFERENT elements merged into one entry carrying the second forward +
first inverse — undo then reverted the wrong element and stranded the
latest edit. Slider drags on one property still coalesce.
- T3 init: openComposition({ overrides }) now replays the stored
override-set onto the freshly-parsed base before exposing the session
(new keyToPath inverse mapping + applyOverrideSet). Previously the
overrides were copied into the map but never applied — reopening an
embedded composition showed and serialized the base template.
- examples: GSAP calls now feature-detect with can() (Phase 3b ops throw
UnsupportedOpError as of the engine-layer fix); UnsupportedOpError
re-exported from the package entry.
- 8 new session tests: coalesce same-path / cross-element / cross-prop,
override round-trip (style/text/attr/timing/removal/restore-base).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix(sdk): transactional batch rollback, sorted coalesce key, root-priority unify
Round-2 review (Rames/Miguel) on the session layer:
- batch() is now transactional: on throw, accumulated inverse patches are
replayed in reverse and the override-set snapshot restored — the model is
exactly as it was at batch entry. Previously a throwing batch left the DOM
partially mutated with no patch trail, no history entry, no recovery path.
2 new tests (model unchanged + undo is no-op after throwing batch).
- history coalesce key sorts opTypes — same op-type set coalesces regardless
of dispatch order within a batch.
- applyPatches comment documents that emitted PatchEvents carry an empty
inversePatches array (hosts keep their own inverse log).
- document.ts extractDimensions/extractDuration now use the engine's
findRoot — dimension extraction and mutations agree on the root element
([data-hf-root] > #stage > first child). Dimensions prefer the runtime's
data-width/data-height forced-override attrs, falling back to inline style.
- ownText documented: snapshot .text is trimmed display text; setText writes
verbatim.
Deferred to follow-up (acknowledged, not ship-blocking): persist-queue flush
error surfacing, debounce window, path default, history ring-buffer.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* feat(lint): add gsap_studio_edit_blocked rule for manual timeline + GSAP element targeting
* fix(studio,core): persist manual position edits for GSAP-owned elements
- sourceMutation: linkedom CSSStyleDeclaration silently drops CSS custom
properties and transform longhands via setProperty; patch the style
attribute string directly so --hf-studio-offset-* and translate survive
the server round-trip (positions never reached disk before this)
- gsapAnimatesTransform(): GSAP owns the full transform stack when it tweens
ANY transform prop (scale, rotation, ...), not just x/y — it folds CSS
translate into its cache once at init, zeroes the longhand once, and never
re-reads it
- applyStudioPathOffset: for GSAP-owned elements keep translate:none live and
sync the offset into GSAP's cache via gsap.set; writing the longhand
double-applied the offset (disappearing elements, scrub snap-back)
- buildPathOffsetPatches: emit the var() translate expression explicitly so
the persisted file re-folds on reload (live inline is none)
- StudioPathOffsetSnapshot: capture/restore GSAP x/y — the drag-response
probe mutates GSAP's cache, which inline-style restore cannot undo (click
made elements jump by the probe distance)
- reapplyPathOffsets: skip GSAP-owned elements (was x/y-only) to stop
seek-time double-apply
- STUDIO_GSAP_DRAG_INTERCEPT flag (default off): keyframe drag intercept is
opt-in until its recording path is hardened; commits take the CSS persist
path
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix(studio): remove duplicate flag declaration, trim useDomEditCommits to 600 lines
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>1 parent 511665b commit fc3ab76
12 files changed
Lines changed: 182 additions & 23 deletions
File tree
- packages
- core/src/studio-api/helpers
- studio/src
- components/editor
- contexts
- hooks
Lines changed: 1 addition & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
1 | 2 | | |
2 | 3 | | |
3 | 4 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
223 | 223 | | |
224 | 224 | | |
225 | 225 | | |
| 226 | + | |
| 227 | + | |
| 228 | + | |
| 229 | + | |
| 230 | + | |
| 231 | + | |
| 232 | + | |
| 233 | + | |
| 234 | + | |
| 235 | + | |
| 236 | + | |
| 237 | + | |
| 238 | + | |
| 239 | + | |
| 240 | + | |
| 241 | + | |
| 242 | + | |
| 243 | + | |
| 244 | + | |
| 245 | + | |
| 246 | + | |
| 247 | + | |
| 248 | + | |
| 249 | + | |
| 250 | + | |
| 251 | + | |
| 252 | + | |
226 | 253 | | |
227 | 254 | | |
228 | 255 | | |
| |||
236 | 263 | | |
237 | 264 | | |
238 | 265 | | |
239 | | - | |
240 | | - | |
241 | | - | |
242 | | - | |
| 266 | + | |
| 267 | + | |
| 268 | + | |
| 269 | + | |
| 270 | + | |
| 271 | + | |
| 272 | + | |
| 273 | + | |
243 | 274 | | |
244 | 275 | | |
245 | 276 | | |
| |||
Lines changed: 30 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
1 | 30 | | |
2 | 31 | | |
3 | 32 | | |
4 | 33 | | |
| 34 | + | |
5 | 35 | | |
6 | 36 | | |
7 | 37 | | |
| |||
Lines changed: 10 additions & 6 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
47 | 47 | | |
48 | 48 | | |
49 | 49 | | |
50 | | - | |
51 | | - | |
52 | | - | |
53 | | - | |
54 | | - | |
55 | | - | |
56 | 50 | | |
57 | 51 | | |
58 | 52 | | |
| |||
89 | 83 | | |
90 | 84 | | |
91 | 85 | | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
92 | 96 | | |
93 | 97 | | |
94 | 98 | | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
32 | 32 | | |
33 | 33 | | |
34 | 34 | | |
35 | | - | |
| 35 | + | |
36 | 36 | | |
37 | 37 | | |
38 | 38 | | |
| |||
223 | 223 | | |
224 | 224 | | |
225 | 225 | | |
| 226 | + | |
226 | 227 | | |
227 | 228 | | |
228 | 229 | | |
| |||
257 | 258 | | |
258 | 259 | | |
259 | 260 | | |
| 261 | + | |
| 262 | + | |
| 263 | + | |
| 264 | + | |
| 265 | + | |
| 266 | + | |
| 267 | + | |
| 268 | + | |
| 269 | + | |
| 270 | + | |
| 271 | + | |
| 272 | + | |
260 | 273 | | |
261 | 274 | | |
262 | 275 | | |
| |||
268 | 281 | | |
269 | 282 | | |
270 | 283 | | |
| 284 | + | |
| 285 | + | |
| 286 | + | |
| 287 | + | |
| 288 | + | |
| 289 | + | |
| 290 | + | |
| 291 | + | |
| 292 | + | |
| 293 | + | |
| 294 | + | |
| 295 | + | |
| 296 | + | |
| 297 | + | |
| 298 | + | |
| 299 | + | |
| 300 | + | |
| 301 | + | |
271 | 302 | | |
272 | 303 | | |
273 | 304 | | |
274 | 305 | | |
275 | 306 | | |
276 | 307 | | |
277 | 308 | | |
278 | | - | |
| 309 | + | |
279 | 310 | | |
280 | | - | |
281 | | - | |
282 | | - | |
| 311 | + | |
| 312 | + | |
| 313 | + | |
283 | 314 | | |
284 | 315 | | |
285 | 316 | | |
| |||
520 | 551 | | |
521 | 552 | | |
522 | 553 | | |
523 | | - | |
| 554 | + | |
| 555 | + | |
| 556 | + | |
| 557 | + | |
| 558 | + | |
| 559 | + | |
524 | 560 | | |
525 | 561 | | |
526 | | - | |
527 | 562 | | |
528 | 563 | | |
529 | 564 | | |
| |||
Lines changed: 1 addition & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
1 | 2 | | |
2 | 3 | | |
3 | 4 | | |
| |||
Lines changed: 17 additions & 1 deletion
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
72 | 72 | | |
73 | 73 | | |
74 | 74 | | |
75 | | - | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
76 | 92 | | |
77 | 93 | | |
78 | 94 | | |
| |||
Lines changed: 26 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
4 | 4 | | |
5 | 5 | | |
6 | 6 | | |
| 7 | + | |
7 | 8 | | |
8 | 9 | | |
9 | 10 | | |
| |||
87 | 88 | | |
88 | 89 | | |
89 | 90 | | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
90 | 98 | | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
91 | 108 | | |
92 | 109 | | |
93 | 110 | | |
94 | 111 | | |
95 | 112 | | |
96 | 113 | | |
97 | 114 | | |
| 115 | + | |
| 116 | + | |
98 | 117 | | |
99 | 118 | | |
100 | 119 | | |
| |||
183 | 202 | | |
184 | 203 | | |
185 | 204 | | |
| 205 | + | |
| 206 | + | |
| 207 | + | |
| 208 | + | |
| 209 | + | |
| 210 | + | |
| 211 | + | |
186 | 212 | | |
187 | 213 | | |
188 | 214 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
101 | 101 | | |
102 | 102 | | |
103 | 103 | | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
104 | 112 | | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
1 | 2 | | |
2 | 3 | | |
3 | 4 | | |
| |||
0 commit comments