hyperframes 0.8.145 → 0.8.146
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/{add-B6OUH46W.js → add-6J4TLUDW.js} +15 -15
- package/dist/{animatedGifPrep-MIWRDJIO.js → animatedGifPrep-TPYA45ZZ.js} +10 -10
- package/dist/{asset-XJ2B2PU3.js → asset-B4C6ZU4V.js} +2 -2
- package/dist/{assetCataloger-A5ZLSCMZ.js → assetCataloger-GX4B55VB.js} +4 -4
- package/dist/{auth-5HOVCZMV.js → auth-T2V3URGO.js} +2 -2
- package/dist/{autoUpdate-WVNL5K3D.js → autoUpdate-O4R7IOYP.js} +2 -2
- package/dist/{backgroundChecks-IGF54SFH.js → backgroundChecks-SQHGZCSH.js} +3 -3
- package/dist/backgroundChecksWorker.js +3 -3
- package/dist/{batchRender-T6JEGCQV.js → batchRender-T7S2CGJE.js} +5 -5
- package/dist/{beats-37T6Y6QP.js → beats-RNEXDM7U.js} +16 -16
- package/dist/{benchmark-ZVVLOWPP.js → benchmark-IVK6FCEU.js} +12 -12
- package/dist/{browser-4SSJOLH4.js → browser-WUE6VK2N.js} +5 -5
- package/dist/{bundleWithLocalizedFonts-XWK423QP.js → bundleWithLocalizedFonts-EEIRL7FT.js} +2 -2
- package/dist/{capture-2SF7YP57.js → capture-K3EONGGU.js} +2 -2
- package/dist/{capture-KZXOPBMO.js → capture-LEKIGJ4G.js} +13 -13
- package/dist/{catalog-NLD4A2C6.js → catalog-IXYQ2KXW.js} +15 -15
- package/dist/{catch-up-4BSRN6IR.js → catch-up-IIF7HKR6.js} +6 -6
- package/dist/{check-IA44QQWM.js → check-W7CDOXNP.js} +38 -38
- package/dist/{checkBrowser-VM35KZMW.js → checkBrowser-5VSXEHBZ.js} +118 -61
- package/dist/{chunk-ZF7SXQJ7.js → chunk-2NAUEDA6.js} +2 -2
- package/dist/{chunk-FXJJCPPB.js → chunk-3HSWLZ6S.js} +1 -1
- package/dist/{chunk-ZSVUAVMT.js → chunk-4RUILXS6.js} +5 -5
- package/dist/{chunk-FBCULCAL.js → chunk-54UXJKFF.js} +1 -1
- package/dist/{chunk-DIRJ322I.js → chunk-66SAATRI.js} +3 -3
- package/dist/{chunk-QLSM7K5S.js → chunk-6TTAXMDG.js} +2 -2
- package/dist/{chunk-5WVW7P23.js → chunk-A72OAGNB.js} +6 -6
- package/dist/{chunk-G75ENCEZ.js → chunk-AIJS74LG.js} +3 -3
- package/dist/{chunk-HB7FJOY4.js → chunk-AJ5R4GM6.js} +1 -1
- package/dist/{chunk-VRW6EBHV.js → chunk-BAKPN2D2.js} +101 -94
- package/dist/{chunk-OTHDVXOJ.js → chunk-CVPWZ5LY.js} +3 -3
- package/dist/{chunk-BIAC7IJT.js → chunk-DDNS2674.js} +1 -1
- package/dist/{chunk-QFSCCQIR.js → chunk-DMKI64EZ.js} +1 -1
- package/dist/{chunk-EPE2HRJK.js → chunk-DUU77E2S.js} +69 -64
- package/dist/{chunk-ZIYZ77MR.js → chunk-EGO6Z24R.js} +3 -3
- package/dist/{chunk-KC5XXX3X.js → chunk-EOOEC32M.js} +1 -1
- package/dist/{chunk-3IBMBNYY.js → chunk-ETNQF37F.js} +10 -10
- package/dist/{chunk-XIHCMVMG.js → chunk-FDL4HV44.js} +2 -2
- package/dist/{chunk-GBGJ7A3D.js → chunk-FJLHVAPO.js} +6 -6
- package/dist/{chunk-QZ3AZTXI.js → chunk-FJWEYVEM.js} +42 -16
- package/dist/{chunk-IHTRYDFN.js → chunk-FKLYMV3U.js} +1 -1
- package/dist/{chunk-PUGIRFPX.js → chunk-FMNVTKX2.js} +4 -4
- package/dist/{chunk-GEMJYBRU.js → chunk-HBGJYBGE.js} +2 -2
- package/dist/{chunk-U5QY7KTN.js → chunk-HTM4CIFS.js} +11 -8
- package/dist/{chunk-T42U2W2R.js → chunk-IXLUTE6N.js} +1 -1
- package/dist/{chunk-FDT6KQV4.js → chunk-JBQJKC23.js} +2 -2
- package/dist/{chunk-IEIJRMLZ.js → chunk-JDB65PPT.js} +62 -63
- package/dist/{chunk-USDEMXXL.js → chunk-JYVGYKXG.js} +1 -1
- package/dist/{chunk-PLR7Z6TC.js → chunk-K7ES5RSK.js} +1 -1
- package/dist/{chunk-VHFEMFYL.js → chunk-KFZUUXUN.js} +3 -3
- package/dist/{chunk-GRY3PN3K.js → chunk-KG265HQ7.js} +2 -2
- package/dist/{chunk-R42WWQVB.js → chunk-KPJMBC4K.js} +2 -2
- package/dist/{chunk-MAYCCGMQ.js → chunk-KVGDFUVO.js} +1 -1
- package/dist/{chunk-CFUXNBWH.js → chunk-LE7T4ML7.js} +1 -1
- package/dist/{chunk-MQZVBHEY.js → chunk-LFNQDTSH.js} +1 -1
- package/dist/{chunk-KIFHYXPB.js → chunk-MOL32UKR.js} +45 -11
- package/dist/{chunk-ICKDMTLA.js → chunk-QSSPIATO.js} +2 -2
- package/dist/{chunk-3KU3PFDW.js → chunk-R6R74HNZ.js} +74 -66
- package/dist/{chunk-ADF3X6GM.js → chunk-SYMSKGXI.js} +2 -2
- package/dist/{chunk-ZEUONDQG.js → chunk-SYUGVPYH.js} +1 -1
- package/dist/{chunk-BFACGEII.js → chunk-TBOMPYG6.js} +2 -2
- package/dist/{chunk-XBX4QJP7.js → chunk-TH4OXAWY.js} +1 -1
- package/dist/{chunk-GMAJB2P6.js → chunk-UOBYRLDD.js} +1 -1
- package/dist/{chunk-4QWD6JYN.js → chunk-UZUUVYAO.js} +3 -3
- package/dist/{chunk-PJ3HDUCY.js → chunk-VAS6HQX3.js} +39 -9
- package/dist/{chunk-6LGNTUZN.js → chunk-VT563F4O.js} +7 -7
- package/dist/{chunk-UXO2L7HJ.js → chunk-WU2B74TL.js} +267 -65
- package/dist/{chunk-OB5MJR4W.js → chunk-WUZ6TMAO.js} +1 -1
- package/dist/{chunk-GQJZORQP.js → chunk-XBSPQ52S.js} +1 -1
- package/dist/{chunk-4ORLNTET.js → chunk-XJUZEB44.js} +1 -1
- package/dist/{chunk-LAFJSYDL.js → chunk-XVIYZU3D.js} +1 -1
- package/dist/{chunk-GXJEGZL2.js → chunk-Z5C4LEAD.js} +17 -18
- package/dist/chunk-Z6FLHIDV.js +3795 -0
- package/dist/{chunk-DDIVWXN7.js → chunk-ZFN6GQU4.js} +4 -4
- package/dist/{chunk-DWUKJQTO.js → chunk-ZVZSFDW3.js} +2 -2
- package/dist/{chunk-5NRXZGKK.js → chunk-ZZ7H4IJ6.js} +9 -9
- package/dist/{clean-RDKJB5FK.js → clean-2GDD2B4F.js} +23 -23
- package/dist/cli.js +52 -52
- package/dist/{cloud-QVPVVNCW.js → cloud-LUKGDTTE.js} +4 -4
- package/dist/{cloudrun-3RE2UCQG.js → cloudrun-NQF5KFQI.js} +6 -6
- package/dist/{compare-LEDOY7MO.js → compare-4P6O4ONG.js} +32 -32
- package/dist/{compiler-F54OZZPM.js → compiler-ACCPOZMQ.js} +6 -6
- package/dist/{component-N42M67WY.js → component-SDDOLNJD.js} +3 -3
- package/dist/{compositions-VB2J2LZI.js → compositions-P2L6HUVQ.js} +15 -15
- package/dist/{delete-Q6RWMJ4Z.js → delete-Q6S4BJ3F.js} +2 -2
- package/dist/{deterministicFonts-U2BIMGPG.js → deterministicFonts-C5JQLCTS.js} +2 -2
- package/dist/{dist-Q4BI5A73.js → dist-GPDOV3IQ.js} +4 -4
- package/dist/{dist-KDJTFJHA.js → dist-HUMFCJ6T.js} +3 -3
- package/dist/{doctor-JMMUHDRV.js → doctor-NL73UKHV.js} +15 -15
- package/dist/{events-RGUAHIBW.js → events-AVOPOKO3.js} +5 -5
- package/dist/{events-6ZBSIBE5.js → events-X7KPU7LG.js} +4 -4
- package/dist/{feedback-FIID6MZW.js → feedback-GJZ5NXSH.js} +10 -10
- package/dist/{ffmpeg-T5SH2EFJ.js → ffmpeg-5V4FP3R6.js} +2 -2
- package/dist/{figma-BL3JJC36.js → figma-V7SVQQI5.js} +3 -3
- package/dist/fontLocalizeCli.js +14 -14
- package/dist/{get-P46GS7S6.js → get-FO3YZYEU.js} +2 -2
- package/dist/{gpuPolicy-AQ2BPLBK.js → gpuPolicy-QGS4TFWP.js} +10 -10
- package/dist/{grade-compare-GDCWU7ZZ.js → grade-compare-7ICSN7KE.js} +33 -33
- package/dist/{help-HJR3RWFG.js → help-Y5LYZG7K.js} +2 -2
- package/dist/{history-GGO5AWJB.js → history-RKGBRNLD.js} +49 -22
- package/dist/hyperframe-runtime.js +214 -68
- package/dist/hyperframe.manifest.json +1 -1
- package/dist/hyperframe.runtime.iife.js +214 -68
- package/dist/hyperframes-player.global.js +4 -5
- package/dist/hyperframes-slideshow.global.js +3 -4
- package/dist/{info-3GFWUBJC.js → info-GGUTL2NR.js} +11 -11
- package/dist/{init-36OPU2ZG.js → init-PG7NA5SQ.js} +31 -31
- package/dist/{inspect-F2HEL5ON.js → inspect-FCXUB6DE.js} +33 -33
- package/dist/{installer-NI6P6GC4.js → installer-LZ4X5TZZ.js} +6 -6
- package/dist/{keyframes-7OY3LMAZ.js → keyframes-R3K6WJ3N.js} +8 -8
- package/dist/{lambda-6GJIWNJ3.js → lambda-3GAKHMC3.js} +7 -7
- package/dist/{layout-FTMLNL5A.js → layout-XNIZIGA2.js} +33 -33
- package/dist/{lint-U6POHUON.js → lint-4KDS2F4O.js} +11 -11
- package/dist/{lintProject-UBZ3TDHU.js → lintProject-SSEXWCH6.js} +3 -3
- package/dist/{list-IP5KI5Q2.js → list-OVPURFPK.js} +2 -2
- package/dist/{login-4VWTVPHG.js → login-EE2AX2PE.js} +4 -4
- package/dist/{manager-YKMHR6LN.js → manager-5ORDVWJB.js} +3 -3
- package/dist/{media-treatment-XGV44AGV.js → media-treatment-UDUEOP67.js} +82 -54
- package/dist/{models-UCXB6UR5.js → models-LPK2O4B2.js} +14 -14
- package/dist/{motionShot-WPVNBXKA.js → motionShot-G76XBOYI.js} +35 -29
- package/dist/{normalize-audio-TYJ3UEHO.js → normalize-audio-TUKWZ3OR.js} +10 -10
- package/dist/{open-HENOLVB2.js → open-7XB2TGVB.js} +6 -6
- package/dist/{parakeetRunner-VFQ6WFTD.js → parakeetRunner-DL556HXL.js} +14 -14
- package/dist/{pipeline-6WPSC3H4.js → pipeline-34N5DLWU.js} +11 -11
- package/dist/{play-OI53WNUM.js → play-DDA2UUQF.js} +26 -26
- package/dist/{preflight-B4AFK3RG.js → preflight-ONF5AJNO.js} +5 -5
- package/dist/{present-NGMEQ4NO.js → present-LLNRKXF5.js} +23 -23
- package/dist/{preview-VT2HWRWZ.js → preview-H3ZZS46F.js} +57 -26
- package/dist/{producer-7TYEAUF7.js → producer-IFQOZK2G.js} +1 -1
- package/dist/{projectConfig-B2AF5DXY.js → projectConfig-SBRMQZGG.js} +8 -8
- package/dist/{publication-2PANQVC6.js → publication-5BQICRZO.js} +4 -4
- package/dist/{publish-O5OL42BG.js → publish-XDUZA3E3.js} +27 -26
- package/dist/{remove-background-T2JM6UVP.js → remove-background-GNKRMFQS.js} +12 -12
- package/dist/{render-Z253WHBK.js → render-JM6KNRIA.js} +27 -27
- package/dist/{render-OLPKOJRU.js → render-KTTC3EE5.js} +12 -12
- package/dist/{render-W7OFXDOJ.js → render-PCFO45RK.js} +5 -5
- package/dist/{render-batch-46IIBFKL.js → render-batch-7R2NRNVJ.js} +5 -5
- package/dist/{renderCancellation-QTZOCDJ2.js → renderCancellation-GP3XD2FS.js} +10 -10
- package/dist/renderSetupWorker.js +3 -3
- package/dist/{resolver-J2PL5FVJ.js → resolver-C4FPX72I.js} +6 -6
- package/dist/{sherpa-ZF42V3RN.js → sherpa-MZP3MV2X.js} +14 -14
- package/dist/skills/media-use/references/media-treatments.md +5 -1
- package/dist/{skills-ACDOO3PX.js → skills-OET4XSHR.js} +7 -7
- package/dist/{skillsUpdateCheck-L5OXVVPU.js → skillsUpdateCheck-IPFSKKJD.js} +3 -3
- package/dist/{slideshow-4OJEPWNZ.js → slideshow-YTWATJRT.js} +1 -1
- package/dist/{snapshot-UUVNZOW5.js → snapshot-EC4N75MS.js} +34 -34
- package/dist/{src-BSLKXEGX.js → src-LIERUCFW.js} +11 -9
- package/dist/{src-KS3AWFGT.js → src-ZZQJOKO6.js} +13 -13
- package/dist/{staticProjectServer-2TTVGPQH.js → staticProjectServer-YH2RHYKW.js} +20 -20
- package/dist/{status-LXPGSSKY.js → status-PTKNQD2E.js} +2 -2
- package/dist/studio/assets/{hyperframes-player-DZ5JrMDg.js → hyperframes-player-tkxyWf5H.js} +3 -3
- package/dist/studio/assets/{index-BqFRhNZa.js → index-BrdqOkqP.js} +1 -1
- package/dist/studio/assets/{index-D8W0a0Z9.js → index-Cg48BxAo.js} +135 -135
- package/dist/studio/assets/{index-BOs6C0aZ.js → index-Dllxg4aU.js} +1 -1
- package/dist/studio/index.html +1 -1
- package/dist/{studio-api-WYWF7XRC.js → studio-api-T2TS2CHL.js} +16 -14
- package/dist/{studioServer-3FG4YTP7.js → studioServer-GJVVJUEL.js} +50 -50
- package/dist/{system-OQLOGGPJ.js → system-OXRG57F2.js} +2 -2
- package/dist/{telemetry-PAOOB4T4.js → telemetry-ZQ5XVE6Z.js} +5 -5
- package/dist/{timeline-JIIJKKPC.js → timeline-I2T5WIPD.js} +34 -25
- package/dist/{tokens-3QRAM75Z.js → tokens-ONUWOXEX.js} +2 -2
- package/dist/{transcribe-QSBP5MUG.js → transcribe-BCQMA537.js} +12 -12
- package/dist/{transcribe-SZB6UMXE.js → transcribe-ZLJF7V5Y.js} +13 -13
- package/dist/{updateCheck-V3DSB26K.js → updateCheck-ZWHDXF73.js} +2 -2
- package/dist/{upgrade-L5GISOEV.js → upgrade-32JGFZWS.js} +2 -2
- package/dist/{urlDownloader-52FERT4X.js → urlDownloader-EVDP43GM.js} +9 -9
- package/dist/{validate-HVVLX7KO.js → validate-G4PUGQCI.js} +27 -27
- package/dist/{video-MEOWRMFG.js → video-TILEY3VJ.js} +10 -10
- package/package.json +3 -2
- package/dist/chunk-AUWHD7RA.js +0 -3753
- package/dist/studio/chunk-6JY3MTIX.js +0 -1204
- package/dist/studio/chunk-EEXSYY5F.js +0 -190
- package/dist/studio/chunk-NZPJUZKT.js +0 -1538
- package/dist/studio/chunk-PZ5AY32C.js +0 -10
- package/dist/studio/chunk-TP6D3U37.js +0 -845
- package/dist/studio/chunk-Y2OHHNM2.js +0 -970
- package/dist/studio/domEditingLayers-PTP2ICGM.js +0 -47
- package/dist/studio/index.css +0 -165
- package/dist/studio/index.d.ts +0 -4318
- package/dist/studio/index.js +0 -100952
- package/dist/studio/player.d.ts +0 -30
- package/dist/studio/player.js +0 -10
- package/dist/studio/styles/tailwind-preset.d.ts +0 -39
- package/dist/studio/styles/tailwind-preset.js +0 -50
- package/dist/studio/styles.css +0 -1
- package/dist/studio/ui.d.ts +0 -297
- package/dist/studio/ui.js +0 -66
|
@@ -28,7 +28,7 @@ import {
|
|
|
28
28
|
} from "./chunk-APDLWWFG.js";
|
|
29
29
|
import {
|
|
30
30
|
findFfBinary
|
|
31
|
-
} from "./chunk-
|
|
31
|
+
} from "./chunk-VAS6HQX3.js";
|
|
32
32
|
import {
|
|
33
33
|
COMPOSITION_ATTRIBUTES,
|
|
34
34
|
checkSubCompositionUsability,
|
|
@@ -54,7 +54,7 @@ import {
|
|
|
54
54
|
isSceneLikeCompositionId,
|
|
55
55
|
parseSlideshowManifest,
|
|
56
56
|
resolveSlideshow
|
|
57
|
-
} from "./chunk-
|
|
57
|
+
} from "./chunk-IXLUTE6N.js";
|
|
58
58
|
import {
|
|
59
59
|
__commonJS,
|
|
60
60
|
__require,
|
|
@@ -4362,7 +4362,10 @@ function truncateSnippet(value, maxLength = 220) {
|
|
|
4362
4362
|
return `${normalized2.slice(0, maxLength - 3)}...`;
|
|
4363
4363
|
}
|
|
4364
4364
|
function mediaSrcTagRe(tagAlternation) {
|
|
4365
|
-
return new RegExp(`<(${tagAlternation})\\b[^>]*\\ssrc\\s*=\\s*["'
|
|
4365
|
+
return new RegExp(`<(${tagAlternation})\\b[^>]*\\ssrc\\s*=\\s*("[^"]*"|'[^']*')[^>]*>`, "gi");
|
|
4366
|
+
}
|
|
4367
|
+
function mediaSrcOf(match) {
|
|
4368
|
+
return readDecodedAttr(match[0], "src") ?? "";
|
|
4366
4369
|
}
|
|
4367
4370
|
function normalized(source) {
|
|
4368
4371
|
const chars = [];
|
|
@@ -4828,7 +4831,7 @@ function describeRootDimensionsDrift(source, dataWidth, dataHeight) {
|
|
|
4828
4831
|
const declared = `Root composition declares data-width="${dataWidth}" data-height="${dataHeight}"`;
|
|
4829
4832
|
if (!bodyCssMismatch) {
|
|
4830
4833
|
return {
|
|
4831
|
-
message: `${declared}, but ${viewportMismatch}. The viewport meta has no effect on capture
|
|
4834
|
+
message: `${declared}, but ${viewportMismatch}. The viewport meta has no effect on capture (the renderer sizes the viewport from the root's own data-width/data-height), so this is stale metadata, not a clipping risk.`,
|
|
4832
4835
|
fixHint: "update the meta viewport to match, or scaffold with `hyperframes init --resolution portrait`"
|
|
4833
4836
|
};
|
|
4834
4837
|
}
|
|
@@ -5100,7 +5103,7 @@ var coreRules = [
|
|
|
5100
5103
|
findings.push({
|
|
5101
5104
|
code: "unclosed_tag_swallowed_element",
|
|
5102
5105
|
severity: "error",
|
|
5103
|
-
message: `<${tag.name}> is missing its closing \`>\` before the next
|
|
5106
|
+
message: `<${tag.name}> is missing its closing \`>\` before the next \`<\`, so the following element is swallowed as bogus attribute text and never becomes a real node.`,
|
|
5104
5107
|
fixHint: "Close the previous tag's `>` before opening the next element.",
|
|
5105
5108
|
snippet: truncateSnippet(tag.raw)
|
|
5106
5109
|
});
|
|
@@ -5806,7 +5809,7 @@ var mediaRules = [
|
|
|
5806
5809
|
severity: "error",
|
|
5807
5810
|
message: `<video${elementId ? ` id="${elementId}"` : ""}> has data-start but declares neither muted nor data-has-audio. If the file has sound, add data-has-audio="true" (the sound stays on this clip). If it is silent, add muted.`,
|
|
5808
5811
|
elementId,
|
|
5809
|
-
fixHint: 'Add data-has-audio="true" when the file has sound (recommended
|
|
5812
|
+
fixHint: 'Add data-has-audio="true" when the file has sound (recommended: it keeps picture and sound on one clip), or add `muted` for silent footage.',
|
|
5810
5813
|
snippet: truncateSnippet(tag.raw)
|
|
5811
5814
|
});
|
|
5812
5815
|
}
|
|
@@ -5891,7 +5894,7 @@ var mediaRules = [
|
|
|
5891
5894
|
severity: "error",
|
|
5892
5895
|
message: `Self-closing <${tagName}/> is invalid HTML. The browser will leave the tag open, swallowing all subsequent elements as invisible fallback content. This makes compositions INVISIBLE.`,
|
|
5893
5896
|
elementId,
|
|
5894
|
-
fixHint: `Change <${tagName} .../> to <${tagName} ...></${tagName}
|
|
5897
|
+
fixHint: `Change <${tagName} .../> to <${tagName} ...></${tagName}>. Media elements MUST have explicit closing tags.`,
|
|
5895
5898
|
snippet: truncateSnippet(scMatch[0])
|
|
5896
5899
|
});
|
|
5897
5900
|
}
|
|
@@ -5934,7 +5937,7 @@ var mediaRules = [
|
|
|
5934
5937
|
findings.push({
|
|
5935
5938
|
code: "base64_media_prohibited",
|
|
5936
5939
|
severity: "error",
|
|
5937
|
-
message: `Inline base64 audio/video detected (${(dataSize / 1024).toFixed(0)} KB)${isSuspicious ? "
|
|
5940
|
+
message: `Inline base64 audio/video detected (${(dataSize / 1024).toFixed(0)} KB)${isSuspicious ? ", likely fabricated data" : ""}. Base64 media is prohibited: it bloats file size and breaks rendering.`,
|
|
5938
5941
|
fixHint: "Use a relative path (assets/music.mp3) or HTTPS URL for the audio/video src. Never embed media as base64.",
|
|
5939
5942
|
snippet: truncateSnippet((b64Match[1] ?? "").slice(0, 80) + "...")
|
|
5940
5943
|
});
|
|
@@ -5963,7 +5966,7 @@ var mediaRules = [
|
|
|
5963
5966
|
findings.push({
|
|
5964
5967
|
code: "media_missing_id",
|
|
5965
5968
|
severity: "error",
|
|
5966
|
-
message: `<${tag.name}> has data-start but no id attribute. The renderer requires id to discover media elements
|
|
5969
|
+
message: `<${tag.name}> has data-start but no id attribute. The renderer requires id to discover media elements, so this ${tag.name === "audio" ? "audio will be SILENT" : "video will be FROZEN"} in renders.`,
|
|
5967
5970
|
fixHint: `Add a unique id attribute: <${tag.name} id="my-${tag.name}" ...>`,
|
|
5968
5971
|
snippet: truncateSnippet(tag.raw)
|
|
5969
5972
|
});
|
|
@@ -6020,7 +6023,7 @@ var mediaRules = [
|
|
|
6020
6023
|
findings.push({
|
|
6021
6024
|
code: "media_crossorigin_breaks_preview",
|
|
6022
6025
|
severity: "error",
|
|
6023
|
-
message: `<${tag.name}${elementId ? ` id="${elementId}"` : ""}> has crossorigin, which forces a CORS-checked fetch. If the media host omits Access-Control-Allow-Origin, the load silently fails in Studio preview (media shows BLANK/black) while server-side renders still work
|
|
6026
|
+
message: `<${tag.name}${elementId ? ` id="${elementId}"` : ""}> has crossorigin, which forces a CORS-checked fetch. If the media host omits Access-Control-Allow-Origin, the load silently fails in Studio preview (media shows BLANK/black) while server-side renders still work, which hides the bug.`,
|
|
6024
6027
|
elementId,
|
|
6025
6028
|
fixHint: "Remove the crossorigin attribute unless you read the media back via canvas/WebGL/WebAudio AND the host is known to send CORS headers. Plain displayed media never needs it.",
|
|
6026
6029
|
snippet: truncateSnippet(tag.raw)
|
|
@@ -6054,7 +6057,7 @@ var mediaRules = [
|
|
|
6054
6057
|
findings.push({
|
|
6055
6058
|
code: "video_audio_double_source",
|
|
6056
6059
|
severity: "error",
|
|
6057
|
-
message: `<audio${audioInfo.id ? ` id="${audioInfo.id}"` : ""}> and <video${videoInfo.id ? ` id="${videoInfo.id}"` : ""}> both point to the same source at the same time. The unmuted video already provides audio
|
|
6060
|
+
message: `<audio${audioInfo.id ? ` id="${audioInfo.id}"` : ""}> and <video${videoInfo.id ? ` id="${videoInfo.id}"` : ""}> both point to the same source at the same time. The unmuted video already provides audio, so the duplicate <audio> will cause double playback and echo.`,
|
|
6058
6061
|
elementId: audioInfo.id,
|
|
6059
6062
|
fixHint: "Remove the <audio> element and let the video carry its own sound (recommended), or mute the video (add `muted`) and keep the separate <audio> when picture and sound must be cut independently.",
|
|
6060
6063
|
snippet: truncateSnippet(audioInfo.raw)
|
|
@@ -6090,7 +6093,7 @@ function findVolumeTweenOverridesGainFindings(ctx) {
|
|
|
6090
6093
|
findings.push({
|
|
6091
6094
|
code: "audio_volume_tween_overrides_gain",
|
|
6092
6095
|
severity: "warning",
|
|
6093
|
-
message: `#${id} has data-volume="${volume}" (${db}) and a GSAP tween on \`volume\`. Tween values are absolute
|
|
6096
|
+
message: `#${id} has data-volume="${volume}" (${db}) and a GSAP tween on \`volume\`. Tween values are absolute: they REPLACE this gain rather than scale it, so wherever the tween names a value the clip plays at that value, not at ${db}.`,
|
|
6094
6097
|
elementId: id,
|
|
6095
6098
|
fixHint: "Write the tween's targets in the same absolute gain (e.g. `volume: 1.95`, not `volume: 1`), or reset data-volume to 1 and let the tween carry the level on its own.",
|
|
6096
6099
|
snippet: truncateSnippet(tag.raw)
|
|
@@ -6109,7 +6112,7 @@ function findVolumeDoubleAutomationFindings(ctx) {
|
|
|
6109
6112
|
findings.push({
|
|
6110
6113
|
code: "audio_volume_double_automation",
|
|
6111
6114
|
severity: "warning",
|
|
6112
|
-
message: `#${id} has both a volume automation lane and a GSAP tween on \`volume\`. The lane wins
|
|
6115
|
+
message: `#${id} has both a volume automation lane and a GSAP tween on \`volume\`. The lane wins, and the tween is ignored in preview and in the render.`,
|
|
6113
6116
|
elementId: id,
|
|
6114
6117
|
fixHint: "Keep one of them: delete the volume lane to go back to tweening, or drop the tween and shape the level in the automation lane.",
|
|
6115
6118
|
snippet: truncateSnippet(tag.raw)
|
|
@@ -6157,7 +6160,7 @@ function findCarveUngroupedSourcesFindings(ctx) {
|
|
|
6157
6160
|
severity: "warning",
|
|
6158
6161
|
message: `${elementId ? `#${elementId}'s` : "This"} carve names ${clipIds.length} voice clips directly (${clipIds.join(", ")}) instead of a group.`,
|
|
6159
6162
|
elementId,
|
|
6160
|
-
fixHint: "Group the voice clips and carve against the group
|
|
6163
|
+
fixHint: "Group the voice clips and carve against the group. A hand-rolled clip list silently rots when a clip is added.",
|
|
6161
6164
|
snippet: truncateSnippet(tag.raw)
|
|
6162
6165
|
});
|
|
6163
6166
|
}
|
|
@@ -6206,7 +6209,7 @@ function findAudioGroupTimingAttrFindings(ctx) {
|
|
|
6206
6209
|
findings.push({
|
|
6207
6210
|
code: "audio_group_timing_attrs",
|
|
6208
6211
|
severity: "warning",
|
|
6209
|
-
message: `${elementId ? `#${elementId}` : "This audio group"} carries ${present.map((attr) => `\`${attr}\``).join(", ")}, which a bus has no use for
|
|
6212
|
+
message: `${elementId ? `#${elementId}` : "This audio group"} carries ${present.map((attr) => `\`${attr}\``).join(", ")}, which a bus has no use for: its members carry the timing and its automation clock is composition time.`,
|
|
6210
6213
|
elementId,
|
|
6211
6214
|
fixHint: `Remove ${present.map((attr) => `\`${attr}\``).join(", ")} from the group element.`,
|
|
6212
6215
|
snippet: truncateSnippet(tag.raw)
|
|
@@ -6223,7 +6226,7 @@ function findAudioGroupCarveAttrFindings(ctx) {
|
|
|
6223
6226
|
findings.push({
|
|
6224
6227
|
code: "audio_group_carve_attr",
|
|
6225
6228
|
severity: "warning",
|
|
6226
|
-
message: `${elementId ? `#${elementId}` : "This audio group"} carries \`data-fx-carve\`, which belongs on the clip being carved
|
|
6229
|
+
message: `${elementId ? `#${elementId}` : "This audio group"} carries \`data-fx-carve\`, which belongs on the clip being carved. A bus has no audio of its own to level-match against, and a carve here stacks with any its members already have.`,
|
|
6227
6230
|
elementId,
|
|
6228
6231
|
fixHint: "Remove `data-fx-carve` and the `fromCarve` nodes it wrote into this bus's `data-fx-chain`, and carve the bed clip instead.",
|
|
6229
6232
|
snippet: truncateSnippet(tag.raw)
|
|
@@ -6888,7 +6891,7 @@ var compositionRules = [
|
|
|
6888
6891
|
findings.push({
|
|
6889
6892
|
code: "root_composition_missing_html_wrapper",
|
|
6890
6893
|
severity: "error",
|
|
6891
|
-
message: "Composition starts with a bare element instead of a proper HTML document. An index.html that contains data-composition-id but no <!DOCTYPE html>, <html>, or <body> is a fragment
|
|
6894
|
+
message: "Composition starts with a bare element instead of a proper HTML document. An index.html that contains data-composition-id but no <!DOCTYPE html>, <html>, or <body> is a fragment: browsers quirks-mode it, the preview server cannot load it, and the bundler will fail to inject runtime scripts.",
|
|
6892
6895
|
fixHint: 'Wrap the composition in <!DOCTYPE html><html><head><meta charset="UTF-8"></head><body>...</body></html>.',
|
|
6893
6896
|
snippet: rootTag ? truncateSnippet(rootTag.raw) : void 0
|
|
6894
6897
|
});
|
|
@@ -7043,7 +7046,7 @@ var compositionRules = [
|
|
|
7043
7046
|
findings.push({
|
|
7044
7047
|
code: "unknown_variable_binding",
|
|
7045
7048
|
severity: "warning",
|
|
7046
|
-
message: `<${tag.name}> binds ${attr}="${id}" but no variable "${id}" is declared in data-composition-variables
|
|
7049
|
+
message: `<${tag.name}> binds ${attr}="${id}" but no variable "${id}" is declared in data-composition-variables, so the binding will silently keep the authored fallback.`,
|
|
7047
7050
|
fixHint: `Declare the variable on the composition root (<html>, or the [data-composition-id] root element for a template/fragment comp): data-composition-variables='[{"id":"${id}","type":"${attr === "data-var-src" ? "image" : "string"}","label":"${id}","default":"..."}]', or fix the binding id.`,
|
|
7048
7051
|
elementId: readAttr(tag.raw, "id") || void 0,
|
|
7049
7052
|
snippet: truncateSnippet(tag.raw)
|
|
@@ -7084,8 +7087,8 @@ var compositionRules = [
|
|
|
7084
7087
|
{
|
|
7085
7088
|
code: "html_dir_attribute_breaks_render",
|
|
7086
7089
|
severity: "error",
|
|
7087
|
-
message: `<html dir="${dir}"> renders correctly in preview/snapshot but produces a fully blank/black video from render
|
|
7088
|
-
fixHint: `Remove dir="${dir}" from <html>. Keep lang, and scope ${scopedDirection} to individual text-containing elements instead
|
|
7090
|
+
message: `<html dir="${dir}"> renders correctly in preview/snapshot but produces a fully blank/black video from render. This is a confirmed, silent failure.`,
|
|
7091
|
+
fixHint: `Remove dir="${dir}" from <html>. Keep lang, and scope ${scopedDirection} to individual text-containing elements instead. Text still shapes correctly via the browser's own bidi algorithm.`,
|
|
7089
7092
|
snippet: truncateSnippet(htmlTag.raw)
|
|
7090
7093
|
}
|
|
7091
7094
|
];
|
|
@@ -7123,7 +7126,7 @@ var compositionRules = [
|
|
|
7123
7126
|
findings.push({
|
|
7124
7127
|
code: "subcomposition_blanks_before_host",
|
|
7125
7128
|
severity: "warning",
|
|
7126
|
-
message: `<${t.tag.name}${elementId ? ` id="${elementId}"` : ""}> sub-composition ends at ${round3(t.end)}s but the composition runs to ${round3(rootDuration)}s
|
|
7129
|
+
message: `<${t.tag.name}${elementId ? ` id="${elementId}"` : ""}> sub-composition ends at ${round3(t.end)}s but the composition runs to ${round3(rootDuration)}s, so its slot will be blank for ~${gap}s.`,
|
|
7127
7130
|
elementId,
|
|
7128
7131
|
fixHint: `data-duration is the slot's visible window. Set this sub-composition's data-duration to ${round3(rootDuration - t.start)} to fill the host window, or add another clip to cover the remaining ~${gap}s.`,
|
|
7129
7132
|
snippet: truncateSnippet(t.tag.raw)
|
|
@@ -7162,7 +7165,7 @@ var compositionRules = [
|
|
|
7162
7165
|
severity: "error",
|
|
7163
7166
|
message: `Root element has class="${rootClasses.join(" ")}" and is styled by ${offenders.length} rule(s) keyed off that class (e.g. ${example}). At render, every sub-composition rule is scoped to [data-composition-id="${rootCompositionId}"] <selector>, so a selector whose leftmost part is the ROOT's own class becomes a descendant selector that cannot match the scoped element itself. Since #1886 the producer preserves the authored root as an inner wrapper, so this no longer renders the scene unstyled, but #root is the shape the scoper special-cases and the registry blocks model. Use it so preview, render, and Studio agree.`,
|
|
7164
7167
|
selector: example,
|
|
7165
|
-
fixHint: `Give the root id="root" and style it with \`#root { ... }\` plus plain descendant selectors (\`.kicker\`, \`#hero\`)
|
|
7168
|
+
fixHint: `Give the root id="root" and style it with \`#root { ... }\` plus plain descendant selectors (\`.kicker\`, \`#hero\`). The runtime already scopes each sub-composition by data-composition-id, so a class namespace on the root is redundant.`,
|
|
7166
7169
|
snippet: truncateSnippet(rootTag.raw)
|
|
7167
7170
|
}
|
|
7168
7171
|
];
|
|
@@ -7245,7 +7248,7 @@ ${allInlineStyles}`.replace(/\/\*[\s\S]*?\*\//g, "");
|
|
|
7245
7248
|
{
|
|
7246
7249
|
code: "root_composition_missing_duration_source",
|
|
7247
7250
|
severity: "error",
|
|
7248
|
-
message: `Root composition uses Three.js with no data-duration. The runtime cannot discover a Three.js scene's duration automatically (no AnimationClip/AnimationMixer inspection)
|
|
7251
|
+
message: `Root composition uses Three.js with no data-duration. The runtime cannot discover a Three.js scene's duration automatically (no AnimationClip/AnimationMixer inspection), so render will fail with "Composition has zero duration".`,
|
|
7249
7252
|
fixHint: 'Add data-duration="<seconds>" to the root element.',
|
|
7250
7253
|
snippet: truncateSnippet(rootTag.raw)
|
|
7251
7254
|
}
|
|
@@ -7308,8 +7311,8 @@ ${allInlineStyles}`.replace(/\/\*[\s\S]*?\*\//g, "");
|
|
|
7308
7311
|
{
|
|
7309
7312
|
code: "composition_heavy_overlay_count_high",
|
|
7310
7313
|
severity: "warning",
|
|
7311
|
-
message: `This composition has ${heavyCount} elements carrying "heavy overlay" CSS (filter:blur, radial-gradient, or clip-path). Field signal: a composition with ~40 such elements
|
|
7312
|
-
fixHint: `${splitTarget} and concat the pieces (FFmpeg or the runtime's slideshow) so each capture only sees a small subset of heavy overlays at once. Even hidden overlays (opacity:0 / visibility:hidden) contribute
|
|
7314
|
+
message: `This composition has ${heavyCount} elements carrying "heavy overlay" CSS (filter:blur, radial-gradient, or clip-path). Field signal: a composition with ~40 such elements (including opacity:0 / visibility:hidden ones) captures solid-black for the first ~half of the render, recovering near the end. Reproduces identically via drawElement, forced screenshot capture, and snapshot, so the capture layer itself is the offender (not encoder/mux). Independent of duration. Presence alone matters; only display:none elements are excluded here.`,
|
|
7315
|
+
fixHint: `${splitTarget} and concat the pieces (FFmpeg or the runtime's slideshow) so each capture only sees a small subset of heavy overlays at once. Even hidden overlays (opacity:0 / visibility:hidden) contribute: either remove truly unused ones from the source or scope them into their own per-transition sub-composition. If an overlay is genuinely inert for the whole clip, use display:none so it never enters the render tree. Field ref ts=1784040753 (#hyperframes-cli-feedback).`
|
|
7313
7316
|
}
|
|
7314
7317
|
];
|
|
7315
7318
|
},
|
|
@@ -7352,8 +7355,8 @@ ${allInlineStyles}`.replace(/\/\*[\s\S]*?\*\//g, "");
|
|
|
7352
7355
|
{
|
|
7353
7356
|
code: "root_zoom_rescales_a_fixed_canvas",
|
|
7354
7357
|
severity: "error",
|
|
7355
|
-
message: `\`zoom: ${hit.value}\` on ${hit.where} rescales the painted composition inside a frame that does not rescale with it. The capture frame is sized from the root's data-width/data-height in LAYOUT pixels, and \`zoom\` changes only what is painted inside it, so ` + (enlarging ? `content past the frame's edge renders zero pixels
|
|
7356
|
-
fixHint: `Author the composition at its real size
|
|
7358
|
+
message: `\`zoom: ${hit.value}\` on ${hit.where} rescales the painted composition inside a frame that does not rescale with it. The capture frame is sized from the root's data-width/data-height in LAYOUT pixels, and \`zoom\` changes only what is painted inside it, so ` + (enlarging ? `content past the frame's edge renders zero pixels. It is inside the declared composition and absent from the output, with nothing else reporting it.` : `the composition paints into part of the frame and the remainder of every output frame is dead space.`),
|
|
7359
|
+
fixHint: `Author the composition at its real size: set data-width/data-height (and the root's width/height) to the dimensions you want, and drop the canvas-level \`zoom\`. To scale the OUTPUT without changing layout, pass \`--output-resolution\` to render, which supersamples. \`zoom\` on descendants is fine and is not flagged: it is honoured exactly as specified.`,
|
|
7357
7360
|
snippet: hit.snippet
|
|
7358
7361
|
}
|
|
7359
7362
|
];
|
|
@@ -7397,8 +7400,8 @@ ${allInlineStyles}`.replace(/\/\*[\s\S]*?\*\//g, "");
|
|
|
7397
7400
|
{
|
|
7398
7401
|
code: "composition_exceeds_inspection_viewport_cap",
|
|
7399
7402
|
severity: "warning",
|
|
7400
|
-
message: `${over.join(" and ")} exceeds the ${INSPECTION_VIEWPORT_CAP}px viewport cap that check, validate, snapshot, compare and layout clamp to. render is unaffected and produces the full size, so the video is correct
|
|
7401
|
-
fixHint: `Keep the authored size if the output needs it, and verify the region past ${INSPECTION_VIEWPORT_CAP}px from a render rather than from check/snapshot/compare
|
|
7403
|
+
message: `${over.join(" and ")} exceeds the ${INSPECTION_VIEWPORT_CAP}px viewport cap that check, validate, snapshot, compare and layout clamp to. render is unaffected and produces the full size, so the video is correct, but every inspection command captures a ${INSPECTION_VIEWPORT_CAP}px-wide frame, and anything beyond that is missing from what they report. Measured at 0.8.71: a 5000x400 composition renders 5000x400 with all content, while snapshot returns 4096x400 with the element at left:4500 absent.`,
|
|
7404
|
+
fixHint: `Keep the authored size if the output needs it, and verify the region past ${INSPECTION_VIEWPORT_CAP}px from a render rather than from check/snapshot/compare. A clean inspection result does not cover it. If the large canvas is only there to gain resolution, author at the layout size and pass \`--output-resolution\` to render instead, which supersamples without changing layout.`,
|
|
7402
7405
|
snippet: truncateSnippet(rootTag.raw)
|
|
7403
7406
|
}
|
|
7404
7407
|
];
|
|
@@ -8275,7 +8278,7 @@ ${right.raw}`)
|
|
|
8275
8278
|
findings.push({
|
|
8276
8279
|
code: "gsap_animates_clip_element",
|
|
8277
8280
|
severity: "error",
|
|
8278
|
-
message: `GSAP animation sets ${conflictingProps.join(", ")} on a clip element. Selector "${sel}" resolves to element ${elDesc}. The framework manages clip visibility, and autoAlpha writes visibility as well as opacity
|
|
8281
|
+
message: `GSAP animation sets ${conflictingProps.join(", ")} on a clip element. Selector "${sel}" resolves to element ${elDesc}. The framework manages clip visibility, and autoAlpha writes visibility as well as opacity. Do not animate these properties on clip elements.`,
|
|
8279
8282
|
selector: sel,
|
|
8280
8283
|
elementId: clipInfo.id || void 0,
|
|
8281
8284
|
fixHint: "Remove the visibility/display/autoAlpha tween, or move the content into a child <div> and target that instead.",
|
|
@@ -8469,7 +8472,7 @@ ${right.raw}`)
|
|
|
8469
8472
|
findings.push({
|
|
8470
8473
|
code: "audio_reactive_single_tween_per_group",
|
|
8471
8474
|
severity: "warning",
|
|
8472
|
-
message: "Audio-reactive captions use a single tween per group based on peak values. This sets one static value at group.start
|
|
8475
|
+
message: "Audio-reactive captions use a single tween per group based on peak values. This sets one static value at group.start, which is not perceptible as audio reactivity.",
|
|
8473
8476
|
fixHint: "Sample audio data at 100-200ms intervals throughout each group's lifetime (for loop from group.start to group.end) and create a tween at each sample point for visible pulsing."
|
|
8474
8477
|
});
|
|
8475
8478
|
}
|
|
@@ -8554,7 +8557,7 @@ ${right.raw}`)
|
|
|
8554
8557
|
...locate(script, gsapCallOffset(script, "timeline")),
|
|
8555
8558
|
code: "gsap_timeline_not_registered",
|
|
8556
8559
|
severity: "error",
|
|
8557
|
-
message: "GSAP timeline is created but never registered in window.__timelines. The runtime discovers timelines from this registry
|
|
8560
|
+
message: "GSAP timeline is created but never registered in window.__timelines. The runtime discovers timelines from this registry. Without registration, animations will not play during preview or render.",
|
|
8558
8561
|
fixHint: 'Add `window.__timelines = window.__timelines || {};` and `window.__timelines["root"] = tl;` after creating the timeline (use the composition\'s data-composition-id as the key).'
|
|
8559
8562
|
});
|
|
8560
8563
|
}
|
|
@@ -8622,9 +8625,9 @@ ${right.raw}`)
|
|
|
8622
8625
|
findings.push({
|
|
8623
8626
|
code: "gsap_from_opacity_noop",
|
|
8624
8627
|
severity: "error",
|
|
8625
|
-
message: `"${sel}" has CSS \`opacity: 0\` and a gsap.${win.method}() that also sets opacity to 0. gsap.from() animates FROM the specified value TO the current CSS value
|
|
8628
|
+
message: `"${sel}" has CSS \`opacity: 0\` and a gsap.${win.method}() that also sets opacity to 0. gsap.from() animates FROM the specified value TO the current CSS value. Since CSS is already 0, the element animates from 0\u21920 and never becomes visible.`,
|
|
8626
8629
|
selector: sel,
|
|
8627
|
-
fixHint: `Remove \`opacity: 0\` from the CSS/inline style on "${sel}". Let gsap.from({opacity: 0}) handle the initial hidden state
|
|
8630
|
+
fixHint: `Remove \`opacity: 0\` from the CSS/inline style on "${sel}". Let gsap.from({opacity: 0}) handle the initial hidden state. It will animate FROM 0 TO the CSS value (1 by default).`,
|
|
8628
8631
|
snippet: truncateSnippet(win.raw)
|
|
8629
8632
|
});
|
|
8630
8633
|
}
|
|
@@ -8703,12 +8706,12 @@ ${right.raw}`)
|
|
|
8703
8706
|
if (layoutProps.length > 0 && allTargetsHtmlInCanvas(call.selector)) layoutProps = [];
|
|
8704
8707
|
if (layoutProps.length === 0 && reflowProps.length === 0 && !usesRoundProps) continue;
|
|
8705
8708
|
const flagged = [...layoutProps, ...reflowProps, ...usesRoundProps ? ["roundProps"] : []];
|
|
8706
|
-
const message = `GSAP tween on "${call.selector}" uses motion that snaps to integer device pixels: ${flagged.join(", ")}. Layout and text-reflow properties snap during browser layout; roundProps rounds the tween value. Slow motion or an ease-out tail then stutters under the seek-by-frame capture engine
|
|
8709
|
+
const message = `GSAP tween on "${call.selector}" uses motion that snaps to integer device pixels: ${flagged.join(", ")}. Layout and text-reflow properties snap during browser layout; roundProps rounds the tween value. Slow motion or an ease-out tail then stutters under the seek-by-frame capture engine. Animate transforms (x/y/scale/opacity) instead.`;
|
|
8707
8710
|
const fixes = [];
|
|
8708
8711
|
if (layoutProps.length > 0) {
|
|
8709
8712
|
const tokens = [...new Set(layoutProps.flatMap((p) => LAYOUT_FIX[p] ?? []))];
|
|
8710
8713
|
fixes.push(
|
|
8711
|
-
`replace ${layoutProps.join("/")} with the transform equivalent (${tokens.join(", ")})
|
|
8714
|
+
`replace ${layoutProps.join("/")} with the transform equivalent (${tokens.join(", ")}), e.g. tl.fromTo("${call.selector}", { x: -1300 }, { x: 0, ...yourAnimation })`
|
|
8712
8715
|
);
|
|
8713
8716
|
}
|
|
8714
8717
|
if (reflowProps.length > 0) {
|
|
@@ -8720,7 +8723,7 @@ ${right.raw}`)
|
|
|
8720
8723
|
}
|
|
8721
8724
|
if (spacing.length > 0) {
|
|
8722
8725
|
parts.push(
|
|
8723
|
-
`for ${spacing.join("/")}, split the text into per-character elements and animate each glyph's x (the spread)
|
|
8726
|
+
`for ${spacing.join("/")}, split the text into per-character elements and animate each glyph's x (the spread), since uniform scale is NOT equivalent, or hold the final value statically`
|
|
8724
8727
|
);
|
|
8725
8728
|
}
|
|
8726
8729
|
fixes.push(
|
|
@@ -8784,7 +8787,7 @@ ${right.raw}`)
|
|
|
8784
8787
|
findings.push({
|
|
8785
8788
|
code: "gsap_relative_value_second_writer",
|
|
8786
8789
|
severity: "error",
|
|
8787
|
-
message: `Relative value(s) ${values} on "${win.targetSelector}" start while another writer for the same propert${sharedProps.length > 1 ? "ies" : "y"} is active between ${formatTime(win.position)} and ${formatTime(overlapEnd)}. Relative tweens capture their base at tween init: the sequential path inits mid-flight of the other writer, a cold render worker landing later inits with its end state
|
|
8790
|
+
message: `Relative value(s) ${values} on "${win.targetSelector}" start while another writer for the same propert${sharedProps.length > 1 ? "ies" : "y"} is active between ${formatTime(win.position)} and ${formatTime(overlapEnd)}. Relative tweens capture their base at tween init: the sequential path inits mid-flight of the other writer, a cold render worker landing later inits with its end state, so the same frame renders at two different positions (snap at chunk boundaries).`,
|
|
8788
8791
|
selector: win.targetSelector,
|
|
8789
8792
|
fixHint: `Use absolute values for ${sharedProps.join(", ")}, or a fromTo() with explicit endpoints, so every seek path resolves the same state. Single-writer relative values are safe; the conflict is the second writer.`,
|
|
8790
8793
|
snippet: truncateSnippet(`${win.raw}
|
|
@@ -8853,13 +8856,13 @@ ${other.raw}`)
|
|
|
8853
8856
|
const readsInvariant = TRANSFORM_INVARIANT_READ.test(fn.body);
|
|
8854
8857
|
const badMember = firstParamMemberAccessHazard(fn);
|
|
8855
8858
|
if (!readsSensitive && !readsInvariant && !badMember) continue;
|
|
8856
|
-
const reason = readsSensitive ? "reads transform-sensitive geometry, so its result depends on the worker's own seek order" : badMember ? `accesses .${badMember} on its first parameter
|
|
8859
|
+
const reason = readsSensitive ? "reads transform-sensitive geometry, so its result depends on the worker's own seek order" : badMember ? `accesses .${badMember} on its first parameter. GSAP function values receive (index, target, targets), so the first parameter is a NUMBER and this throws at tween init` : "measures layout at tween init, which is deterministic across cold render workers only while the measured layout never animates";
|
|
8857
8860
|
findings.push({
|
|
8858
8861
|
code: "gsap_function_value_hazard",
|
|
8859
8862
|
severity: readsSensitive || badMember ? "error" : "warning",
|
|
8860
8863
|
message: `Function-valued tween var for ${prop} on "${anim.targetSelector}" ${reason}. Each render worker initializes tweens independently.`,
|
|
8861
8864
|
selector: anim.targetSelector,
|
|
8862
|
-
fixHint: badMember ? "Use the SECOND parameter for the element: (index, target) =>
|
|
8865
|
+
fixHint: badMember ? "Use the SECOND parameter for the element: (index, target) => ..., or index arithmetic like (i) => i * 20." : "Compute the value once at build time (before the timeline is registered) and pass a constant, or derive it from fixed composition coordinates.",
|
|
8863
8866
|
snippet: truncateSnippet(raw)
|
|
8864
8867
|
});
|
|
8865
8868
|
}
|
|
@@ -8895,9 +8898,9 @@ ${other.raw}`)
|
|
|
8895
8898
|
findings.push({
|
|
8896
8899
|
code: "gsap_callback_dom_measurement",
|
|
8897
8900
|
severity: "warning",
|
|
8898
|
-
message: "Timeline callback reaches DOM measurement (getBoundingClientRect/getTotalLength/getComputedStyle/...). The renderer seeks with suppressEvents=false, so callbacks re-fire on every seek
|
|
8901
|
+
message: "Timeline callback reaches DOM measurement (getBoundingClientRect/getTotalLength/getComputedStyle/...). The renderer seeks with suppressEvents=false, so callbacks re-fire on every seek, and a cold render worker runs them against whatever DOM state its own non-linear seek order produced. Measured geometry is seek-order-dependent, and values measured at build time (before the callback ran) are stale or zero.",
|
|
8899
8902
|
selector: truncateSnippet(site, 120),
|
|
8900
|
-
fixHint: "Do all measurement and DOM setup synchronously at build time, before registering the timeline
|
|
8903
|
+
fixHint: "Do all measurement and DOM setup synchronously at build time, before registering the timeline, or derive geometry from fixed composition coordinates instead of measuring.",
|
|
8901
8904
|
snippet: truncateSnippet(snippet)
|
|
8902
8905
|
});
|
|
8903
8906
|
};
|
|
@@ -8970,8 +8973,8 @@ ${other.raw}`)
|
|
|
8970
8973
|
findings.push({
|
|
8971
8974
|
code: "gsap_timeline_return_used_as_tween",
|
|
8972
8975
|
severity: "error",
|
|
8973
|
-
message: `\`${match[0].includes(".push") ? "push" : boundName}\` captures the return of \`.${method}()\` on timeline \`${timelineVars[0]}\`, but a timeline's \`.to()\`/\`.from()\`/\`.set()\` returns THE TIMELINE ITSELF, not the tween it created. Every captured value is the same master timeline, so a tween-scoped call on it
|
|
8974
|
-
fixHint: "To build a group of keyframes you can later discard, put them in a nested timeline: `const nested = gsap.timeline(); nested.to(...); tl.add(nested, 0)
|
|
8976
|
+
message: `\`${match[0].includes(".push") ? "push" : boundName}\` captures the return of \`.${method}()\` on timeline \`${timelineVars[0]}\`, but a timeline's \`.to()\`/\`.from()\`/\`.set()\` returns THE TIMELINE ITSELF, not the tween it created. Every captured value is the same master timeline, so a tween-scoped call on it (\`.kill()\` above all) hits the whole composition: it interrupts the timeline and detaches it from its parent, which freezes a parent-driven seek while an explicit \`tl.progress()\` keeps working. Only \`gsap.to()\` returns a tween.`,
|
|
8977
|
+
fixHint: "To build a group of keyframes you can later discard, put them in a nested timeline: `const nested = gsap.timeline(); nested.to(...); tl.add(nested, 0);`. Then `nested.kill()` replaces exactly those keyframes and cannot reach `tl`. Children keep their absolute times when the nest is added at 0. To hold a single real tween, create it with `gsap.to(...)` and place it with `tl.add(tween, at)`.",
|
|
8975
8978
|
snippet: truncateSnippet(source.slice(contextStart, match.index + match[0].length + 60))
|
|
8976
8979
|
});
|
|
8977
8980
|
}
|
|
@@ -9066,7 +9069,7 @@ ${other.raw}`)
|
|
|
9066
9069
|
findings.push({
|
|
9067
9070
|
code: "svg_drawon_css_dasharray_conflict",
|
|
9068
9071
|
severity: "error",
|
|
9069
|
-
message: `GSAP writes strokeDasharray on "${targetLabel}", but its CSS ("${conflictToken}") declares a multi-component stroke-dasharray. GSAP merges dash lists per component, so the CSS gap survives (e.g. "641.4px, 10px")
|
|
9072
|
+
message: `GSAP writes strokeDasharray on "${targetLabel}", but its CSS ("${conflictToken}") declares a multi-component stroke-dasharray. GSAP merges dash lists per component, so the CSS gap survives (e.g. "641.4px, 10px"). The draw-on hide only hides one gap's worth and the line stays visible the whole scene.`,
|
|
9070
9073
|
selector: quotedSelector ?? void 0,
|
|
9071
9074
|
fixHint: `Remove the CSS stroke-dasharray from "${conflictToken}" (decorative dashes belong on a separate element), or set the full two-component value in GSAP: strokeDasharray: "\${len} \${len}".`,
|
|
9072
9075
|
snippet: truncateSnippet(match[0] + firstVars.slice(1))
|
|
@@ -9188,7 +9191,7 @@ ${other.raw}`)
|
|
|
9188
9191
|
findings.push({
|
|
9189
9192
|
code: "svg_measure_before_path_d",
|
|
9190
9193
|
severity: sameVarAssignmentExists ? "warning" : "error",
|
|
9191
|
-
message: sameVarAssignmentExists ? `getTotalLength() is called on "${tokenLabel}", whose \`d\` is only assigned inside a function body
|
|
9194
|
+
message: sameVarAssignmentExists ? `getTotalLength() is called on "${tokenLabel}", whose \`d\` is only assigned inside a function body. If the measure runs before that function (e.g. the function is a timeline callback), the length is 0 and the dash animation is dead.` : `getTotalLength() is called on "${tokenLabel}", but the path has no static \`d\` attribute and no d assignment exists anywhere, so getTotalLength() returns 0 in Chrome, silently killing dash animations.`,
|
|
9192
9195
|
selector: tokenLabel,
|
|
9193
9196
|
fixHint: "Assign the path's `d` synchronously at build time (top level, before measuring), or author a static d attribute in the HTML.",
|
|
9194
9197
|
snippet: truncateSnippet(match[0] + ")")
|
|
@@ -9725,7 +9728,7 @@ var fontRules = [
|
|
|
9725
9728
|
{
|
|
9726
9729
|
code: "system_font_will_alias",
|
|
9727
9730
|
severity: "warning",
|
|
9728
|
-
message: `Font ${aliased.length === 1 ? "family" : "families"} will be substituted at render time: ${aliased.join(", ")}. In distributed/Lambda rendering system-font capture is disabled
|
|
9731
|
+
message: `Font ${aliased.length === 1 ? "family" : "families"} will be substituted at render time: ${aliased.join(", ")}. In distributed/Lambda rendering system-font capture is disabled, so these fonts will fall back to OS defaults. Embed explicit @font-face declarations instead.`
|
|
9729
9732
|
}
|
|
9730
9733
|
];
|
|
9731
9734
|
},
|
|
@@ -9745,7 +9748,7 @@ var fontRules = [
|
|
|
9745
9748
|
code: "font_family_without_font_face",
|
|
9746
9749
|
severity: "error",
|
|
9747
9750
|
message: `Font ${undeclared.length === 1 ? "family" : "families"} used without @font-face declaration: ${undeclared.join(", ")}. These are not in the auto-resolved font list, so the renderer cannot supply them automatically. Text will fall back to a generic font, producing incorrect typography in the video.`,
|
|
9748
|
-
fixHint: "Add @font-face { font-family: '...'; src: url('capture/assets/fonts/...woff2'); } for each font family, pointing to the captured .woff2 files. For an OS-bundled system font (e.g. Hiragino Sans, Microsoft YaHei) that has no downloadable file, use src: local('Exact Font Name') instead
|
|
9751
|
+
fixHint: "Add @font-face { font-family: '...'; src: url('capture/assets/fonts/...woff2'); } for each font family, pointing to the captured .woff2 files. For an OS-bundled system font (e.g. Hiragino Sans, Microsoft YaHei) that has no downloadable file, use src: local('Exact Font Name') instead. The declaration alone satisfies this check without needing a font file."
|
|
9749
9752
|
});
|
|
9750
9753
|
return findings;
|
|
9751
9754
|
}
|
|
@@ -10216,7 +10219,7 @@ function collectLocalVideoCandidates(projectDir, htmlSources) {
|
|
|
10216
10219
|
const re = new RegExp(videoSrcRe.source, videoSrcRe.flags);
|
|
10217
10220
|
let match;
|
|
10218
10221
|
while ((match = re.exec(scannable)) !== null) {
|
|
10219
|
-
const rawSrc = match
|
|
10222
|
+
const rawSrc = mediaSrcOf(match);
|
|
10220
10223
|
const reference = resolveLocalVideoReference(projectDir, rawSrc, compSrcPath);
|
|
10221
10224
|
if (!reference || candidates.has(reference.resolved)) continue;
|
|
10222
10225
|
candidates.set(reference.resolved, reference.src);
|
|
@@ -10602,9 +10605,10 @@ function lintAudioSrcNotFound(projectDir, htmlSources) {
|
|
|
10602
10605
|
const audioSrcRe = mediaSrcTagRe("audio");
|
|
10603
10606
|
const missingByFile = /* @__PURE__ */ new Map();
|
|
10604
10607
|
for (const { html, compSrcPath } of htmlSources) {
|
|
10608
|
+
const scannable = maskNonScannableRanges(html);
|
|
10605
10609
|
let match;
|
|
10606
|
-
while ((match = audioSrcRe.exec(
|
|
10607
|
-
const src = match
|
|
10610
|
+
while ((match = audioSrcRe.exec(scannable)) !== null) {
|
|
10611
|
+
const src = mediaSrcOf(match);
|
|
10608
10612
|
if (/^(https?:|data:|blob:)/i.test(src)) continue;
|
|
10609
10613
|
if (isUnresolvedAssetPlaceholder(src)) continue;
|
|
10610
10614
|
const rootRelative = compSrcPath ? rewriteAssetPath(compSrcPath, src, (path) => existsSync2(join2(projectDir, path))) : src;
|
|
@@ -10636,7 +10640,7 @@ function lintMissingLocalAsset(projectDir, htmlSources) {
|
|
|
10636
10640
|
let match;
|
|
10637
10641
|
while ((match = re.exec(scannable)) !== null) {
|
|
10638
10642
|
const tagName = (match[1] ?? "").toLowerCase();
|
|
10639
|
-
const rawSrc = match
|
|
10643
|
+
const rawSrc = mediaSrcOf(match);
|
|
10640
10644
|
if (isUnresolvedAssetPlaceholder(rawSrc)) continue;
|
|
10641
10645
|
const src = cleanAssetUrl(rawSrc);
|
|
10642
10646
|
if (!src) continue;
|
|
@@ -10662,7 +10666,7 @@ function lintMissingLocalAsset(projectDir, htmlSources) {
|
|
|
10662
10666
|
severity: "error",
|
|
10663
10667
|
file,
|
|
10664
10668
|
message: `<${tagName}> element references local file(s) not found in the project: ${unique.join(", ")}. The renderer will silently skip these and produce a video with missing visuals.`,
|
|
10665
|
-
fixHint: unique.length === 1 ? `Add "${unique[0]}" to the project directory, or update the src attribute to point to an existing file. Common cause: captured asset filenames are unreliable (heygen-logo.svg often contains Google, nvidia-logo.svg may contain Autodesk, etc.). Open the contact sheets and verify the file actually exists at this path before referencing it.` : "Add the missing files to the project directory, or update the src attributes to point to existing files. Captured asset filenames are unreliable
|
|
10669
|
+
fixHint: unique.length === 1 ? `Add "${unique[0]}" to the project directory, or update the src attribute to point to an existing file. Common cause: captured asset filenames are unreliable (heygen-logo.svg often contains Google, nvidia-logo.svg may contain Autodesk, etc.). Open the contact sheets and verify the file actually exists at this path before referencing it.` : "Add the missing files to the project directory, or update the src attributes to point to existing files. Captured asset filenames are unreliable. Verify against capture/contact-sheets/ and capture/extracted/asset-descriptions.md."
|
|
10666
10670
|
});
|
|
10667
10671
|
}
|
|
10668
10672
|
return findings;
|
|
@@ -10729,11 +10733,6 @@ function lintMultipleRootCompositions(projectDir) {
|
|
|
10729
10733
|
}
|
|
10730
10734
|
function lintDuplicateAudioTracks(htmlSources) {
|
|
10731
10735
|
const findings = [];
|
|
10732
|
-
function extractAttr(tag, name) {
|
|
10733
|
-
const re = new RegExp(`\\b${name}\\s*=\\s*["']([^"']+)["']`, "i");
|
|
10734
|
-
const m = tag.match(re);
|
|
10735
|
-
return m?.[1] ?? null;
|
|
10736
|
-
}
|
|
10737
10736
|
const tracks = [];
|
|
10738
10737
|
const seen = /* @__PURE__ */ new Set();
|
|
10739
10738
|
for (const { html } of htmlSources) {
|
|
@@ -10742,10 +10741,10 @@ function lintDuplicateAudioTracks(htmlSources) {
|
|
|
10742
10741
|
while ((match = soundTagRe.exec(html)) !== null) {
|
|
10743
10742
|
const tag = match[0];
|
|
10744
10743
|
if (/^<video/i.test(tag) && !isAudibleVideoTag(tag)) continue;
|
|
10745
|
-
const trackStr =
|
|
10746
|
-
const startStr =
|
|
10747
|
-
const durStr =
|
|
10748
|
-
const src =
|
|
10744
|
+
const trackStr = readAttr(tag, "data-track-index");
|
|
10745
|
+
const startStr = readAttr(tag, "data-start");
|
|
10746
|
+
const durStr = readAttr(tag, "data-duration");
|
|
10747
|
+
const src = readDecodedAttr(tag, "src") || "unknown";
|
|
10749
10748
|
if (!trackStr || !startStr) continue;
|
|
10750
10749
|
const trackIndex = parseInt(trackStr, 10);
|
|
10751
10750
|
const start = parseFloat(startStr);
|
|
@@ -10819,7 +10818,7 @@ function lintMissingOrEmptySubComposition(projectDir, rootHtml) {
|
|
|
10819
10818
|
code: "missing_or_empty_sub_composition",
|
|
10820
10819
|
severity: "error",
|
|
10821
10820
|
message: `data-composition-src references "${srcPath}", but ${problem}.`,
|
|
10822
|
-
fixHint: folder ? `Point data-composition-src at the HTML file inside the folder, such as "${srcPath}/index.html".` : `Fix this before rendering
|
|
10821
|
+
fixHint: folder ? `Point data-composition-src at the HTML file inside the folder, such as "${srcPath}/index.html".` : `Fix this before rendering: the render pre-flight rejects unusable sub-compositions. Write valid HTML into "${srcPath}". It needs a <template> or <body> containing an element with data-composition-id, data-width, and data-height. Preview/studio still tolerates and skips the scene while you author it. If a scene-authoring step is still running, wait for it to finish before referencing the file, or re-run the step that generates it.`
|
|
10823
10822
|
});
|
|
10824
10823
|
}
|
|
10825
10824
|
return findings;
|
|
@@ -58,7 +58,7 @@ function getCgroupMemoryLimitMb() {
|
|
|
58
58
|
_cachedCgroupLimitMb = parseCgroupLimitMb(v2Content, v1Content);
|
|
59
59
|
if (_cachedCgroupLimitMb !== null) {
|
|
60
60
|
console.warn(
|
|
61
|
-
`[SystemMemory] cgroup memory limit detected: ${_cachedCgroupLimitMb} MiB
|
|
61
|
+
`[SystemMemory] cgroup memory limit detected: ${_cachedCgroupLimitMb} MiB. It governs memory-adaptive render behaviour instead of host RAM.`
|
|
62
62
|
);
|
|
63
63
|
}
|
|
64
64
|
return _cachedCgroupLimitMb;
|
|
@@ -15,12 +15,12 @@ import {
|
|
|
15
15
|
findFFmpeg,
|
|
16
16
|
findFFprobe,
|
|
17
17
|
getFFmpegInstallHint
|
|
18
|
-
} from "./chunk-
|
|
18
|
+
} from "./chunk-SYUGVPYH.js";
|
|
19
19
|
import {
|
|
20
20
|
DEFAULT_VP9_CPU_USED,
|
|
21
21
|
SDR_RGB_TO_BT709_FILTER,
|
|
22
22
|
renderProvenanceArgs
|
|
23
|
-
} from "./chunk-
|
|
23
|
+
} from "./chunk-WU2B74TL.js";
|
|
24
24
|
|
|
25
25
|
// src/background-removal/pipeline.ts
|
|
26
26
|
import { spawn } from "child_process";
|
|
@@ -203,7 +203,7 @@ function inferInputKind(inputPath) {
|
|
|
203
203
|
}
|
|
204
204
|
async function probeMedia(inputPath) {
|
|
205
205
|
const isImage = inferInputKind(inputPath) === "image";
|
|
206
|
-
const engine = await import("./src-
|
|
206
|
+
const engine = await import("./src-LIERUCFW.js");
|
|
207
207
|
const meta = await engine.extractMediaMetadata(inputPath);
|
|
208
208
|
if (isImage) {
|
|
209
209
|
return { width: meta.width, height: meta.height, fps: 0, frameCount: 1 };
|
|
@@ -8,10 +8,10 @@ import {
|
|
|
8
8
|
DEFAULT_VP9_CPU_USED,
|
|
9
9
|
isHttpUrl,
|
|
10
10
|
runFfmpeg
|
|
11
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-WU2B74TL.js";
|
|
12
12
|
import {
|
|
13
13
|
parseAnimatedGifMetadata
|
|
14
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-BAKPN2D2.js";
|
|
15
15
|
import {
|
|
16
16
|
parseHTML
|
|
17
17
|
} from "./chunk-SDYDNHEE.js";
|
|
@@ -16,10 +16,10 @@ import {
|
|
|
16
16
|
import {
|
|
17
17
|
formatVariableValidationIssue,
|
|
18
18
|
validateVariables
|
|
19
|
-
} from "./chunk-
|
|
19
|
+
} from "./chunk-BAKPN2D2.js";
|
|
20
20
|
import {
|
|
21
21
|
extractCompositionMetadata
|
|
22
|
-
} from "./chunk-
|
|
22
|
+
} from "./chunk-LFNQDTSH.js";
|
|
23
23
|
import {
|
|
24
24
|
c
|
|
25
25
|
} from "./chunk-ZTZA4UWZ.js";
|
|
@@ -4357,7 +4357,7 @@ function buildAspectMismatch(compositionWidth, compositionHeight, outputResoluti
|
|
|
4357
4357
|
compositionHeight,
|
|
4358
4358
|
outputResolution
|
|
4359
4359
|
);
|
|
4360
|
-
const suggestion = suggestedResolution ? ` The composition is ${describeOrientation(compositionWidth, compositionHeight)}
|
|
4360
|
+
const suggestion = suggestedResolution ? ` The composition is ${describeOrientation(compositionWidth, compositionHeight)}, so use --resolution ${suggestedResolution} instead.` : ` Pick a preset whose orientation matches, or omit --resolution to render at the composition's native dimensions.`;
|
|
4361
4361
|
return {
|
|
4362
4362
|
ok: false,
|
|
4363
4363
|
kind: "aspect-mismatch",
|