@hyperframes/core 0.8.199 → 0.8.201
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/audioAnalysis.d.ts +2 -0
- package/dist/audioAnalysis.js +14 -5
- package/dist/audioGroups.d.ts +3 -7
- package/dist/audioGroups.js +4 -8
- package/dist/audioResample.d.ts +2 -0
- package/dist/audioResample.js +7 -0
- package/dist/audioRhythm.d.ts +3 -0
- package/dist/audioRhythm.js +37 -20
- package/dist/audioSteps.d.ts +3 -0
- package/dist/audioSteps.js +7 -0
- package/dist/beats/beatDetection.js +22 -7
- package/dist/compiler/htmlBundler.js +15 -40
- package/dist/compiler/inlineSubCompositions.d.ts +2 -0
- package/dist/compiler/inlineSubCompositions.js +64 -5
- package/dist/compiler/srcset.d.ts +1 -0
- package/dist/compiler/srcset.js +46 -0
- package/dist/generated/position-edits-render-inline.js +1 -1
- package/dist/generated/runtime-inline.js +1 -1
- package/dist/hyperframe.runtime.iife.js +66 -62
- package/dist/index.d.ts +3 -1
- package/dist/index.js +3 -1
- package/dist/mediaGradeAnalyzer.d.ts +1 -0
- package/dist/mediaGradeAnalyzer.js +32 -20
- package/dist/previewProxyBox.d.ts +14 -0
- package/dist/previewProxyBox.js +15 -0
- package/dist/runtime/bridge.d.ts +1 -0
- package/dist/runtime/bridge.js +1 -0
- package/dist/runtime/clipReveal.d.ts +1 -1
- package/dist/runtime/diagnostics.d.ts +2 -0
- package/dist/runtime/diagnostics.js +40 -1
- package/dist/runtime/media.d.ts +1 -0
- package/dist/runtime/media.js +15 -21
- package/dist/runtime/timedClipHide.d.ts +2 -1
- package/dist/runtime/timedClipHide.js +8 -4
- package/dist/runtime/timeline.d.ts +8 -1
- package/dist/runtime/timeline.js +32 -13
- package/dist/runtime/types.d.ts +2 -1
- package/dist/runtime/validateVariables.js +6 -7
- package/dist/studioPreviewMark.d.ts +3 -1
- package/dist/studioPreviewMark.js +4 -0
- package/dist/variables.d.ts +1 -1
- package/package.json +4 -4
package/dist/index.d.ts
CHANGED
|
@@ -22,6 +22,7 @@ export { AUDIO_GROUP_RENDER_ID_ATTR, MEDIA_RENDER_ID_ATTR, assignMediaRenderIds,
|
|
|
22
22
|
export { RENDER_FRAME_ID_PREFIX, RENDER_FRAME_ID_SUFFIX, renderFrameIdForRenderId, } from "./runtime/renderFrameSibling.js";
|
|
23
23
|
export { rewriteAssetPaths, rewriteAssetPath, rewriteCssAssetUrls, rewriteInlineStyleAssetUrls, } from "./compiler/rewriteSubCompPaths.js";
|
|
24
24
|
export { CSS_URL_RE, isNonRelativeUrl, isPathInside } from "./compiler/assetPaths.js";
|
|
25
|
+
export { rewriteSrcsetUrls } from "./compiler/srcset.js";
|
|
25
26
|
export { checkSubCompositionUsability, type ParsableDocumentLike, type SubCompositionValidity, type SubCompositionValidityReason, } from "./compiler/subCompositionValidity.js";
|
|
26
27
|
export { RUNTIME_BOOTSTRAP_ATTR, stripEmbeddedRuntimeScripts } from "./compiler/htmlDocument.js";
|
|
27
28
|
export { COMPOSITION_SOURCE_URL } from "./compiler/scriptRuns.js";
|
|
@@ -32,7 +33,7 @@ export { clearGifFramesBeforeNext, gifClearsAfterLeavingFrameInPlace, parseAnima
|
|
|
32
33
|
export { HF_COLOR_GRADING_ATTR, HF_COLOR_GRADING_ADJUST_KEYS, HF_COLOR_GRADING_ACTIVE_EFFECT_KEYS, HF_COLOR_GRADING_ANIMATABLE_PROPERTIES, HF_COLOR_GRADING_CANVAS_ID_PREFIX, HF_COLOR_GRADING_COLOR_SPACE, HF_COLOR_GRADING_CURVE_KEYS, HF_COLOR_GRADING_DETAIL_KEYS, HF_COLOR_GRADING_EFFECT_APPLY_DEFAULTS, HF_COLOR_GRADING_EFFECT_KEYS, HF_COLOR_GRADING_EFFECT_PRESETS, HF_COLOR_GRADING_GRADE_PRESETS, HF_COLOR_GRADING_HUE_CURVE_KEYS, HF_COLOR_GRADING_LUT_KEYS, HF_COLOR_GRADING_PALETTES, HF_COLOR_GRADING_PRESETS, HF_COLOR_GRADING_TOP_LEVEL_KEYS, HF_COLOR_GRADING_WHEEL_KEYS, getHfColorGradingCapabilities, hasHfColorGradingAuthoredValues, isHfColorGradingActive, normalizeHfColorGrading, normalizeHfColorGradingWithVariables, resolveHfColorGradingVariables, serializeHfColorGrading, type HfColorGrading, type HfColorGradingAdjust, type HfColorGradingAdjustKey, type HfColorGradingAnimatablePath, type HfColorGradingActiveEffectKey, type HfColorGradingDetailKey, type HfColorGradingDetails, type HfColorGradingEffectKey, type HfColorGradingEffects, type HfColorGradingLutRef, type HfColorGradingPreset, type HfColorGradingPresetId, type HfColorGradingTarget, type HfColorGradingVariableMap, type HfColorGradingCapabilities, type HfColorGradingCurveKey, type HfColorGradingCurves, type HfColorGradingHueCurveKey, type HfColorGradingHueCurves, type HfColorGradingHueRange, type HfColorGradingSecondary, type HfColorGradingSecondaryCorrection, type HfColorGradingSoftRange, type HfColorGradingTonalWheel, type HfColorGradingWheelKey, type HfColorGradingWheels, type HfColorCurvePoint, type HfHueCurvePoint, type NormalizedHfColorGrading, type NormalizedHfColorGradingCurves, type NormalizedHfColorGradingHueCurves, type NormalizedHfColorGradingSecondary, type NormalizedHfColorGradingWheels, type ResolvedHfColorGrading, } from "./colorGrading.js";
|
|
33
34
|
export { parseCubeLut, CubeLutParseError, type ParseCubeLutOptions } from "./colorLuts.js";
|
|
34
35
|
export { firstFrameColourArgs, hdrToSdrToneMapFilter, parseFirstFrameColour, type ToneMapSourceColour, } from "./hdrToneMap.js";
|
|
35
|
-
export { PREVIEW_PROXY_BOX_PARAM, formatPreviewProxyBox, parsePreviewProxyBox, quantizePreviewProxyBox, type PreviewProxyBox, } from "./previewProxyBox.js";
|
|
36
|
+
export { HEAVY_BITRATE, HEAVY_PIXEL_SHARE, PREVIEW_PROXY_BOX_PARAM, PREVIEW_PROXY_LIGHT_PARAM, PREVIEW_QUALITY_LEVELS, formatPreviewProxyBox, isHeavyForPreview, isPreviewQuality, parsePreviewProxyBox, quantizePreviewProxyBox, scalePreviewProxyBox, type PreviewProxyBox, type PreviewQuality, type PreviewSourceFacts, } from "./previewProxyBox.js";
|
|
36
37
|
export { HYPERFRAME_RUNTIME_ARTIFACTS, HYPERFRAME_RUNTIME_CONTRACT, loadHyperframeRuntimeSource, type HyperframeRuntimeContract, } from "./inline-scripts/hyperframe.js";
|
|
37
38
|
export { HYPERFRAME_RUNTIME_GLOBALS, HYPERFRAME_BRIDGE_SOURCES, HYPERFRAME_CONTROL_ACTIONS, type HyperframeControlAction, } from "./inline-scripts/runtimeContract.js";
|
|
38
39
|
export { getHyperframeRuntimeScript } from "./generated/runtime-inline.js";
|
|
@@ -49,6 +50,7 @@ export { fitTextFontSize } from "./text/index.js";
|
|
|
49
50
|
export type { FitTextOptions, FitTextResult } from "./text/index.js";
|
|
50
51
|
export { formatRenderOutputTimestamp } from "./utils/renderOutputTimestamp.js";
|
|
51
52
|
export { getVariables } from "./runtime/getVariables.js";
|
|
53
|
+
export { SCENE_CALLBACK_ERROR_PREFIX } from "./runtime/diagnostics.js";
|
|
52
54
|
export { parseStartExpression, parseNumeric, type ReferenceExpression, } from "./runtime/startExpression.js";
|
|
53
55
|
export { COMPOSITION_CONTRACT_VERSION, COMPOSITION_ATTRIBUTES, CANONICAL_AUTHORED_TIMING_ATTRIBUTES, DERIVED_TIMING_ATTRIBUTES, LEGACY_TIMING_ATTRIBUTES, readClipTiming, writeClipTiming, ClipTimingWriteError, type ClipTiming, type ClipTimingDiagnostic, type ClipTimingDiagnosticCode, type ClipTimingUpdate, } from "./compositionContract.js";
|
|
54
56
|
export { createRuntimeStartTimeResolver } from "./runtime/startResolver.js";
|
package/dist/index.js
CHANGED
|
@@ -18,6 +18,7 @@ export { RENDER_FRAME_ID_PREFIX, RENDER_FRAME_ID_SUFFIX, renderFrameIdForRenderI
|
|
|
18
18
|
// package (which imports core utilities back).
|
|
19
19
|
export { rewriteAssetPaths, rewriteAssetPath, rewriteCssAssetUrls, rewriteInlineStyleAssetUrls, } from "./compiler/rewriteSubCompPaths.js";
|
|
20
20
|
export { CSS_URL_RE, isNonRelativeUrl, isPathInside } from "./compiler/assetPaths.js";
|
|
21
|
+
export { rewriteSrcsetUrls } from "./compiler/srcset.js";
|
|
21
22
|
export { checkSubCompositionUsability, } from "./compiler/subCompositionValidity.js";
|
|
22
23
|
export { RUNTIME_BOOTSTRAP_ATTR, stripEmbeddedRuntimeScripts } from "./compiler/htmlDocument.js";
|
|
23
24
|
export { COMPOSITION_SOURCE_URL } from "./compiler/scriptRuns.js";
|
|
@@ -28,7 +29,7 @@ export { clearGifFramesBeforeNext, gifClearsAfterLeavingFrameInPlace, parseAnima
|
|
|
28
29
|
export { HF_COLOR_GRADING_ATTR, HF_COLOR_GRADING_ADJUST_KEYS, HF_COLOR_GRADING_ACTIVE_EFFECT_KEYS, HF_COLOR_GRADING_ANIMATABLE_PROPERTIES, HF_COLOR_GRADING_CANVAS_ID_PREFIX, HF_COLOR_GRADING_COLOR_SPACE, HF_COLOR_GRADING_CURVE_KEYS, HF_COLOR_GRADING_DETAIL_KEYS, HF_COLOR_GRADING_EFFECT_APPLY_DEFAULTS, HF_COLOR_GRADING_EFFECT_KEYS, HF_COLOR_GRADING_EFFECT_PRESETS, HF_COLOR_GRADING_GRADE_PRESETS, HF_COLOR_GRADING_HUE_CURVE_KEYS, HF_COLOR_GRADING_LUT_KEYS, HF_COLOR_GRADING_PALETTES, HF_COLOR_GRADING_PRESETS, HF_COLOR_GRADING_TOP_LEVEL_KEYS, HF_COLOR_GRADING_WHEEL_KEYS, getHfColorGradingCapabilities, hasHfColorGradingAuthoredValues, isHfColorGradingActive, normalizeHfColorGrading, normalizeHfColorGradingWithVariables, resolveHfColorGradingVariables, serializeHfColorGrading, } from "./colorGrading.js";
|
|
29
30
|
export { parseCubeLut, CubeLutParseError } from "./colorLuts.js";
|
|
30
31
|
export { firstFrameColourArgs, hdrToSdrToneMapFilter, parseFirstFrameColour, } from "./hdrToneMap.js";
|
|
31
|
-
export { PREVIEW_PROXY_BOX_PARAM, formatPreviewProxyBox, parsePreviewProxyBox, quantizePreviewProxyBox, } from "./previewProxyBox.js";
|
|
32
|
+
export { HEAVY_BITRATE, HEAVY_PIXEL_SHARE, PREVIEW_PROXY_BOX_PARAM, PREVIEW_PROXY_LIGHT_PARAM, PREVIEW_QUALITY_LEVELS, formatPreviewProxyBox, isHeavyForPreview, isPreviewQuality, parsePreviewProxyBox, quantizePreviewProxyBox, scalePreviewProxyBox, } from "./previewProxyBox.js";
|
|
32
33
|
// Inline scripts
|
|
33
34
|
export { HYPERFRAME_RUNTIME_ARTIFACTS, HYPERFRAME_RUNTIME_CONTRACT, loadHyperframeRuntimeSource, } from "./inline-scripts/hyperframe.js";
|
|
34
35
|
export { HYPERFRAME_RUNTIME_GLOBALS, HYPERFRAME_BRIDGE_SOURCES, HYPERFRAME_CONTROL_ACTIONS, } from "./inline-scripts/runtimeContract.js";
|
|
@@ -44,6 +45,7 @@ export { fitTextFontSize } from "./text/index.js";
|
|
|
44
45
|
export { formatRenderOutputTimestamp } from "./utils/renderOutputTimestamp.js";
|
|
45
46
|
// Runtime helpers (composition-side)
|
|
46
47
|
export { getVariables } from "./runtime/getVariables.js";
|
|
48
|
+
export { SCENE_CALLBACK_ERROR_PREFIX } from "./runtime/diagnostics.js";
|
|
47
49
|
export { parseStartExpression, parseNumeric, } from "./runtime/startExpression.js";
|
|
48
50
|
export { COMPOSITION_CONTRACT_VERSION, COMPOSITION_ATTRIBUTES, CANONICAL_AUTHORED_TIMING_ATTRIBUTES, DERIVED_TIMING_ATTRIBUTES, LEGACY_TIMING_ATTRIBUTES, readClipTiming, writeClipTiming, ClipTimingWriteError, } from "./compositionContract.js";
|
|
49
51
|
// Also exposed via the ./runtime/start-resolver subpath. This root re-export
|
|
@@ -56,6 +56,7 @@ type NumericStats = Readonly<Record<string, number | undefined>>;
|
|
|
56
56
|
export declare function parseMediaTreatmentSignalStats(raw: string): GradeSignalFrame[];
|
|
57
57
|
export declare function statsToAdjust(stats: NumericStats): Pick<MediaTreatmentAnalysis, "adjust" | "measured">;
|
|
58
58
|
export declare function summarizeMediaTreatmentAnalysis(probe: GradeMediaProbe, frames: readonly GradeSignalFrame[]): MediaTreatmentAnalysis;
|
|
59
|
+
export declare function gradeSampleTimes(duration: number): number[];
|
|
59
60
|
export declare function analyzeMediaGrade(mediaPath: string, options?: {
|
|
60
61
|
ffmpegPath?: string;
|
|
61
62
|
ffprobePath?: string;
|
|
@@ -212,43 +212,55 @@ export function summarizeMediaTreatmentAnalysis(probe, frames) {
|
|
|
212
212
|
warnings,
|
|
213
213
|
};
|
|
214
214
|
}
|
|
215
|
-
export function
|
|
216
|
-
|
|
217
|
-
|
|
215
|
+
export function gradeSampleTimes(duration) {
|
|
216
|
+
return Array.from({ length: SAMPLE_FRAMES }, (_, i) => (duration * (i + 0.5)) / SAMPLE_FRAMES);
|
|
217
|
+
}
|
|
218
|
+
function readSignalStats(ffmpegPath, mediaPath, seekSeconds, frames) {
|
|
219
|
+
const timeoutMs = Number(process.env.HYPERFRAMES_ANALYZE_TIMEOUT_MS) || DEFAULT_TIMEOUT_MS;
|
|
220
|
+
const keyframeSeek = seekSeconds === null ? [] : ["-noaccurate_seek", "-ss", seekSeconds.toFixed(3)];
|
|
218
221
|
try {
|
|
219
|
-
|
|
220
|
-
const isImage = IMAGE_EXTENSIONS.has(extname(mediaPath).toLowerCase());
|
|
221
|
-
const fps = !isImage && probe.duration
|
|
222
|
-
? Math.max(0.1, Math.min(2, SAMPLE_FRAMES / probe.duration))
|
|
223
|
-
: null;
|
|
224
|
-
const filters = [
|
|
225
|
-
fps ? `fps=${fps.toFixed(4)}` : null,
|
|
226
|
-
"format=yuv444p",
|
|
227
|
-
"signalstats",
|
|
228
|
-
"metadata=print:file=-",
|
|
229
|
-
]
|
|
230
|
-
.filter(Boolean)
|
|
231
|
-
.join(",");
|
|
232
|
-
const raw = execFileSync(ffmpegPath, [
|
|
222
|
+
return execFileSync(ffmpegPath, [
|
|
233
223
|
"-hide_banner",
|
|
234
224
|
"-nostdin",
|
|
235
225
|
"-v",
|
|
236
226
|
"error",
|
|
227
|
+
...keyframeSeek,
|
|
237
228
|
"-i",
|
|
238
229
|
mediaPath,
|
|
239
230
|
"-vf",
|
|
240
|
-
|
|
231
|
+
`trim=end_frame=${frames},format=yuv444p,signalstats,metadata=print:file=-`,
|
|
241
232
|
"-frames:v",
|
|
242
|
-
String(
|
|
233
|
+
String(frames),
|
|
243
234
|
"-f",
|
|
244
235
|
"null",
|
|
245
236
|
"-",
|
|
246
237
|
], {
|
|
247
238
|
encoding: "utf8",
|
|
248
|
-
timeout:
|
|
239
|
+
timeout: timeoutMs,
|
|
249
240
|
stdio: ["ignore", "pipe", "pipe"],
|
|
250
241
|
windowsHide: true,
|
|
251
242
|
});
|
|
243
|
+
}
|
|
244
|
+
catch (error) {
|
|
245
|
+
if (error.code !== "ETIMEDOUT")
|
|
246
|
+
throw error;
|
|
247
|
+
const target = seekSeconds === null
|
|
248
|
+
? `the first ${frames} frames`
|
|
249
|
+
: `the frame at ${seekSeconds.toFixed(1)}s`;
|
|
250
|
+
throw new Error(`FFmpeg took longer than ${timeoutMs / 1000}s to measure ${target}; set HYPERFRAMES_ANALYZE_TIMEOUT_MS to allow more`);
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
export function analyzeMediaGrade(mediaPath, options = {}) {
|
|
254
|
+
const ffmpegPath = options.ffmpegPath ?? "ffmpeg";
|
|
255
|
+
const ffprobePath = options.ffprobePath ?? "ffprobe";
|
|
256
|
+
try {
|
|
257
|
+
const probe = probeMedia(mediaPath, ffprobePath);
|
|
258
|
+
const isImage = IMAGE_EXTENSIONS.has(extname(mediaPath).toLowerCase());
|
|
259
|
+
const raw = !isImage && probe.duration
|
|
260
|
+
? gradeSampleTimes(probe.duration)
|
|
261
|
+
.map((time) => readSignalStats(ffmpegPath, mediaPath, time, 1))
|
|
262
|
+
.join("\n")
|
|
263
|
+
: readSignalStats(ffmpegPath, mediaPath, null, SAMPLE_FRAMES);
|
|
252
264
|
return summarizeMediaTreatmentAnalysis(probe, parseMediaTreatmentSignalStats(raw));
|
|
253
265
|
}
|
|
254
266
|
catch (error) {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
/** Query parameter carrying the largest box, in device pixels, a preview shows a proxied video at. */
|
|
2
2
|
export declare const PREVIEW_PROXY_BOX_PARAM = "hf-proxy-box";
|
|
3
|
+
export declare const PREVIEW_PROXY_LIGHT_PARAM = "hf-proxy-light";
|
|
3
4
|
export type PreviewProxyBox = {
|
|
4
5
|
width: number;
|
|
5
6
|
height: number;
|
|
@@ -9,3 +10,16 @@ export declare function quantizePreviewProxyBox(width: number, height: number):
|
|
|
9
10
|
export declare function formatPreviewProxyBox(box: PreviewProxyBox): string;
|
|
10
11
|
/** Accepts only ladder rungs, so a client cannot make one cached copy per pixel. */
|
|
11
12
|
export declare function parsePreviewProxyBox(raw: string): PreviewProxyBox | null;
|
|
13
|
+
export declare const PREVIEW_QUALITY_LEVELS: readonly [1, 0.5, 0.25, 0.125];
|
|
14
|
+
export type PreviewQuality = (typeof PREVIEW_QUALITY_LEVELS)[number];
|
|
15
|
+
export declare function isPreviewQuality(value: unknown): value is PreviewQuality;
|
|
16
|
+
export type PreviewSourceFacts = {
|
|
17
|
+
width: number;
|
|
18
|
+
height: number;
|
|
19
|
+
bitDepth: number;
|
|
20
|
+
bitrate: number;
|
|
21
|
+
};
|
|
22
|
+
export declare const HEAVY_PIXEL_SHARE = 0.5;
|
|
23
|
+
export declare const HEAVY_BITRATE = 40000000;
|
|
24
|
+
export declare function scalePreviewProxyBox(box: PreviewProxyBox, level: PreviewQuality): PreviewProxyBox;
|
|
25
|
+
export declare function isHeavyForPreview(facts: PreviewSourceFacts, copyBox: PreviewProxyBox): boolean;
|
package/dist/previewProxyBox.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
/** Query parameter carrying the largest box, in device pixels, a preview shows a proxied video at. */
|
|
2
2
|
export const PREVIEW_PROXY_BOX_PARAM = "hf-proxy-box";
|
|
3
|
+
export const PREVIEW_PROXY_LIGHT_PARAM = "hf-proxy-light";
|
|
3
4
|
// 256 * 2^(k/4), even: nearby boxes share one cached copy, and a rung overshoots by at most ~19%.
|
|
4
5
|
const RUNGS = [
|
|
5
6
|
256, 304, 362, 430, 512, 608, 724, 862, 1024, 1218, 1448, 1722, 2048, 2436, 2896, 3444, 4096,
|
|
@@ -28,3 +29,17 @@ export function parsePreviewProxyBox(raw) {
|
|
|
28
29
|
const height = Number(match[2]);
|
|
29
30
|
return RUNGS.includes(width) && RUNGS.includes(height) ? { width, height } : null;
|
|
30
31
|
}
|
|
32
|
+
export const PREVIEW_QUALITY_LEVELS = [1, 0.5, 0.25, 0.125];
|
|
33
|
+
export function isPreviewQuality(value) {
|
|
34
|
+
return PREVIEW_QUALITY_LEVELS.some((level) => level === value);
|
|
35
|
+
}
|
|
36
|
+
export const HEAVY_PIXEL_SHARE = 0.5;
|
|
37
|
+
export const HEAVY_BITRATE = 40_000_000;
|
|
38
|
+
export function scalePreviewProxyBox(box, level) {
|
|
39
|
+
return quantizePreviewProxyBox(box.width * level, box.height * level) ?? box;
|
|
40
|
+
}
|
|
41
|
+
export function isHeavyForPreview(facts, copyBox) {
|
|
42
|
+
return (copyBox.width * copyBox.height <= HEAVY_PIXEL_SHARE * facts.width * facts.height ||
|
|
43
|
+
facts.bitDepth > 8 ||
|
|
44
|
+
facts.bitrate >= HEAVY_BITRATE);
|
|
45
|
+
}
|
package/dist/runtime/bridge.d.ts
CHANGED
|
@@ -22,6 +22,7 @@ type BridgeDeps = {
|
|
|
22
22
|
onSetRuntimeData?: (channel: string, payload: unknown, requestId?: number) => void;
|
|
23
23
|
onClearRuntimeData?: (channel: string, requestId?: number) => void;
|
|
24
24
|
onSetDisplayScale?: (scale: number) => void;
|
|
25
|
+
onSetPreviewQuality?: (level: number) => void;
|
|
25
26
|
getCanonicalFps: () => number;
|
|
26
27
|
};
|
|
27
28
|
export declare function setRuntimeProtocolFps(fps: number): void;
|
package/dist/runtime/bridge.js
CHANGED
|
@@ -30,6 +30,7 @@ const CONTROL_HANDLERS = new Map(Object.entries({
|
|
|
30
30
|
"set-playback-rate": (data, deps) => deps.onSetPlaybackRate(Number(data.playbackRate ?? 1)),
|
|
31
31
|
"set-idle-heartbeat": (data, deps) => deps.onSetIdleHeartbeat(Boolean(data.slow)),
|
|
32
32
|
"set-display-scale": (data, deps) => deps.onSetDisplayScale?.(Number(data.scale)),
|
|
33
|
+
"set-preview-quality": (data, deps) => deps.onSetPreviewQuality?.(Number(data.level)),
|
|
33
34
|
"set-root-duration": (data, deps) => deps.onSetRootDuration(Number(data.durationSeconds ?? 0)),
|
|
34
35
|
"set-play-range": (data, deps) => deps.onSetPlayRange(Number(data.startSeconds ?? 0), data.endSeconds == null ? null : Number(data.endSeconds)),
|
|
35
36
|
"set-color-grading": (data, deps) => deps.onSetColorGrading(data.target ?? null, data.grading ?? null),
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/** The visibility pass's mark on a timed clip it hides; the runtime's rules hide the clip's contents by it. */
|
|
2
2
|
export declare const CLIP_HIDDEN_ATTR = "data-hf-clip-hidden";
|
|
3
3
|
/** Shows a clip the visibility pass hid, contents included; returns a function that puts the mark back. */
|
|
4
|
-
export declare function revealClip(el:
|
|
4
|
+
export declare function revealClip(el: Element & ElementCSSInlineStyle, priority?: string): () => void;
|
|
5
5
|
/** Called by shader-transitions builds on load: their revealers go through revealClip. */
|
|
6
6
|
export declare function declareClipRevealAware(): void;
|
|
7
7
|
/** A shader-transitions build is on the page whose revealers predate revealClip. */
|
|
@@ -1,3 +1,5 @@
|
|
|
1
1
|
/** The named error readiness waits fail with once start-up or the render-ready publish throws; it stays set. */
|
|
2
2
|
export declare function recordStartupError(error: unknown): void;
|
|
3
|
+
/** Starts the warning for a scene callback that threw; the render log shows it once per render. */
|
|
4
|
+
export declare const SCENE_CALLBACK_ERROR_PREFIX = "[hyperframes] scene timeline callback threw:";
|
|
3
5
|
export declare function swallow(label: string, error?: unknown): void;
|
|
@@ -32,15 +32,24 @@ import { getDebugSurface } from "./globals.js";
|
|
|
32
32
|
export function recordStartupError(error) {
|
|
33
33
|
let detail = "unknown error";
|
|
34
34
|
try {
|
|
35
|
-
|
|
35
|
+
if (error instanceof Error) {
|
|
36
|
+
detail = `${error.name}: ${error.message}`;
|
|
37
|
+
detail += startupErrorStack(error, detail);
|
|
38
|
+
}
|
|
39
|
+
else {
|
|
40
|
+
detail = String(error);
|
|
41
|
+
}
|
|
36
42
|
}
|
|
37
43
|
catch (err) {
|
|
38
44
|
swallow("runtime.startupError", err);
|
|
39
45
|
}
|
|
46
|
+
detail = detail.replace(/\s+/g, " ").trim();
|
|
40
47
|
window.__hfStartupError =
|
|
41
48
|
`HyperFrames runtime failed: ${detail}. Check the composition's scripts and window.__timelines ` +
|
|
42
49
|
`entries; if they look right, report this as a HyperFrames bug.`;
|
|
43
50
|
}
|
|
51
|
+
/** Starts the warning for a scene callback that threw; the render log shows it once per render. */
|
|
52
|
+
export const SCENE_CALLBACK_ERROR_PREFIX = "[hyperframes] scene timeline callback threw:";
|
|
44
53
|
export function swallow(label, error) {
|
|
45
54
|
if (typeof window === "undefined")
|
|
46
55
|
return;
|
|
@@ -63,3 +72,33 @@ export function swallow(label, error) {
|
|
|
63
72
|
console.debug(`[hyperframes] ${label} swallowed:`, error);
|
|
64
73
|
}
|
|
65
74
|
}
|
|
75
|
+
function isCompositionFrame(frame) {
|
|
76
|
+
const location = /(?:\(|@|\bat )(\S+?):\d+(?::\d+)?\)?$/.exec(frame)?.[1];
|
|
77
|
+
if (!location || location.startsWith("hyperframes://"))
|
|
78
|
+
return false;
|
|
79
|
+
return (location.startsWith("hyperframes-composition://") ||
|
|
80
|
+
/\.html?(?:[?#]|$)/i.test(location) ||
|
|
81
|
+
(typeof document !== "undefined" && location === document.URL));
|
|
82
|
+
}
|
|
83
|
+
function startupErrorStack(error, header) {
|
|
84
|
+
try {
|
|
85
|
+
const stack = error.stack;
|
|
86
|
+
if (typeof stack !== "string")
|
|
87
|
+
return "";
|
|
88
|
+
const hasHeader = stack === header || stack.startsWith(`${header}\n`) || stack.startsWith(`${header}\r`);
|
|
89
|
+
const body = hasHeader ? stack.slice(header.length) : stack;
|
|
90
|
+
const frames = body
|
|
91
|
+
.split(/[\r\n]+/)
|
|
92
|
+
.map((line) => line.trim().replace(/\s+/g, " "))
|
|
93
|
+
.filter((line) => /^at\s+\S/.test(line) || /^[^@]*@.+:\d+(?::\d+)?$/.test(line));
|
|
94
|
+
const selected = frames.slice(0, 3);
|
|
95
|
+
const composition = frames.find(isCompositionFrame);
|
|
96
|
+
if (composition && !selected.includes(composition))
|
|
97
|
+
selected.push(composition);
|
|
98
|
+
return selected.length ? ` [thrown at: ${selected.join(" <- ")}]` : "";
|
|
99
|
+
}
|
|
100
|
+
catch (err) {
|
|
101
|
+
swallow("runtime.startupError.stack", err);
|
|
102
|
+
return "";
|
|
103
|
+
}
|
|
104
|
+
}
|
package/dist/runtime/media.d.ts
CHANGED
|
@@ -81,6 +81,7 @@ export declare function evictMediaSyncState(el: HTMLMediaElement): void;
|
|
|
81
81
|
export declare function hasMediaSyncStateForTest(el: HTMLMediaElement): boolean;
|
|
82
82
|
export declare const MEDIA_HARD_SYNC_SECONDS = 0.5;
|
|
83
83
|
export declare const VIDEO_PRESEEK_SECONDS = 1.5;
|
|
84
|
+
export declare function mediaSourceTimeAt(clip: RuntimeMediaClip, timeSeconds: number): number;
|
|
84
85
|
/** Drift a playing audio element may carry before sync pulls it back onto the playhead. */
|
|
85
86
|
export declare const MEDIA_SYNC_TOLERANCE_SECONDS = 0.04;
|
|
86
87
|
export declare function syncRuntimeMedia(params: {
|
package/dist/runtime/media.js
CHANGED
|
@@ -193,6 +193,19 @@ export function hasMediaSyncStateForTest(el) {
|
|
|
193
193
|
}
|
|
194
194
|
export const MEDIA_HARD_SYNC_SECONDS = 0.5;
|
|
195
195
|
export const VIDEO_PRESEEK_SECONDS = 1.5;
|
|
196
|
+
export function mediaSourceTimeAt(clip, timeSeconds) {
|
|
197
|
+
const elapsed = Math.max(0, Math.min(timeSeconds, clip.end) - (clip.origin ?? clip.start));
|
|
198
|
+
const target = clip.mediaStart + sourceTimeAt(clip.rate ?? clip.playbackRate, elapsed);
|
|
199
|
+
if (clip.sourceDuration == null)
|
|
200
|
+
return target;
|
|
201
|
+
const loopLength = clip.sourceDuration - clip.mediaStart;
|
|
202
|
+
if (clip.loop && loopLength > 0 && target >= clip.sourceDuration)
|
|
203
|
+
return clip.mediaStart + ((target - clip.mediaStart) % loopLength);
|
|
204
|
+
// A non-loop video past its source holds the last frame.
|
|
205
|
+
if (!clip.loop && clip.el.tagName === "VIDEO")
|
|
206
|
+
return Math.min(target, clip.sourceDuration);
|
|
207
|
+
return target;
|
|
208
|
+
}
|
|
196
209
|
function prepareUpcomingVideo(clip, time, playing, rate) {
|
|
197
210
|
const { el } = clip;
|
|
198
211
|
const dueIn = clip.start - time;
|
|
@@ -206,15 +219,7 @@ function prepareUpcomingVideo(clip, time, playing, rate) {
|
|
|
206
219
|
findInjectedRenderFrame(el) ||
|
|
207
220
|
("__HF_EXPORT_RENDER_SEEK_CONFIG" in window && window.__HF_EXPORT_RENDER_SEEK_CONFIG))
|
|
208
221
|
return;
|
|
209
|
-
|
|
210
|
-
sourceTimeAt(clip.rate ?? clip.playbackRate, clip.start - (clip.origin ?? clip.start));
|
|
211
|
-
if (clip.sourceDuration != null) {
|
|
212
|
-
const loopLength = clip.sourceDuration - clip.mediaStart;
|
|
213
|
-
if (clip.loop && loopLength > 0 && target >= clip.sourceDuration)
|
|
214
|
-
target = clip.mediaStart + ((target - clip.mediaStart) % loopLength);
|
|
215
|
-
else if (!clip.loop)
|
|
216
|
-
target = Math.min(target, clip.sourceDuration);
|
|
217
|
-
}
|
|
222
|
+
const target = mediaSourceTimeAt(clip, clip.start);
|
|
218
223
|
if (!Number.isFinite(target) || target < 0 || Math.abs(el.currentTime - target) <= 0.001)
|
|
219
224
|
return;
|
|
220
225
|
try {
|
|
@@ -282,13 +287,9 @@ export function syncRuntimeMedia(params) {
|
|
|
282
287
|
!inWindow &&
|
|
283
288
|
(params.timeSeconds >= clip.end || sameInstant(params.timeSeconds, clip.end)) &&
|
|
284
289
|
isClipVisibleAt(params.timeSeconds, clip.start, clip.end, params.getCompositionDuration());
|
|
285
|
-
|
|
286
|
-
clip.mediaStart;
|
|
290
|
+
const relTime = mediaSourceTimeAt(clip, params.timeSeconds);
|
|
287
291
|
const isHeldVideoTail = isTerminalVideo ||
|
|
288
292
|
(isNonLoopVideo && clip.sourceDuration != null && relTime >= clip.sourceDuration && inWindow);
|
|
289
|
-
if (isHeldVideoTail && clip.sourceDuration != null) {
|
|
290
|
-
relTime = Math.min(relTime, clip.sourceDuration);
|
|
291
|
-
}
|
|
292
293
|
const previousRelativeTime = lastRelativeTime.get(el);
|
|
293
294
|
const audioReenteredAfterBackwardSeek = el.tagName === "AUDIO" &&
|
|
294
295
|
(previousRelativeTime === undefined || relTime < previousRelativeTime - 0.04);
|
|
@@ -308,13 +309,6 @@ export function syncRuntimeMedia(params) {
|
|
|
308
309
|
(!el.ended || clip.loop || isHeldVideoTail || canSeekEndedMediaBackward);
|
|
309
310
|
if (isActive) {
|
|
310
311
|
lastRelativeTime.set(el, relTime);
|
|
311
|
-
// Loop wrapping: when media reaches end, restart from mediaStart
|
|
312
|
-
if (clip.loop && clip.sourceDuration != null && clip.sourceDuration > 0) {
|
|
313
|
-
const loopLength = clip.sourceDuration - clip.mediaStart;
|
|
314
|
-
if (loopLength > 0 && relTime >= clip.sourceDuration) {
|
|
315
|
-
relTime = clip.mediaStart + ((relTime - clip.mediaStart) % loopLength);
|
|
316
|
-
}
|
|
317
|
-
}
|
|
318
312
|
const userVol = clampVolume(params.userVolume ?? 1);
|
|
319
313
|
const fallbackAuthorVolume = clampAudioGain(clip.volume ?? 1);
|
|
320
314
|
const previousRuntimeVolume = lastRuntimeAppliedVolume.get(el);
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { CLIP_HIDDEN_ATTR } from "./clipReveal.js";
|
|
2
2
|
export { CLIP_HIDDEN_ATTR };
|
|
3
3
|
export declare const SKIPPED_CLIP = "[data-start]:not(video, audio, img, [data-hf-upcoming], [data-hf-autostamped])[data-hf-clip-hidden]";
|
|
4
|
-
export declare function showHiddenClips(): () => void;
|
|
4
|
+
export declare function showHiddenClips(only?: (el: HTMLElement) => boolean): () => void;
|
|
5
|
+
export declare function showClips(hidden: Array<Element & ElementCSSInlineStyle>): () => void;
|
|
5
6
|
export declare function defersMedia(): boolean;
|
|
6
7
|
/** True in Studio's preview, where hidden clips' images are neither fetched nor decoded until due. */
|
|
7
8
|
export declare function skipsHiddenImages(): boolean;
|
|
@@ -21,12 +21,16 @@ const hideHiddenClipContents = (clip) => {
|
|
|
21
21
|
.join(", ");
|
|
22
22
|
return `${clip} * { visibility: hidden !important; } ${filterOutputLayers} { filter: none !important; }`;
|
|
23
23
|
};
|
|
24
|
-
export function showHiddenClips() {
|
|
25
|
-
|
|
24
|
+
export function showHiddenClips(only = () => true) {
|
|
25
|
+
return showClips(Array.from(document.querySelectorAll(`[${CLIP_HIDDEN_ATTR}]`)).filter(only));
|
|
26
|
+
}
|
|
27
|
+
export function showClips(hidden) {
|
|
26
28
|
const putMarksBack = hidden.map((el) => revealClip(el));
|
|
27
29
|
return () => {
|
|
28
|
-
for (const el of hidden)
|
|
29
|
-
el.style.visibility
|
|
30
|
+
for (const el of hidden) {
|
|
31
|
+
if (el.style.getPropertyValue("visibility") !== "hidden")
|
|
32
|
+
el.style.setProperty("visibility", "hidden", "important");
|
|
33
|
+
}
|
|
30
34
|
for (const putMarkBack of putMarksBack)
|
|
31
35
|
putMarkBack();
|
|
32
36
|
};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { RuntimeTimelineMessage, RuntimeTimelineLike } from "./types.js";
|
|
1
|
+
import type { RuntimeTimelineClip, RuntimeTimelineMessage, RuntimeTimelineLike } from "./types.js";
|
|
2
2
|
import { createRuntimeStartTimeResolver } from "./startResolver.js";
|
|
3
3
|
export declare function runtimeElementWindow(rawNode: Element, options: {
|
|
4
4
|
timelineRegistry: Record<string, RuntimeTimelineLike | undefined>;
|
|
@@ -15,12 +15,19 @@ export declare function isRuntimeElementVisibleAt(rawNode: Element, options: {
|
|
|
15
15
|
timelineRegistry: Record<string, RuntimeTimelineLike | undefined>;
|
|
16
16
|
resolver: ReturnType<typeof createRuntimeStartTimeResolver>;
|
|
17
17
|
}): boolean;
|
|
18
|
+
export declare function parseElementDurationAttr(element: Element): number | null;
|
|
18
19
|
/**
|
|
19
20
|
* Parse an authored track attribute, honoring 0 (a valid top-lane index).
|
|
20
21
|
* `parseInt(...) || fallback` silently replaced authored track 0 with the
|
|
21
22
|
* synthetic fallback, so track-0 clips drifted to the bottom of the timeline.
|
|
22
23
|
*/
|
|
23
24
|
export declare function parseAuthoredTrack(el: Element, fallback: number): number;
|
|
25
|
+
export type ParkedTimelineScene = {
|
|
26
|
+
name: string;
|
|
27
|
+
host: () => Element;
|
|
28
|
+
};
|
|
24
29
|
export declare function collectRuntimeTimelinePayload(params: {
|
|
25
30
|
canonicalFps: number;
|
|
31
|
+
parked?: ParkedTimelineScene[];
|
|
32
|
+
parkedClips?: Map<string, RuntimeTimelineClip[]>;
|
|
26
33
|
}): RuntimeTimelineMessage;
|
package/dist/runtime/timeline.js
CHANGED
|
@@ -40,9 +40,10 @@ export function runtimeElementWindow(rawNode, options) {
|
|
|
40
40
|
duration = liveDuration;
|
|
41
41
|
}
|
|
42
42
|
}
|
|
43
|
+
const isRoot = compId !== null && rawNode === findRootCompositionElement(rawNode.ownerDocument);
|
|
43
44
|
return {
|
|
44
45
|
start,
|
|
45
|
-
end: duration != null && duration > 0 ? start + duration : Number.POSITIVE_INFINITY,
|
|
46
|
+
end: !isRoot && duration != null && duration > 0 ? start + duration : Number.POSITIVE_INFINITY,
|
|
46
47
|
};
|
|
47
48
|
}
|
|
48
49
|
export function isRuntimeElementVisibleAt(rawNode, options) {
|
|
@@ -58,7 +59,7 @@ export function isRuntimeElementVisibleAt(rawNode, options) {
|
|
|
58
59
|
function parseNum(value) {
|
|
59
60
|
return parseStrictFiniteTimingNumber(value);
|
|
60
61
|
}
|
|
61
|
-
function parseElementDurationAttr(element) {
|
|
62
|
+
export function parseElementDurationAttr(element) {
|
|
62
63
|
const publicDuration = element.getAttribute("data-duration");
|
|
63
64
|
const authoredDuration = element.getAttribute(AUTHORED_DURATION_ATTR);
|
|
64
65
|
const resolved = resolveAuthoredTimingWindow({
|
|
@@ -353,13 +354,14 @@ export function collectRuntimeTimelinePayload(params) {
|
|
|
353
354
|
// as clips — they have no declared timing so the timeline can't show their
|
|
354
355
|
// actual visibility window. They can still appear as scenes via the separate
|
|
355
356
|
// scene collection below.
|
|
356
|
-
const
|
|
357
|
+
const CLIP_NODES = "[data-start], [data-track-index], [data-composition-id], video, audio, img";
|
|
358
|
+
const nodes = Array.from(document.querySelectorAll(CLIP_NODES));
|
|
357
359
|
let maxEnd = 0;
|
|
358
|
-
|
|
360
|
+
const clipOf = (node, i) => {
|
|
359
361
|
if (node === root)
|
|
360
|
-
|
|
362
|
+
return null;
|
|
361
363
|
if (["SCRIPT", "STYLE", "LINK", "META", "TEMPLATE", "NOSCRIPT"].includes(node.tagName))
|
|
362
|
-
|
|
364
|
+
return null;
|
|
363
365
|
const compositionContext = resolveNearestCompositionContext(node, root);
|
|
364
366
|
const tag = node.tagName.toLowerCase();
|
|
365
367
|
let start = tag === "video" || tag === "audio"
|
|
@@ -387,21 +389,19 @@ export function collectRuntimeTimelinePayload(params) {
|
|
|
387
389
|
}
|
|
388
390
|
}
|
|
389
391
|
if (duration == null || duration <= 0)
|
|
390
|
-
|
|
392
|
+
return null;
|
|
391
393
|
duration = clampDurationToRootWindow(start, duration);
|
|
392
394
|
if (duration <= 0)
|
|
393
|
-
|
|
395
|
+
return null;
|
|
394
396
|
const slots = startResolver.resolveHostSlotsForElement(node);
|
|
395
397
|
if (slots.length > 0) {
|
|
396
398
|
const played = cutToHostSlots({ start, end: start + duration }, slots);
|
|
397
399
|
const keepZeroLengthSoStudioNestsChildren = isNestedHost;
|
|
398
400
|
if (played.end <= played.start && !keepZeroLengthSoStudioNestsChildren)
|
|
399
|
-
|
|
401
|
+
return null;
|
|
400
402
|
start = played.start;
|
|
401
403
|
duration = played.end - played.start;
|
|
402
404
|
}
|
|
403
|
-
const end = start + duration;
|
|
404
|
-
maxEnd = Math.max(maxEnd, end);
|
|
405
405
|
const kind = nodeCompositionId && nodeCompositionId !== rootCompositionId
|
|
406
406
|
? "composition"
|
|
407
407
|
: tag === "video"
|
|
@@ -411,7 +411,7 @@ export function collectRuntimeTimelinePayload(params) {
|
|
|
411
411
|
: tag === "img"
|
|
412
412
|
? "image"
|
|
413
413
|
: "element";
|
|
414
|
-
|
|
414
|
+
return {
|
|
415
415
|
id: stableClipId(node) ?? nodeCompositionId ?? null,
|
|
416
416
|
label: buildTimelineClipLabel(node, kind, clips.length),
|
|
417
417
|
start,
|
|
@@ -433,7 +433,26 @@ export function collectRuntimeTimelinePayload(params) {
|
|
|
433
433
|
timelineLabel: node.getAttribute("data-timeline-label"),
|
|
434
434
|
timelineGroup: node.getAttribute("data-timeline-group"),
|
|
435
435
|
timelinePriority: parseNum(node.getAttribute("data-timeline-priority")),
|
|
436
|
-
}
|
|
436
|
+
};
|
|
437
|
+
};
|
|
438
|
+
const addClip = (clip) => {
|
|
439
|
+
clips.push(clip);
|
|
440
|
+
maxEnd = Math.max(maxEnd, clip.start + clip.duration);
|
|
441
|
+
};
|
|
442
|
+
for (const [i, node] of nodes.entries()) {
|
|
443
|
+
const clip = clipOf(node, i);
|
|
444
|
+
if (clip)
|
|
445
|
+
addClip(clip);
|
|
446
|
+
}
|
|
447
|
+
for (const scene of params.parked ?? []) {
|
|
448
|
+
let parkedClips = params.parkedClips?.get(scene.name);
|
|
449
|
+
if (!parkedClips) {
|
|
450
|
+
const offset = nodes.length;
|
|
451
|
+
parkedClips = Array.from(scene.host().querySelectorAll(CLIP_NODES)).flatMap((node, j) => clipOf(node, offset + j) ?? []);
|
|
452
|
+
params.parkedClips?.set(scene.name, parkedClips);
|
|
453
|
+
}
|
|
454
|
+
for (const clip of parkedClips)
|
|
455
|
+
addClip(clip);
|
|
437
456
|
}
|
|
438
457
|
// ── GSAP introspection ──────────────────────────────────────────────────
|
|
439
458
|
// Discover elements animated by GSAP that weren't picked up by the DOM query
|
package/dist/runtime/types.d.ts
CHANGED
|
@@ -6,7 +6,7 @@ export type RuntimeJson = string | number | boolean | null | RuntimeJson[] | {
|
|
|
6
6
|
import type { HyperframeControlAction } from "../inline-scripts/runtimeContract.js";
|
|
7
7
|
import type { HyperframePickerElementInfo } from "../inline-scripts/pickerApi.js";
|
|
8
8
|
import type { RuntimeProtocolV1 } from "./protocol.js";
|
|
9
|
-
type RuntimeBridgeControlActionBase = HyperframeControlAction | "tick" | "set-volume" | "set-media-output-muted" | "set-native-media-sync-disabled" | "set-web-audio-media-disabled" | "set-idle-heartbeat" | "set-display-scale" | "set-root-duration" | "set-play-range" | "stop-media" | "flash-elements";
|
|
9
|
+
type RuntimeBridgeControlActionBase = HyperframeControlAction | "tick" | "set-volume" | "set-media-output-muted" | "set-native-media-sync-disabled" | "set-web-audio-media-disabled" | "set-idle-heartbeat" | "set-display-scale" | "set-preview-quality" | "set-root-duration" | "set-play-range" | "stop-media" | "flash-elements";
|
|
10
10
|
type RuntimeBridgeControlMessageBase = {
|
|
11
11
|
source: "hf-parent";
|
|
12
12
|
type: "control";
|
|
@@ -21,6 +21,7 @@ type RuntimeBridgeControlMessageBase = {
|
|
|
21
21
|
disabled?: boolean;
|
|
22
22
|
slow?: boolean;
|
|
23
23
|
scale?: number;
|
|
24
|
+
level?: number;
|
|
24
25
|
playbackRate?: number;
|
|
25
26
|
target?: HfColorGradingTarget | string | null;
|
|
26
27
|
grading?: RuntimeJson;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { isFontValue, isImageValue } from "@hyperframes/parsers/composition";
|
|
1
2
|
/**
|
|
2
3
|
* Compare a flat values map (from `--variables` / `data-variable-values`) to
|
|
3
4
|
* the declared schema (`data-composition-variables`). Returns issues for keys
|
|
@@ -86,10 +87,7 @@ function checkType(value, decl) {
|
|
|
86
87
|
actual: jsTypeOf(value),
|
|
87
88
|
};
|
|
88
89
|
}
|
|
89
|
-
|
|
90
|
-
// value ({name: 42} / {}) is caught here rather than surfacing as a bogus
|
|
91
|
-
// font-family at render time.
|
|
92
|
-
if (typeof value.name !== "string" || typeof value.source !== "string") {
|
|
90
|
+
if (!isFontValue(value)) {
|
|
93
91
|
return {
|
|
94
92
|
kind: "type-mismatch",
|
|
95
93
|
variableId: decl.id,
|
|
@@ -111,13 +109,14 @@ function checkType(value, decl) {
|
|
|
111
109
|
actual: jsTypeOf(value),
|
|
112
110
|
};
|
|
113
111
|
}
|
|
114
|
-
|
|
115
|
-
if (typeof value.url !== "string") {
|
|
112
|
+
if (!isImageValue(value)) {
|
|
116
113
|
return {
|
|
117
114
|
kind: "type-mismatch",
|
|
118
115
|
variableId: decl.id,
|
|
119
116
|
expected: "image object {url: string}",
|
|
120
|
-
actual:
|
|
117
|
+
actual: typeof value.url !== "string"
|
|
118
|
+
? "object missing string url"
|
|
119
|
+
: "object with invalid alt/fit",
|
|
121
120
|
};
|
|
122
121
|
}
|
|
123
122
|
return null;
|
|
@@ -5,5 +5,7 @@ export declare const STUDIO_PREVIEW_LAZY_ATTR = "data-hf-preview-lazy";
|
|
|
5
5
|
export declare const STUDIO_PREVIEW_ERRORS = "__hfPreviewErrors";
|
|
6
6
|
export declare const PREVIEW_RASTER_ATTR = "data-hf-preview-raster";
|
|
7
7
|
export declare const RUNTIME_STAMPED_ATTR = "data-hf-autostamped";
|
|
8
|
-
export declare const
|
|
8
|
+
export declare const STUDIO_PREVIEW_FAR_SCENE_ATTR = "data-hf-far-scene";
|
|
9
|
+
export declare const STUDIO_PREVIEW_PARKED_SCENE_ATTR = "data-hf-parked-scene";
|
|
10
|
+
export declare const PREVIEW_ONLY_ATTRS: readonly ["data-hf-upcoming", "data-hf-far-scene", "data-hf-parked-scene", "data-hf-preview-lazy", "data-hf-preview-raster", "data-hf-autostamped"];
|
|
9
11
|
export declare function applyAuthoredTiming(el: Element, attrs: ReadonlyArray<readonly [name: string, value: string]>): void;
|
|
@@ -5,8 +5,12 @@ export const STUDIO_PREVIEW_LAZY_ATTR = "data-hf-preview-lazy";
|
|
|
5
5
|
export const STUDIO_PREVIEW_ERRORS = "__hfPreviewErrors";
|
|
6
6
|
export const PREVIEW_RASTER_ATTR = "data-hf-preview-raster";
|
|
7
7
|
export const RUNTIME_STAMPED_ATTR = "data-hf-autostamped";
|
|
8
|
+
export const STUDIO_PREVIEW_FAR_SCENE_ATTR = "data-hf-far-scene";
|
|
9
|
+
export const STUDIO_PREVIEW_PARKED_SCENE_ATTR = "data-hf-parked-scene";
|
|
8
10
|
export const PREVIEW_ONLY_ATTRS = [
|
|
9
11
|
STUDIO_PREVIEW_UPCOMING_ATTR,
|
|
12
|
+
STUDIO_PREVIEW_FAR_SCENE_ATTR,
|
|
13
|
+
STUDIO_PREVIEW_PARKED_SCENE_ATTR,
|
|
10
14
|
STUDIO_PREVIEW_LAZY_ATTR,
|
|
11
15
|
PREVIEW_RASTER_ATTR,
|
|
12
16
|
RUNTIME_STAMPED_ATTR,
|
package/dist/variables.d.ts
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* the Node-only modules reachable from the core/parsers root entries, so
|
|
6
6
|
* browser consumers (SDK, Studio, embedders) can bundle it.
|
|
7
7
|
*/
|
|
8
|
-
export type { CompositionVariable, CompositionVariableType, CompositionVariableBase, StringVariable, NumberVariable, ColorVariable, BooleanVariable, EnumVariable, FontVariable, ImageVariable, } from "@hyperframes/parsers/composition";
|
|
8
|
+
export type { CompositionVariable, CompositionVariableType, CompositionVariableBase, StringVariable, NumberVariable, ColorVariable, BooleanVariable, EnumVariable, FontVariable, FontValue, ImageVariable, ImageValue, } from "@hyperframes/parsers/composition";
|
|
9
9
|
export { COMPOSITION_VARIABLE_TYPES, parseCompositionVariables, isCompositionVariable, isScalarVariableValue, scanVariableUsage, } from "@hyperframes/parsers/composition";
|
|
10
10
|
export type { VariableUsageScan } from "@hyperframes/parsers/composition";
|
|
11
11
|
export { getVariables, readDeclaredDefaults } from "./runtime/getVariables.js";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hyperframes/core",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.201",
|
|
4
4
|
"description": "",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -397,9 +397,9 @@
|
|
|
397
397
|
"linkedom": "^0.18.12",
|
|
398
398
|
"postcss": "^8.5.8",
|
|
399
399
|
"wawoff2": "^2.0.1",
|
|
400
|
-
"@hyperframes/lint": "0.8.
|
|
401
|
-
"@hyperframes/
|
|
402
|
-
"@hyperframes/
|
|
400
|
+
"@hyperframes/lint": "0.8.201",
|
|
401
|
+
"@hyperframes/studio-server": "0.8.201",
|
|
402
|
+
"@hyperframes/parsers": "0.8.201"
|
|
403
403
|
},
|
|
404
404
|
"devDependencies": {
|
|
405
405
|
"@fontsource/archivo-black": "^5.2.8",
|