@hyperframes/studio 0.8.193 → 0.8.194

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-Ca-mgTnS.js";/*!
1
+ import{n as Qi}from"./index-CAQ7OcNJ.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-Ca-mgTnS.js"></script>
17
+ <script type="module" crossorigin src="/assets/index-CAQ7OcNJ.js"></script>
18
18
  <link rel="stylesheet" crossorigin href="/assets/index-CYbvOVp-.css">
19
19
  </head>
20
20
  <body>
package/dist/index.js CHANGED
@@ -17608,7 +17608,7 @@ async function writeProjectFilesWithHistoryInQueue({
17608
17608
 
17609
17609
  // src/utils/gsapSoftReload.ts
17610
17610
  import { COLOR_GRADING_SOURCE_HIDDEN_ATTR as COLOR_GRADING_SOURCE_HIDDEN_ATTR2 } from "@hyperframes/core/color-grading";
17611
- import { motionPathPluginUrl } from "@hyperframes/core/gsap-cdn";
17611
+ import { GSAP_BASE_URL_META, motionPathPluginUrl } from "@hyperframes/core/gsap-cdn";
17612
17612
 
17613
17613
  // src/utils/authoredSource.ts
17614
17614
  import { ensureHfIds, isCompositionTemplate } from "@hyperframes/parsers/hf-ids";
@@ -17870,6 +17870,12 @@ function fileDocs(ownFile, written, nestedFiles) {
17870
17870
 
17871
17871
  // src/utils/gsapSoftReload.ts
17872
17872
  var pendingReadyRetry = null;
17873
+ function previewMotionPathUrl(doc, version) {
17874
+ const baseUrl = doc.head.querySelector(
17875
+ `meta[name="${GSAP_BASE_URL_META}"]`
17876
+ )?.content;
17877
+ return motionPathPluginUrl(version, baseUrl);
17878
+ }
17873
17879
  function ensureMotionPathPluginLoaded(iframe) {
17874
17880
  if (pendingReadyRetry) window.removeEventListener("message", pendingReadyRetry);
17875
17881
  pendingReadyRetry = null;
@@ -17900,7 +17906,7 @@ function ensureMotionPathPluginLoaded(iframe) {
17900
17906
  try {
17901
17907
  win.__hfMotionPathPluginLoading = true;
17902
17908
  const pluginScript = doc.createElement("script");
17903
- pluginScript.src = motionPathPluginUrl(win.gsap?.version);
17909
+ pluginScript.src = previewMotionPathUrl(doc, win.gsap?.version);
17904
17910
  const finalize = () => {
17905
17911
  win.__hfMotionPathPluginLoading = false;
17906
17912
  try {
@@ -18086,7 +18092,7 @@ function applySoftReload(iframe, scriptText, options = {}) {
18086
18092
  }
18087
18093
  win.__hfMotionPathPluginLoading = true;
18088
18094
  const pluginScript = doc.createElement("script");
18089
- pluginScript.src = motionPathPluginUrl(win.gsap?.version);
18095
+ pluginScript.src = previewMotionPathUrl(doc, win.gsap?.version);
18090
18096
  pluginScript.onload = () => {
18091
18097
  win.__hfMotionPathPluginLoading = false;
18092
18098
  executeScript();
@@ -98684,7 +98690,7 @@ function levelOf(settings) {
98684
98690
  return settings.shutterDeg > STRONG_ABOVE ? "strong" : "subtle";
98685
98691
  }
98686
98692
  function patchForLevel(level) {
98687
- return level === "off" ? { enabled: false } : { enabled: true, ...LEVEL_SETTINGS[level] };
98693
+ return level === "off" ? { enabled: false, events: false } : { enabled: true, events: true, ...LEVEL_SETTINGS[level] };
98688
98694
  }
98689
98695
 
98690
98696
  // src/components/panels/MotionBlurPanel.tsx
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hyperframes/studio",
3
- "version": "0.8.193",
3
+ "version": "0.8.194",
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.193",
64
- "@hyperframes/sdk": "0.8.193",
65
- "@hyperframes/core": "0.8.193",
66
- "@hyperframes/shader-transitions": "0.8.193",
67
- "@hyperframes/studio-server": "0.8.193",
68
- "@hyperframes/player": "0.8.193"
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"
69
69
  },
70
70
  "devDependencies": {
71
71
  "@tailwindcss/vite": "^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.193"
85
+ "@hyperframes/producer": "0.8.194"
86
86
  },
87
87
  "peerDependencies": {
88
88
  "react": "19",
@@ -130,20 +130,29 @@ describe("MotionBlurPanel", () => {
130
130
  expect(html).toContain('data-motion-blur-samples="64"');
131
131
  });
132
132
 
133
- it("Off removes data-motion-blur and leaves every other setting on the root", async () => {
133
+ it("Off removes data-motion-blur and its events, and leaves the other settings on the root", async () => {
134
134
  const host = await mount(
135
135
  ' data-motion-blur="on" data-motion-blur-shutter="720" data-motion-blur-samples="64" data-motion-blur-tier="filter" data-motion-blur-events="on"',
136
136
  );
137
137
  await click(radio(host, "Off"));
138
138
  const html = files["index.html"] ?? "";
139
139
  expect(html).not.toContain('data-motion-blur="on"');
140
- for (const kept of ['shutter="720"', 'samples="64"', 'tier="filter"', 'events="on"']) {
140
+ expect(html).not.toContain("data-motion-blur-events");
141
+ for (const kept of ['shutter="720"', 'samples="64"', 'tier="filter"']) {
141
142
  expect(html).toContain(`data-motion-blur-${kept}`);
142
143
  }
143
144
  });
144
145
 
146
+ it("turns the events attribute on with Subtle and Strong, so callback-driven motion is found", async () => {
147
+ const host = await mount();
148
+ await click(radio(host, "Subtle"));
149
+ expect(files["index.html"]).toContain('data-motion-blur-events="on"');
150
+ await click(radio(host, "Strong"));
151
+ expect(files["index.html"]).toContain('data-motion-blur-events="on"');
152
+ });
153
+
145
154
  it("writes nothing when the chosen level is already set", async () => {
146
- const host = await mount(' data-motion-blur="on"');
155
+ const host = await mount(' data-motion-blur="on" data-motion-blur-events="on"');
147
156
  await click(radio(host, "Subtle"));
148
157
  expect(writeProjectFile).not.toHaveBeenCalled();
149
158
  });
@@ -130,9 +130,19 @@ describe("levelOf and patchForLevel", () => {
130
130
  });
131
131
 
132
132
  it("sets Subtle to the default blur and Strong to the maximum, and leaves the rest alone for Off", () => {
133
- expect(patchForLevel("subtle")).toEqual({ enabled: true, shutterDeg: 240, samples: null });
134
- expect(patchForLevel("strong")).toEqual({ enabled: true, shutterDeg: 720, samples: 64 });
135
- expect(patchForLevel("off")).toEqual({ enabled: false });
133
+ expect(patchForLevel("subtle")).toEqual({
134
+ enabled: true,
135
+ events: true,
136
+ shutterDeg: 240,
137
+ samples: null,
138
+ });
139
+ expect(patchForLevel("strong")).toEqual({
140
+ enabled: true,
141
+ events: true,
142
+ shutterDeg: 720,
143
+ samples: 64,
144
+ });
145
+ expect(patchForLevel("off")).toEqual({ enabled: false, events: false });
136
146
  });
137
147
 
138
148
  it("reads back as the level that was chosen", () => {
@@ -92,7 +92,9 @@ export function levelOf(settings: MotionBlurSettings): BlurLevel {
92
92
  return settings.shutterDeg > STRONG_ABOVE ? "strong" : "subtle";
93
93
  }
94
94
 
95
- /** What choosing `level` changes; Off leaves the shutter, samples and every other setting where they were. */
95
+ /** What choosing `level` changes; blur on turns events on too, and Off leaves the shutter, samples and the rest alone. */
96
96
  export function patchForLevel(level: BlurLevel): Partial<MotionBlurSettings> {
97
- return level === "off" ? { enabled: false } : { enabled: true, ...LEVEL_SETTINGS[level] };
97
+ return level === "off"
98
+ ? { enabled: false, events: false }
99
+ : { enabled: true, events: true, ...LEVEL_SETTINGS[level] };
98
100
  }
@@ -318,6 +318,28 @@ window.__timelines["root"] = tl;`;
318
318
  expect(contentWindow.__hfMotionPathPluginLoading).toBe(false);
319
319
  });
320
320
 
321
+ it("loads a missing soft-reload plugin from the iframe mirror and reports its failure", () => {
322
+ const { iframe, contentWindow } = buildMockIframe({
323
+ gsap: { timeline: vi.fn(), registerPlugin: vi.fn(), version: "3.14.2" },
324
+ });
325
+ const head = iframe.contentDocument!.head;
326
+ const meta = document.createElement("meta");
327
+ meta.name = "hyperframes-gsap-base-url";
328
+ meta.content = "http://localhost:8080/vendor/npm";
329
+ head.appendChild(meta);
330
+ const onAsyncFailure = vi.fn();
331
+ expect(applySoftReload(iframe, MOTION_PATH_SCRIPT_TEXT, { onAsyncFailure })).toBe("applied");
332
+ const plugin = head.querySelector("script");
333
+ expect(plugin?.src).toBe(
334
+ "http://localhost:8080/vendor/npm/gsap@3.14.2/dist/MotionPathPlugin.min.js",
335
+ );
336
+ plugin?.onerror?.(new Event("error"));
337
+ expect(onAsyncFailure).toHaveBeenCalledTimes(1);
338
+ expect(contentWindow.__hfForceTimelineRebind).not.toHaveBeenCalled();
339
+ expect(contentWindow.__hfMotionPathPluginLoading).toBe(false);
340
+ expect(head.querySelectorAll("script")).toHaveLength(1);
341
+ });
342
+
321
343
  it('returns "cannot-soft-reload" when multiple GSAP scripts exist (ambiguous)', () => {
322
344
  const script1 = document.createElement("script");
323
345
  script1.textContent = "const tl = gsap.timeline({ paused: true });";
@@ -490,6 +512,26 @@ describe("ensureMotionPathPluginLoaded", () => {
490
512
  );
491
513
  });
492
514
 
515
+ it("preloads from the iframe mirror once, retaining the runtime version", () => {
516
+ const { iframe, appendedScripts, contentWindow, registerPlugin } = buildBootstrapIframe({
517
+ gsap: { version: "3.14.2", registerPlugin: vi.fn() },
518
+ });
519
+ const meta = document.createElement("meta");
520
+ meta.name = "hyperframes-gsap-base-url";
521
+ meta.content = "http://localhost:8080/vendor/npm";
522
+ iframe.contentDocument!.head.appendChild(meta);
523
+ ensureMotionPathPluginLoaded(iframe);
524
+ ensureMotionPathPluginLoaded(iframe);
525
+ expect(appendedScripts).toHaveLength(1);
526
+ expect(appendedScripts[0]!.src).toBe(
527
+ "http://localhost:8080/vendor/npm/gsap@3.14.2/dist/MotionPathPlugin.min.js",
528
+ );
529
+ appendedScripts[0]!.onerror?.(new Event("error"));
530
+ expect(contentWindow.__hfMotionPathPluginLoading).toBe(false);
531
+ expect(registerPlugin).not.toHaveBeenCalled();
532
+ expect(appendedScripts).toHaveLength(1);
533
+ });
534
+
493
535
  it("appends the plugin script once and registers it on load", () => {
494
536
  const { iframe, contentWindow, appendedScripts, registerPlugin } = buildBootstrapIframe();
495
537
  ensureMotionPathPluginLoaded(iframe);
@@ -1,5 +1,5 @@
1
1
  import { COLOR_GRADING_SOURCE_HIDDEN_ATTR } from "@hyperframes/core/color-grading";
2
- import { motionPathPluginUrl } from "@hyperframes/core/gsap-cdn";
2
+ import { GSAP_BASE_URL_META, motionPathPluginUrl } from "@hyperframes/core/gsap-cdn";
3
3
  import { findAuthoredElement } from "./authoredSource";
4
4
  import { applyAuthoredInlineOpacity, readStampedAuthoredOpacity } from "./authoredOpacity";
5
5
  import {
@@ -50,6 +50,13 @@ type IframeWindow = Window & {
50
50
  // At most one preview waits for its runtime; a newer load replaces it, so a frame that never boots is not kept alive.
51
51
  let pendingReadyRetry: ((event: MessageEvent) => void) | null = null;
52
52
 
53
+ function previewMotionPathUrl(doc: Document, version?: string): string {
54
+ const baseUrl = doc.head.querySelector<HTMLMetaElement>(
55
+ `meta[name="${GSAP_BASE_URL_META}"]`,
56
+ )?.content;
57
+ return motionPathPluginUrl(version, baseUrl);
58
+ }
59
+
53
60
  export function ensureMotionPathPluginLoaded(iframe: HTMLIFrameElement | null): void {
54
61
  if (pendingReadyRetry) window.removeEventListener("message", pendingReadyRetry);
55
62
  pendingReadyRetry = null;
@@ -86,7 +93,7 @@ export function ensureMotionPathPluginLoaded(iframe: HTMLIFrameElement | null):
86
93
  try {
87
94
  win.__hfMotionPathPluginLoading = true;
88
95
  const pluginScript = doc.createElement("script");
89
- pluginScript.src = motionPathPluginUrl(win.gsap?.version);
96
+ pluginScript.src = previewMotionPathUrl(doc, win.gsap?.version);
90
97
  const finalize = () => {
91
98
  win.__hfMotionPathPluginLoading = false;
92
99
  try {
@@ -480,7 +487,7 @@ export function applySoftReload(
480
487
  }
481
488
  win.__hfMotionPathPluginLoading = true;
482
489
  const pluginScript = doc.createElement("script");
483
- pluginScript.src = motionPathPluginUrl(win.gsap?.version);
490
+ pluginScript.src = previewMotionPathUrl(doc, win.gsap?.version);
484
491
  pluginScript.onload = () => {
485
492
  win.__hfMotionPathPluginLoading = false;
486
493
  executeScript();