@hyperframes/studio 0.8.194 → 0.8.196

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,4 +1,4 @@
1
- import{n as Qi}from"./index-CAQ7OcNJ.js";/*!
1
+ import{n as Qi}from"./index-BS2__aBb.js";/*!
2
2
  * Copyright (c) 2026-present, Vanilagy and contributors
3
3
  *
4
4
  * This Source Code Form is subject to the terms of the Mozilla Public
package/dist/index.html CHANGED
@@ -14,7 +14,7 @@
14
14
  document.documentElement.dataset.theme = "paper";
15
15
  }
16
16
  </script>
17
- <script type="module" crossorigin src="/assets/index-CAQ7OcNJ.js"></script>
17
+ <script type="module" crossorigin src="/assets/index-BS2__aBb.js"></script>
18
18
  <link rel="stylesheet" crossorigin href="/assets/index-CYbvOVp-.css">
19
19
  </head>
20
20
  <body>
package/dist/index.js CHANGED
@@ -43356,6 +43356,7 @@ import { useCallback as useCallback61 } from "react";
43356
43356
 
43357
43357
  // src/components/editor/useLayerRevealOverride.ts
43358
43358
  import { useCallback as useCallback60, useEffect as useEffect56, useRef as useRef73 } from "react";
43359
+ import { revealClip } from "@hyperframes/core/runtime/clip-reveal";
43359
43360
  var LAYER_REVEAL_LIFT_Z = "2147483000";
43360
43361
  var LAYER_REVEAL_PENDING_COMMIT_ATTR = "data-hf-studio-reveal-pending-commit";
43361
43362
  var revealCommitSequence = 0;
@@ -43436,9 +43437,10 @@ function rollbackLayerRevealCommit(element, ownership) {
43436
43437
  }
43437
43438
  }
43438
43439
  function restoreIfOurs(el, property, applied, prior) {
43439
- if (applied == null) return;
43440
- if (el.style.getPropertyValue(property) !== applied) return;
43440
+ if (applied == null) return false;
43441
+ if (el.style.getPropertyValue(property) !== applied) return false;
43441
43442
  restoreInline(el, property, prior);
43443
+ return true;
43442
43444
  }
43443
43445
  function readHideSignals(el, win) {
43444
43446
  const computed = win.getComputedStyle(el);
@@ -43456,20 +43458,21 @@ function revealNode(el, win, needs) {
43456
43458
  opacity: el.style.opacity
43457
43459
  };
43458
43460
  const applied = {};
43461
+ let putMarkBack;
43459
43462
  if (needs.display) {
43460
43463
  el.style.removeProperty("display");
43461
43464
  if (win.getComputedStyle(el).display === "none") el.style.display = "block";
43462
43465
  applied.display = el.style.display;
43463
43466
  }
43464
43467
  if (needs.visibility) {
43465
- el.style.visibility = "visible";
43468
+ putMarkBack = revealClip(el);
43466
43469
  applied.visibility = "visible";
43467
43470
  }
43468
43471
  if (needs.opacity) {
43469
43472
  el.style.opacity = "1";
43470
43473
  applied.opacity = "1";
43471
43474
  }
43472
- return { element: el, priors, applied };
43475
+ return { element: el, priors, applied, putMarkBack };
43473
43476
  }
43474
43477
  function revealHiddenChain(element) {
43475
43478
  const doc = element.ownerDocument;
@@ -43538,10 +43541,11 @@ function useLayerRevealOverride({
43538
43541
  const state = stateRef.current;
43539
43542
  stateRef.current = null;
43540
43543
  if (!state) return;
43541
- for (const { element, priors, applied } of state.nodes) {
43544
+ for (const { element, priors, applied, putMarkBack } of state.nodes) {
43542
43545
  if (!element.isConnected) continue;
43543
43546
  restoreIfOurs(element, "display", applied.display, priors.display);
43544
- restoreIfOurs(element, "visibility", applied.visibility, priors.visibility);
43547
+ if (restoreIfOurs(element, "visibility", applied.visibility, priors.visibility))
43548
+ putMarkBack?.();
43545
43549
  restoreIfOurs(element, "opacity", applied.opacity, priors.opacity);
43546
43550
  }
43547
43551
  if (state.lift && state.base.isConnected) restoreLiftedElement(state.base, state.lift);
@@ -90886,6 +90890,7 @@ var StudioHeader = memo46(function StudioHeader2({
90886
90890
  import { useState as useState136, useCallback as useCallback164, useRef as useRef169, useEffect as useEffect136 } from "react";
90887
90891
 
90888
90892
  // src/hooks/useGestureRecording.ts
90893
+ import { revealClip as revealClip2 } from "@hyperframes/core/runtime/clip-reveal";
90889
90894
  import { useCallback as useCallback163, useEffect as useEffect135, useRef as useRef168, useState as useState135 } from "react";
90890
90895
  function readBasePosition(element, iframeEl) {
90891
90896
  let baseOpacity = 1;
@@ -90926,7 +90931,8 @@ function connectGsapRuntime(element, iframeEl, elementEndTime) {
90926
90931
  startTime: win.__player?.getTime() ?? 0,
90927
90932
  maxSeekTime: elementEndTime != null && elementEndTime < tlDuration ? elementEndTime : tlDuration,
90928
90933
  savedVisibility: element.style.visibility,
90929
- savedTranslate: element.style.getPropertyValue("translate")
90934
+ savedTranslate: element.style.getPropertyValue("translate"),
90935
+ putMarkBack: null
90930
90936
  };
90931
90937
  }
90932
90938
  } catch {
@@ -90938,7 +90944,8 @@ function applyRuntimePreview(runtime, time, properties) {
90938
90944
  runtime.timeline.seek(seekTime);
90939
90945
  runtime.element.style.setProperty("translate", "none");
90940
90946
  runtime.gsap.set(runtime.element, { ...properties });
90941
- runtime.element.style.visibility = "visible";
90947
+ const putMarkBack = revealClip2(runtime.element);
90948
+ runtime.putMarkBack ??= putMarkBack;
90942
90949
  liveTime.notify(seekTime);
90943
90950
  usePlayerStore.getState().setCurrentTime(seekTime);
90944
90951
  }
@@ -91014,6 +91021,7 @@ function releaseRuntimePreview(r) {
91014
91021
  if (!runtime) return;
91015
91022
  const { element, savedVisibility, savedTranslate } = runtime;
91016
91023
  element.style.visibility = savedVisibility;
91024
+ runtime.putMarkBack?.();
91017
91025
  element.style.setProperty("translate", savedTranslate || "");
91018
91026
  try {
91019
91027
  runtime.gsap.set(runtime.element, {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hyperframes/studio",
3
- "version": "0.8.194",
3
+ "version": "0.8.196",
4
4
  "description": "",
5
5
  "repository": {
6
6
  "type": "git",
@@ -60,12 +60,12 @@
60
60
  "dockview-react": "8.3.1",
61
61
  "gsap": "^3.13.0",
62
62
  "mediabunny": "^1.45.3",
63
- "@hyperframes/parsers": "0.8.194",
64
- "@hyperframes/core": "0.8.194",
65
- "@hyperframes/sdk": "0.8.194",
66
- "@hyperframes/shader-transitions": "0.8.194",
67
- "@hyperframes/player": "0.8.194",
68
- "@hyperframes/studio-server": "0.8.194"
63
+ "@hyperframes/sdk": "0.8.196",
64
+ "@hyperframes/parsers": "0.8.196",
65
+ "@hyperframes/core": "0.8.196",
66
+ "@hyperframes/studio-server": "0.8.196",
67
+ "@hyperframes/player": "0.8.196",
68
+ "@hyperframes/shader-transitions": "0.8.196"
69
69
  },
70
70
  "devDependencies": {
71
71
  "@tailwindcss/vite": "^4.3.3",
@@ -73,7 +73,7 @@
73
73
  "@types/react-dom": "19",
74
74
  "@vitejs/plugin-react": "^4.0.0",
75
75
  "chokidar": "^4.0.3",
76
- "esbuild": "^0.25.12",
76
+ "esbuild": "^0.28.2",
77
77
  "fake-indexeddb": "^6.2.5",
78
78
  "puppeteer-core": "^25.2.1",
79
79
  "tailwindcss": "^4.3.3",
@@ -82,7 +82,7 @@
82
82
  "vite": "^6.4.2",
83
83
  "vitest": "^4.1.11",
84
84
  "zustand": "^5.0.0",
85
- "@hyperframes/producer": "0.8.194"
85
+ "@hyperframes/producer": "0.8.196"
86
86
  },
87
87
  "peerDependencies": {
88
88
  "react": "19",
@@ -18,6 +18,7 @@ import {
18
18
  readTimelineElementZIndex,
19
19
  } from "../../player/lib/timelineElementHelpers";
20
20
  import { installReactActEnvironment } from "../../hooks/domSelectionTestHarness";
21
+ import { CLIP_HIDDEN_ATTR } from "@hyperframes/core/runtime/clip-reveal";
21
22
 
22
23
  installReactActEnvironment();
23
24
 
@@ -149,6 +150,33 @@ describe("useLayerRevealOverride — delayed reveal ownership", () => {
149
150
  act(() => root.unmount());
150
151
  });
151
152
 
153
+ it("lifts the runtime's hidden-clip mark with the visibility it forces, and puts both back", () => {
154
+ const host = document.createElement("div");
155
+ const clip = makeEl("2", "absolute");
156
+ clip.style.visibility = "hidden";
157
+ clip.setAttribute(CLIP_HIDDEN_ATTR, "");
158
+ document.body.appendChild(host);
159
+ const root = createRoot(host);
160
+ let scheduleReveal: ((element: HTMLElement, delayMs: number) => void) | undefined;
161
+
162
+ function Harness({ isPlaying }: { isPlaying: boolean }) {
163
+ ({ scheduleReveal } = useLayerRevealOverride({ isPlaying, selectedElement: clip }));
164
+ return null;
165
+ }
166
+
167
+ act(() => root.render(React.createElement(Harness, { isPlaying: false })));
168
+ act(() => scheduleReveal!(clip, 0));
169
+ act(() => vi.advanceTimersByTime(0));
170
+ expect([clip.style.visibility, clip.hasAttribute(CLIP_HIDDEN_ATTR)]).toEqual([
171
+ "visible",
172
+ false,
173
+ ]);
174
+
175
+ act(() => root.render(React.createElement(Harness, { isPlaying: true })));
176
+ expect([clip.style.visibility, clip.hasAttribute(CLIP_HIDDEN_ATTR)]).toEqual(["hidden", true]);
177
+ act(() => root.unmount());
178
+ });
179
+
152
180
  it("revalidates the current selection before a delayed reveal runs", () => {
153
181
  const host = document.createElement("div");
154
182
  const selected = makeEl("2", "absolute");
@@ -1,4 +1,5 @@
1
1
  import { useCallback, useEffect, useRef } from "react";
2
+ import { revealClip } from "@hyperframes/core/runtime/clip-reveal";
2
3
  import {
3
4
  LAYER_REVEAL_PRIOR_POSITION_ATTR,
4
5
  LAYER_REVEAL_PRIOR_Z_ATTR,
@@ -15,6 +16,7 @@ interface RevealedNode {
15
16
  priors: { display: string; visibility: string; opacity: string };
16
17
  /** Values THIS override wrote — restore only while they are still in place. */
