@hyperframes/core 0.8.193 → 0.8.195
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/browserMarker.d.ts +3 -0
- package/dist/browserMarker.js +7 -0
- package/dist/compiler/compositionScoping.js +10 -4
- package/dist/compiler/htmlBundler.js +6 -5
- package/dist/compiler/htmlDocument.d.ts +1 -0
- package/dist/compiler/htmlDocument.js +3 -0
- package/dist/compiler/standaloneEntry.d.ts +1 -1
- package/dist/compiler/standaloneEntry.js +1 -3
- package/dist/generated/runtime-inline.js +1 -1
- package/dist/gsapCdn.d.ts +8 -2
- package/dist/gsapCdn.js +45 -5
- package/dist/hyperframe.runtime.iife.js +72 -66
- package/dist/runtime/clipReveal.d.ts +8 -0
- package/dist/runtime/clipReveal.js +23 -0
- package/dist/runtime/media.js +8 -1
- package/dist/runtime/preloadMedia.d.ts +1 -0
- package/dist/runtime/preloadMedia.js +43 -1
- package/dist/runtime/startResolver.js +10 -2
- package/dist/runtime/timedClipHide.d.ts +4 -2
- package/dist/runtime/timedClipHide.js +28 -13
- package/package.json +12 -4
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/** The visibility pass's mark on a timed clip it hides; the runtime's rules hide the clip's contents by it. */
|
|
2
|
+
export declare const CLIP_HIDDEN_ATTR = "data-hf-clip-hidden";
|
|
3
|
+
/** Shows a clip the visibility pass hid, contents included; returns a function that puts the mark back. */
|
|
4
|
+
export declare function revealClip(el: HTMLElement, priority?: string): () => void;
|
|
5
|
+
/** Called by shader-transitions builds on load: their revealers go through revealClip. */
|
|
6
|
+
export declare function declareClipRevealAware(): void;
|
|
7
|
+
/** A shader-transitions build is on the page whose revealers predate revealClip. */
|
|
8
|
+
export declare function olderShaderBuildOnPage(): boolean;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/** The visibility pass's mark on a timed clip it hides; the runtime's rules hide the clip's contents by it. */
|
|
2
|
+
export const CLIP_HIDDEN_ATTR = "data-hf-clip-hidden";
|
|
3
|
+
/** Shows a clip the visibility pass hid, contents included; returns a function that puts the mark back. */
|
|
4
|
+
export function revealClip(el, priority = "") {
|
|
5
|
+
el.style.setProperty("visibility", "visible", priority);
|
|
6
|
+
if (!el.hasAttribute(CLIP_HIDDEN_ATTR))
|
|
7
|
+
return () => { };
|
|
8
|
+
el.removeAttribute(CLIP_HIDDEN_ATTR);
|
|
9
|
+
return function putClipHiddenMarkBack() {
|
|
10
|
+
el.setAttribute(CLIP_HIDDEN_ATTR, "");
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
/** Called by shader-transitions builds on load: their revealers go through revealClip. */
|
|
14
|
+
export function declareClipRevealAware() {
|
|
15
|
+
if (typeof window !== "undefined")
|
|
16
|
+
window.__hfClipRevealAware = true;
|
|
17
|
+
}
|
|
18
|
+
/** A shader-transitions build is on the page whose revealers predate revealClip. */
|
|
19
|
+
export function olderShaderBuildOnPage() {
|
|
20
|
+
const win = window;
|
|
21
|
+
const shaderOnPage = win.HyperShader !== undefined || win.__hf?.transitions !== undefined;
|
|
22
|
+
return shaderOnPage && win.__hfClipRevealAware !== true;
|
|
23
|
+
}
|
package/dist/runtime/media.js
CHANGED
|
@@ -233,6 +233,11 @@ const MIN_NATIVE_PLAYBACK_RATE = 1 / 16;
|
|
|
233
233
|
// A playing video is steered back by rate, not seeked (a seek resets its decoder).
|
|
234
234
|
// Its rate is written only when steering starts or stops: every write costs a frame.
|
|
235
235
|
const VIDEO_STEER = 0.03;
|
|
236
|
+
// A video that starts a cut late closes its lag over one clip at 3%; a bigger lag steers harder, in steps so the rate is written rarely.
|
|
237
|
+
const VIDEO_STEER_STEPS = [
|
|
238
|
+
[0.3, 0.25],
|
|
239
|
+
[0.1, 0.1],
|
|
240
|
+
];
|
|
236
241
|
const VIDEO_STEER_RELEASE_SECONDS = 0.01;
|
|
237
242
|
/** Direction (+1 fast, -1 slow) a video is being steered in; absent when it plays at its authored rate. */
|
|
238
243
|
const videoSteering = new WeakMap();
|
|
@@ -246,7 +251,9 @@ function steeredVideoRate(el, offset, baseRate) {
|
|
|
246
251
|
videoSteering.delete(el);
|
|
247
252
|
return baseRate;
|
|
248
253
|
}
|
|
249
|
-
|
|
254
|
+
const lag = Math.abs(offset);
|
|
255
|
+
const steer = VIDEO_STEER_STEPS.find(([from]) => lag > from)?.[1] ?? VIDEO_STEER;
|
|
256
|
+
return baseRate * (1 + direction * steer);
|
|
250
257
|
}
|
|
251
258
|
// fallow-ignore-next-line complexity
|
|
252
259
|
export function syncRuntimeMedia(params) {
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
type PreloadableMedia = Pick<HTMLMediaElement, "id" | "tagName" | "getAttribute" | "preload" | "readyState" | "networkState" | "load">;
|
|
2
2
|
export declare function isRenderFrameVideo(media: Pick<Element, "getAttribute" | "id" | "tagName">): boolean;
|
|
3
3
|
export declare function preloadMedia(media: PreloadableMedia): void;
|
|
4
|
+
export declare function prepareUpcomingMedia(media: HTMLMediaElement, isUpcoming: () => boolean): void;
|
|
4
5
|
/** Ends a fetch in flight for good, which preload none alone does not. */
|
|
5
6
|
export declare function stopMediaDownload(media: HTMLMediaElement): void;
|
|
6
7
|
export declare function releaseMedia(media: HTMLMediaElement): void;
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import { parseStrictFiniteTimingNumber } from "./playbackRate.js";
|
|
1
|
+
import { parseStrictFiniteTimingNumber, readMediaStart } from "./playbackRate.js";
|
|
2
2
|
import { createRuntimeStartTimeResolver } from "./startResolver.js";
|
|
3
3
|
import { readMediaRenderId } from "./renderFrameSibling.js";
|
|
4
4
|
import { defersMedia } from "./timedClipHide.js";
|
|
5
|
+
import { swallow } from "./diagnostics.js";
|
|
5
6
|
export function isRenderFrameVideo(media) {
|
|
6
7
|
const ids = window
|
|
7
8
|
.__HF_RENDER_FRAME_VIDEO_IDS;
|
|
@@ -17,8 +18,49 @@ export function preloadMedia(media) {
|
|
|
17
18
|
if (media.readyState < 3 && !videoAlreadyLoading)
|
|
18
19
|
media.load();
|
|
19
20
|
}
|
|
21
|
+
const pendingPlaybackStart = new WeakMap();
|
|
22
|
+
function cancelPlaybackStart(media) {
|
|
23
|
+
pendingPlaybackStart.get(media)?.();
|
|
24
|
+
}
|
|
25
|
+
export function prepareUpcomingMedia(media, isUpcoming) {
|
|
26
|
+
cancelPlaybackStart(media);
|
|
27
|
+
if (media.tagName !== "VIDEO" || isRenderFrameVideo(media))
|
|
28
|
+
return;
|
|
29
|
+
const prepare = () => {
|
|
30
|
+
if (!media.isConnected || !media.paused || media.seeking || !isUpcoming())
|
|
31
|
+
return;
|
|
32
|
+
const start = readMediaStart(media);
|
|
33
|
+
if (Math.abs(media.currentTime - start) <= 0.02)
|
|
34
|
+
return;
|
|
35
|
+
try {
|
|
36
|
+
media.currentTime = start;
|
|
37
|
+
}
|
|
38
|
+
catch (error) {
|
|
39
|
+
swallow("runtime.preload.playbackStart", error);
|
|
40
|
+
}
|
|
41
|
+
};
|
|
42
|
+
if (media.readyState > 0) {
|
|
43
|
+
prepare();
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
const cancel = () => {
|
|
47
|
+
media.removeEventListener("loadedmetadata", loaded);
|
|
48
|
+
media.removeEventListener("error", cancel);
|
|
49
|
+
media.removeEventListener("abort", cancel);
|
|
50
|
+
pendingPlaybackStart.delete(media);
|
|
51
|
+
};
|
|
52
|
+
const loaded = () => {
|
|
53
|
+
cancel();
|
|
54
|
+
prepare();
|
|
55
|
+
};
|
|
56
|
+
pendingPlaybackStart.set(media, cancel);
|
|
57
|
+
media.addEventListener("loadedmetadata", loaded);
|
|
58
|
+
media.addEventListener("error", cancel);
|
|
59
|
+
media.addEventListener("abort", cancel);
|
|
60
|
+
}
|
|
20
61
|
/** Ends a fetch in flight for good, which preload none alone does not. */
|
|
21
62
|
export function stopMediaDownload(media) {
|
|
63
|
+
cancelPlaybackStart(media);
|
|
22
64
|
for (const source of media.querySelectorAll("source"))
|
|
23
65
|
source.remove();
|
|
24
66
|
media.removeAttribute("src");
|
|
@@ -14,7 +14,15 @@ export function createRuntimeStartTimeResolver(params) {
|
|
|
14
14
|
const startCache = new WeakMap();
|
|
15
15
|
const durationCache = new WeakMap();
|
|
16
16
|
const visiting = new Set();
|
|
17
|
-
const
|
|
17
|
+
const compositionScopeOf = (element) => (element.hasAttribute("data-composition-id") ? element.parentElement : element)?.closest("[data-composition-id]") ?? null;
|
|
18
|
+
const findReferenceTarget = (refId, from) => {
|
|
19
|
+
const scope = compositionScopeOf(from);
|
|
20
|
+
if (scope) {
|
|
21
|
+
for (const candidate of scope.querySelectorAll(`[id="${refId}"]`)) {
|
|
22
|
+
if (compositionScopeOf(candidate) === scope)
|
|
23
|
+
return candidate;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
18
26
|
const byId = doc.getElementById(refId);
|
|
19
27
|
if (byId)
|
|
20
28
|
return byId;
|
|
@@ -82,7 +90,7 @@ export function createRuntimeStartTimeResolver(params) {
|
|
|
82
90
|
// Negative when a host's in-point is past its start; the host's slot cuts that part.
|
|
83
91
|
return resolveHostOffsetForElement(element, fallback) + Math.max(0, expression.value);
|
|
84
92
|
}
|
|
85
|
-
const target = findReferenceTarget(expression.refId);
|
|
93
|
+
const target = findReferenceTarget(expression.refId, element);
|
|
86
94
|
if (!target)
|
|
87
95
|
return fallback;
|
|
88
96
|
const targetStart = resolveStartForElementInternal(target, 0);
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
export
|
|
1
|
+
import { CLIP_HIDDEN_ATTR } from "./clipReveal.js";
|
|
2
|
+
export { CLIP_HIDDEN_ATTR };
|
|
3
|
+
export declare const SKIPPED_CLIP = "[data-start]:not(video, audio, img, [data-hf-upcoming], [data-hf-autostamped])[data-hf-clip-hidden]";
|
|
3
4
|
export declare function showHiddenClips(): () => void;
|
|
4
5
|
export declare function defersMedia(): boolean;
|
|
5
6
|
/** True in Studio's preview, where hidden clips' images are neither fetched nor decoded until due. */
|
|
@@ -7,3 +8,4 @@ export declare function skipsHiddenImages(): boolean;
|
|
|
7
8
|
export declare function hideTimedClipsUntilFirstPass(): void;
|
|
8
9
|
/** True when this call lifted the rule; callers then re-register what skipped hidden elements (grading). */
|
|
9
10
|
export declare function revealTimedClipsAfterFirstPass(): boolean;
|
|
11
|
+
export declare function keepInlineHideRulesForOlderShaders(): void;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { CLIP_HIDDEN_ATTR, olderShaderBuildOnPage, revealClip } from "./clipReveal.js";
|
|
1
2
|
import { RUNTIME_STAMPED_ATTR, STUDIO_PREVIEW_CAPTURE_MODE, STUDIO_PREVIEW_MARK_META, STUDIO_PREVIEW_UPCOMING_ATTR, } from "../studioPreviewMark.js";
|
|
2
3
|
// Hides timed non-media clips from script evaluation until the first visibility pass decides them;
|
|
3
4
|
// before it, a paused page painted every clip at once. Media is left to init's media pass.
|
|
@@ -6,24 +7,28 @@ const HIDE_ATTR = "data-hf-first-pass-hide";
|
|
|
6
7
|
const HIDE_UNTIL_FIRST_PASS = "[data-start]:not(video, audio, img) { visibility: hidden !important; }";
|
|
7
8
|
const PREVIEW_HIDE_UNTIL_FIRST_PASS = 'img[loading="lazy"] { display: none !important; }';
|
|
8
9
|
const SKIP_ATTR = "data-hf-skip-hidden-images";
|
|
9
|
-
export
|
|
10
|
-
const
|
|
10
|
+
export { CLIP_HIDDEN_ATTR };
|
|
11
|
+
const SKIPPABLE_CLIP = `[data-start]:not(video, audio, img, [${STUDIO_PREVIEW_UPCOMING_ATTR}], [${RUNTIME_STAMPED_ATTR}])`;
|
|
12
|
+
export const SKIPPED_CLIP = `${SKIPPABLE_CLIP}[${CLIP_HIDDEN_ATTR}]`;
|
|
11
13
|
const STUDIO_PREVIEW_MARK = `meta[name="${STUDIO_PREVIEW_MARK_META}"]`;
|
|
12
|
-
export const CLIP_HIDDEN_ATTR = "data-hf-clip-hidden";
|
|
13
14
|
const CONTENTS_ATTR = "data-hf-hidden-clip-contents";
|
|
14
|
-
const
|
|
15
|
-
const
|
|
16
|
-
const
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
15
|
+
const HIDDEN_CLIP_DOUBLED_FOR_WEIGHT = `[${CLIP_HIDDEN_ATTR}][${CLIP_HIDDEN_ATTR}]`;
|
|
16
|
+
const HIDDEN_CLIP_BY_INLINE_STYLE = `[${CLIP_HIDDEN_ATTR}][style*="visibility: hidden"]`;
|
|
17
|
+
const skipHiddenImages = (clip) => `${SKIPPABLE_CLIP}${clip} img { display: none !important; }`;
|
|
18
|
+
const hideHiddenClipContents = (clip) => {
|
|
19
|
+
const filterOutputLayers = [clip, `${clip} *`]
|
|
20
|
+
.flatMap((layer) => [layer, `${layer}::before`, `${layer}::after`])
|
|
21
|
+
.join(", ");
|
|
22
|
+
return `${clip} * { visibility: hidden !important; } ${filterOutputLayers} { filter: none !important; }`;
|
|
23
|
+
};
|
|
20
24
|
export function showHiddenClips() {
|
|
21
25
|
const hidden = Array.from(document.querySelectorAll(`[${CLIP_HIDDEN_ATTR}]`));
|
|
22
|
-
|
|
23
|
-
el.style.visibility = "visible";
|
|
26
|
+
const putMarksBack = hidden.map((el) => revealClip(el));
|
|
24
27
|
return () => {
|
|
25
28
|
for (const el of hidden)
|
|
26
29
|
el.style.visibility = "hidden";
|
|
30
|
+
for (const putMarkBack of putMarksBack)
|
|
31
|
+
putMarkBack();
|
|
27
32
|
};
|
|
28
33
|
}
|
|
29
34
|
function appendStyle(parent, attr, css) {
|
|
@@ -49,9 +54,9 @@ export function hideTimedClipsUntilFirstPass() {
|
|
|
49
54
|
const preview = mark !== null;
|
|
50
55
|
const capture = mark?.getAttribute("content") === STUDIO_PREVIEW_CAPTURE_MODE;
|
|
51
56
|
if (preview && !capture && !skipsHiddenImages())
|
|
52
|
-
appendStyle(parent, SKIP_ATTR,
|
|
57
|
+
appendStyle(parent, SKIP_ATTR, skipHiddenImages(HIDDEN_CLIP_DOUBLED_FOR_WEIGHT));
|
|
53
58
|
if (!document.querySelector(`style[${CONTENTS_ATTR}]`))
|
|
54
|
-
appendStyle(parent, CONTENTS_ATTR,
|
|
59
|
+
appendStyle(parent, CONTENTS_ATTR, hideHiddenClipContents(HIDDEN_CLIP_DOUBLED_FOR_WEIGHT));
|
|
55
60
|
const win = window;
|
|
56
61
|
// A runtime that already initialised may never run another pass to lift a new rule.
|
|
57
62
|
if (win.__hfFirstPassHidden || win.__hyperframeRuntimeBootstrapped)
|
|
@@ -72,3 +77,13 @@ export function revealTimedClipsAfterFirstPass() {
|
|
|
72
77
|
style.remove();
|
|
73
78
|
return true;
|
|
74
79
|
}
|
|
80
|
+
export function keepInlineHideRulesForOlderShaders() {
|
|
81
|
+
if (!olderShaderBuildOnPage())
|
|
82
|
+
return;
|
|
83
|
+
const skip = document.querySelector(`style[${SKIP_ATTR}]`);
|
|
84
|
+
if (skip)
|
|
85
|
+
skip.textContent = skipHiddenImages(HIDDEN_CLIP_BY_INLINE_STYLE);
|
|
86
|
+
const contents = document.querySelector(`style[${CONTENTS_ATTR}]`);
|
|
87
|
+
if (contents)
|
|
88
|
+
contents.textContent = hideHiddenClipContents(HIDDEN_CLIP_BY_INLINE_STYLE);
|
|
89
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hyperframes/core",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.195",
|
|
4
4
|
"description": "",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -52,6 +52,10 @@
|
|
|
52
52
|
"import": "./dist/safePath.js",
|
|
53
53
|
"types": "./dist/safePath.d.ts"
|
|
54
54
|
},
|
|
55
|
+
"./browser-marker": {
|
|
56
|
+
"import": "./dist/browserMarker.js",
|
|
57
|
+
"types": "./dist/browserMarker.d.ts"
|
|
58
|
+
},
|
|
55
59
|
"./atomic-file": {
|
|
56
60
|
"import": "./dist/atomicFile.js",
|
|
57
61
|
"types": "./dist/atomicFile.d.ts"
|
|
@@ -261,6 +265,10 @@
|
|
|
261
265
|
"import": "./dist/runtime/clipWindow.js",
|
|
262
266
|
"types": "./dist/runtime/clipWindow.d.ts"
|
|
263
267
|
},
|
|
268
|
+
"./runtime/clip-reveal": {
|
|
269
|
+
"import": "./dist/runtime/clipReveal.js",
|
|
270
|
+
"types": "./dist/runtime/clipReveal.d.ts"
|
|
271
|
+
},
|
|
264
272
|
"./runtime/stacking-context": {
|
|
265
273
|
"import": "./dist/runtime/stackingContext.js",
|
|
266
274
|
"types": "./dist/runtime/stackingContext.d.ts"
|
|
@@ -389,9 +397,9 @@
|
|
|
389
397
|
"linkedom": "^0.18.12",
|
|
390
398
|
"postcss": "^8.5.8",
|
|
391
399
|
"wawoff2": "^2.0.1",
|
|
392
|
-
"@hyperframes/lint": "0.8.
|
|
393
|
-
"@hyperframes/
|
|
394
|
-
"@hyperframes/
|
|
400
|
+
"@hyperframes/lint": "0.8.195",
|
|
401
|
+
"@hyperframes/parsers": "0.8.195",
|
|
402
|
+
"@hyperframes/studio-server": "0.8.195"
|
|
395
403
|
},
|
|
396
404
|
"devDependencies": {
|
|
397
405
|
"@fontsource/archivo-black": "^5.2.8",
|