@hyperframes/studio 0.2.8 → 0.3.0
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.
package/dist/index.html
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
<meta charset="UTF-8" />
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
6
|
<title>HyperFrames Studio</title>
|
|
7
|
-
<script type="module" crossorigin src="/assets/index-
|
|
7
|
+
<script type="module" crossorigin src="/assets/index-QlToZFln.js"></script>
|
|
8
8
|
<link rel="stylesheet" crossorigin href="/assets/index-DHr9yo58.css">
|
|
9
9
|
</head>
|
|
10
10
|
<body>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hyperframes/studio",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -32,8 +32,8 @@
|
|
|
32
32
|
"@phosphor-icons/react": "^2.1.10",
|
|
33
33
|
"codemirror": "^6.0.1",
|
|
34
34
|
"motion": "^12.38.0",
|
|
35
|
-
"@hyperframes/
|
|
36
|
-
"@hyperframes/
|
|
35
|
+
"@hyperframes/core": "0.3.0",
|
|
36
|
+
"@hyperframes/player": "0.3.0"
|
|
37
37
|
},
|
|
38
38
|
"devDependencies": {
|
|
39
39
|
"@types/react": "^19.0.0",
|
|
@@ -47,7 +47,7 @@
|
|
|
47
47
|
"vite": "^6.4.2",
|
|
48
48
|
"vitest": "^3.2.4",
|
|
49
49
|
"zustand": "^5.0.0",
|
|
50
|
-
"@hyperframes/producer": "0.
|
|
50
|
+
"@hyperframes/producer": "0.3.0"
|
|
51
51
|
},
|
|
52
52
|
"peerDependencies": {
|
|
53
53
|
"react": "^18.0.0 || ^19.0.0",
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { forwardRef, useRef } from "react";
|
|
2
2
|
import { useMountEffect } from "../../hooks/useMountEffect";
|
|
3
3
|
import type { HyperframesPlayer } from "@hyperframes/player";
|
|
4
|
-
|
|
4
|
+
// NOTE: importing "@hyperframes/player" registers a class extending HTMLElement
|
|
5
|
+
// at module load, which throws under SSR. Defer the import to the mount effect
|
|
6
|
+
// so it only runs in the browser.
|
|
5
7
|
|
|
6
8
|
interface PlayerProps {
|
|
7
9
|
projectId?: string;
|
|
@@ -27,55 +29,68 @@ export const Player = forwardRef<HTMLIFrameElement, PlayerProps>(
|
|
|
27
29
|
const container = containerRef.current;
|
|
28
30
|
if (!container) return;
|
|
29
31
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
const src = directUrl || `/api/projects/${projectId}/preview`;
|
|
33
|
-
player.setAttribute("src", src);
|
|
34
|
-
player.setAttribute("width", String(portrait ? 1080 : 1920));
|
|
35
|
-
player.setAttribute("height", String(portrait ? 1920 : 1080));
|
|
36
|
-
player.style.width = "100%";
|
|
37
|
-
player.style.height = "100%";
|
|
38
|
-
player.style.display = "block";
|
|
39
|
-
container.appendChild(player);
|
|
32
|
+
let canceled = false;
|
|
33
|
+
let cleanup: (() => void) | undefined;
|
|
40
34
|
|
|
41
|
-
//
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
ref(iframe);
|
|
45
|
-
} else if (ref) {
|
|
46
|
-
(ref as React.MutableRefObject<HTMLIFrameElement | null>).current = iframe;
|
|
47
|
-
}
|
|
35
|
+
// Dynamic import registers the custom element in the browser only.
|
|
36
|
+
import("@hyperframes/player").then(() => {
|
|
37
|
+
if (canceled) return;
|
|
48
38
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
39
|
+
// Create the web component imperatively to avoid JSX custom-element typing.
|
|
40
|
+
const player = document.createElement("hyperframes-player") as HyperframesPlayer;
|
|
41
|
+
const src = directUrl || `/api/projects/${projectId}/preview`;
|
|
42
|
+
player.setAttribute("src", src);
|
|
43
|
+
player.setAttribute("width", String(portrait ? 1080 : 1920));
|
|
44
|
+
player.setAttribute("height", String(portrait ? 1920 : 1080));
|
|
45
|
+
player.style.width = "100%";
|
|
46
|
+
player.style.height = "100%";
|
|
47
|
+
player.style.display = "block";
|
|
48
|
+
container.appendChild(player);
|
|
53
49
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
loadCountRef.current++;
|
|
57
|
-
// Reveal animation on reload (hot-reload, composition switch)
|
|
58
|
-
if (loadCountRef.current > 1) {
|
|
59
|
-
container.classList.remove("preview-revealing");
|
|
60
|
-
void container.offsetWidth;
|
|
61
|
-
container.classList.add("preview-revealing");
|
|
62
|
-
const onEnd = () => container.classList.remove("preview-revealing");
|
|
63
|
-
container.addEventListener("animationend", onEnd, { once: true });
|
|
64
|
-
}
|
|
65
|
-
onLoad();
|
|
66
|
-
};
|
|
67
|
-
iframe.addEventListener("load", handleLoad);
|
|
68
|
-
|
|
69
|
-
return () => {
|
|
70
|
-
iframe.removeEventListener("load", handleLoad);
|
|
71
|
-
player.removeEventListener("click", preventToggle, { capture: true });
|
|
72
|
-
container.removeChild(player);
|
|
73
|
-
// Clear the forwarded ref
|
|
50
|
+
// Bridge the inner iframe to the forwarded ref for useTimelinePlayer.
|
|
51
|
+
const iframe = player.iframeElement;
|
|
74
52
|
if (typeof ref === "function") {
|
|
75
|
-
ref(
|
|
53
|
+
ref(iframe);
|
|
76
54
|
} else if (ref) {
|
|
77
|
-
(ref as React.MutableRefObject<HTMLIFrameElement | null>).current =
|
|
55
|
+
(ref as React.MutableRefObject<HTMLIFrameElement | null>).current = iframe;
|
|
78
56
|
}
|
|
57
|
+
|
|
58
|
+
// Prevent the web component's built-in click-to-toggle behavior.
|
|
59
|
+
// The studio manages playback exclusively via useTimelinePlayer.
|
|
60
|
+
const preventToggle = (e: Event) => e.stopImmediatePropagation();
|
|
61
|
+
player.addEventListener("click", preventToggle, { capture: true });
|
|
62
|
+
|
|
63
|
+
// Forward the iframe's native load event to the studio's onIframeLoad.
|
|
64
|
+
const handleLoad = () => {
|
|
65
|
+
loadCountRef.current++;
|
|
66
|
+
// Reveal animation on reload (hot-reload, composition switch)
|
|
67
|
+
if (loadCountRef.current > 1) {
|
|
68
|
+
container.classList.remove("preview-revealing");
|
|
69
|
+
void container.offsetWidth;
|
|
70
|
+
container.classList.add("preview-revealing");
|
|
71
|
+
const onEnd = () => container.classList.remove("preview-revealing");
|
|
72
|
+
container.addEventListener("animationend", onEnd, { once: true });
|
|
73
|
+
}
|
|
74
|
+
onLoad();
|
|
75
|
+
};
|
|
76
|
+
iframe.addEventListener("load", handleLoad);
|
|
77
|
+
|
|
78
|
+
cleanup = () => {
|
|
79
|
+
iframe.removeEventListener("load", handleLoad);
|
|
80
|
+
player.removeEventListener("click", preventToggle, { capture: true });
|
|
81
|
+
container.removeChild(player);
|
|
82
|
+
// Clear the forwarded ref
|
|
83
|
+
if (typeof ref === "function") {
|
|
84
|
+
ref(null);
|
|
85
|
+
} else if (ref) {
|
|
86
|
+
(ref as React.MutableRefObject<HTMLIFrameElement | null>).current = null;
|
|
87
|
+
}
|
|
88
|
+
};
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
return () => {
|
|
92
|
+
canceled = true;
|
|
93
|
+
cleanup?.();
|
|
79
94
|
};
|
|
80
95
|
});
|
|
81
96
|
|