17
18
  applied: { display?: string; visibility?: string; opacity?: string };
19
+ putMarkBack?: () => void;
18
20
  }
19
21
 
20
22
  interface RevealLift {
@@ -158,10 +160,11 @@ function restoreIfOurs(
158
160
  property: "display" | "visibility" | "opacity",
159
161
  applied: string | undefined,
160
162
  prior: string,
161
- ): void {
162
- if (applied == null) return;
163
- if (el.style.getPropertyValue(property) !== applied) return;
163
+ ): boolean {
164
+ if (applied == null) return false;
165
+ if (el.style.getPropertyValue(property) !== applied) return false;
164
166
  restoreInline(el, property, prior);
167
+ return true;
165
168
  }
166
169
 
167
170
  /** What hides this node at the current frame, per computed style. */
@@ -187,6 +190,7 @@ function revealNode(
187
190
  opacity: el.style.opacity,
188
191
  };
189
192
  const applied: RevealedNode["applied"] = {};
193
+ let putMarkBack: RevealedNode["putMarkBack"];
190
194
  if (needs.display) {
191
195
  // Prefer whatever the stylesheet says once the inline hide is lifted;
192
196
  // only force block when the sheet itself hides it.
@@ -195,14 +199,14 @@ function revealNode(
195
199
  applied.display = el.style.display;
196
200
  }
197
201
  if (needs.visibility) {
198
- el.style.visibility = "visible";
202
+ putMarkBack = revealClip(el);
199
203
  applied.visibility = "visible";
200
204
  }
201
205
  if (needs.opacity) {
202
206
  el.style.opacity = "1";
203
207
  applied.opacity = "1";
204
208
  }
205
- return { element: el, priors, applied };
209
+ return { element: el, priors, applied, putMarkBack };
206
210
  }
207
211
 
208
212
  /** Walk `element` → body, force-revealing every hiding node; returns the touched nodes. */
@@ -317,10 +321,11 @@ export function useLayerRevealOverride({
317
321
  const state = stateRef.current;
318
322
  stateRef.current = null;
319
323
  if (!state) return;
320
- for (const { element, priors, applied } of state.nodes) {
324
+ for (const { element, priors, applied, putMarkBack } of state.nodes) {
321
325
  if (!element.isConnected) continue;
322
326
  restoreIfOurs(element, "display", applied.display, priors.display);
323
- restoreIfOurs(element, "visibility", applied.visibility, priors.visibility);
327
+ if (restoreIfOurs(element, "visibility", applied.visibility, priors.visibility))
328
+ putMarkBack?.();
324
329
  restoreIfOurs(element, "opacity", applied.opacity, priors.opacity);
325
330
  }
326
331
  if (state.lift && state.base.isConnected) restoreLiftedElement(state.base, state.lift);
@@ -4,6 +4,7 @@ import React, { act } from "react";
4
4
  import { afterEach, describe, expect, it, vi } from "vitest";
5
5
  import { mountReactHarness } from "./domSelectionTestHarness";
6
6
  import { useGestureRecording, type GestureSample } from "./useGestureRecording";
7
+ import { CLIP_HIDDEN_ATTR } from "@hyperframes/core/runtime/clip-reveal";
7
8
 
8
9
  Reflect.set(globalThis, "IS_REACT_ACT_ENVIRONMENT", true);
9
10
 
@@ -65,6 +66,47 @@ describe("useGestureRecording", () => {
65
66
  act(() => root.unmount());
66
67
  });
67
68
 
69
+ it("lifts the runtime's hidden-clip mark while previewing a hidden clip and puts it back on release", () => {
70
+ const animationFrames: FrameRequestCallback[] = [];
71
+ vi.stubGlobal(
72
+ "requestAnimationFrame",
73
+ vi.fn((callback: FrameRequestCallback) => animationFrames.push(callback)),
74
+ );
75
+ vi.stubGlobal("cancelAnimationFrame", vi.fn());
76
+ const iframe = document.createElement("iframe");
77
+ document.body.append(iframe);
78
+ const element = iframe.contentDocument!.createElement("div");
79
+ element.id = "card";
80
+ element.style.visibility = "hidden";
81
+ element.setAttribute(CLIP_HIDDEN_ATTR, "");
82
+ iframe.contentDocument!.body.append(element);
83
+ Reflect.set(iframe.contentWindow!, "gsap", { getProperty: vi.fn(() => 0), set: vi.fn() });
84
+ Reflect.set(iframe.contentWindow!, "__timelines", {
85
+ main: { duration: () => 5, seek: vi.fn() },
86
+ });
87
+ Reflect.set(iframe.contentWindow!, "__player", { getTime: () => 1 });
88
+
89
+ let recording: ReturnType<typeof useGestureRecording> | null = null;
90
+ function Harness() {
91
+ recording = useGestureRecording();
92
+ return null;
93
+ }
94
+ const root = mountReactHarness(<Harness />);
95
+ act(() => recording?.startRecording(element, iframe, 4));
96
+ document.dispatchEvent(new MouseEvent("pointermove", { bubbles: true, clientX: 10 }));
97
+ act(() => animationFrames.shift()?.(1));
98
+ expect([element.style.visibility, element.hasAttribute(CLIP_HIDDEN_ATTR)]).toEqual([
99
+ "visible",
100
+ false,
101
+ ]);
102
+
103
+ act(() => root.unmount());
104
+ expect([element.style.visibility, element.hasAttribute(CLIP_HIDDEN_ATTR)]).toEqual([
105
+ "hidden",
106
+ true,
107
+ ]);
108
+ });
109
+
68
110
  it("releases the preview runtime when unmounted during a recording", () => {
69
111
  const cancelAnimationFrame = vi.fn();
70
112
  vi.stubGlobal(
@@ -1,3 +1,4 @@
1
+ import { revealClip } from "@hyperframes/core/runtime/clip-reveal";
1
2
  import { useCallback, useEffect, useRef, useState } from "react";
2
3
  import { usePlayerStore, liveTime } from "../player/store/playerStore";
3
4
  import { frameToSeconds, secondsToFrame } from "../player/lib/time";
@@ -36,6 +37,7 @@ interface GsapRuntime {
36
37
  maxSeekTime: number;
37
38
  savedVisibility: string;
38
39
  savedTranslate: string;
40
+ putMarkBack: (() => void) | null;
39
41
  }
40
42
 
41
43
  // ---------------------------------------------------------------------------
@@ -107,6 +109,7 @@ function connectGsapRuntime(
107
109
  elementEndTime != null && elementEndTime < tlDuration ? elementEndTime : tlDuration,
108
110
  savedVisibility: element.style.visibility,
109
111
  savedTranslate: element.style.getPropertyValue("translate"),
112
+ putMarkBack: null,
110
113
  };
111
114
  }
112
115
  } catch {
@@ -124,7 +127,8 @@ function applyRuntimePreview(
124
127
  runtime.timeline.seek(seekTime);
125
128
  runtime.element.style.setProperty("translate", "none");
126
129
  runtime.gsap.set(runtime.element, { ...properties });
127
- runtime.element.style.visibility = "visible";
130
+ const putMarkBack = revealClip(runtime.element);
131
+ runtime.putMarkBack ??= putMarkBack;
128
132
  liveTime.notify(seekTime);
129
133
  usePlayerStore.getState().setCurrentTime(seekTime);
130
134
  }
@@ -251,6 +255,7 @@ function releaseRuntimePreview(r: RecordingRefs): void {
251
255
  if (!runtime) return;
252
256
  const { element, savedVisibility, savedTranslate } = runtime;
253
257
  element.style.visibility = savedVisibility;
258
+ runtime.putMarkBack?.();
254
259
  element.style.setProperty("translate", savedTranslate || "");
255
260
  try {
256
261
  runtime.gsap.set(runtime.element, {
@@ -532,6 +532,25 @@ describe("ensureMotionPathPluginLoaded", () => {
532
532
  expect(appendedScripts).toHaveLength(1);
533
533
  });
534
534
 
535
+ it.each([
536
+ ["3.15.0", "/api/vendor/npm/gsap@3.15.0/dist/MotionPathPlugin.min.js"],
537
+ ["3.14.2", "https://cdn.jsdelivr.net/npm/gsap@3.14.2/dist/MotionPathPlugin.min.js"],
538
+ ])(
539
+ "preloads gsap %s from the host's bundled copy only when it holds that version",
540
+ (version, url) => {
541
+ const { iframe, appendedScripts } = buildBootstrapIframe({
542
+ gsap: { version, registerPlugin: vi.fn() },
543
+ });
544
+ const meta = document.createElement("meta");
545
+ meta.name = "hyperframes-gsap-base-url";
546
+ meta.content = "/api/vendor/npm";
547
+ iframe.contentDocument!.head.appendChild(meta);
548
+ ensureMotionPathPluginLoaded(iframe);
549
+ expect(appendedScripts).toHaveLength(1);
550
+ expect(appendedScripts[0]!.getAttribute("src")).toBe(url);
551
+ },
552
+ );
553
+
535
554
  it("appends the plugin script once and registers it on load", () => {
536
555
  const { iframe, contentWindow, appendedScripts, registerPlugin } = buildBootstrapIframe();
537
556
  ensureMotionPathPluginLoaded(iframe);