dsh-drawai 0.3.3 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (533) hide show
  1. package/LICENSE +4142 -0
  2. package/README.md +43 -10
  3. package/lib/audio-media.js +147 -0
  4. package/lib/audio-service.js +66 -0
  5. package/lib/canvas-format.js +134 -0
  6. package/lib/client.js +5283 -223
  7. package/lib/comfyui-default-workflow.mjs +1825 -0
  8. package/lib/comfyui-workflow.mjs +426 -0
  9. package/lib/document-commands.js +12 -0
  10. package/lib/document-core.js +29 -0
  11. package/lib/drawing-tasks.js +26 -0
  12. package/lib/imagegen.mjs +596 -0
  13. package/lib/index.js +1758 -129
  14. package/lib/media.js +26 -13
  15. package/lib/mgstyle-core.js +75 -0
  16. package/lib/motion/apply.js +180 -0
  17. package/lib/motion/asset-pack-lock.js +10 -0
  18. package/lib/motion/asset-pack.js +335 -0
  19. package/lib/motion/assets.js +290 -0
  20. package/lib/motion/audio.js +103 -0
  21. package/lib/motion/audiolevel.js +63 -0
  22. package/lib/motion/audiomix.js +144 -0
  23. package/lib/motion/beats.js +337 -0
  24. package/lib/motion/builtin-assets.js +62 -0
  25. package/lib/motion/canvas-spec.js +442 -0
  26. package/lib/motion/capture.js +95 -0
  27. package/lib/motion/cards.js +80 -0
  28. package/lib/motion/collect.js +447 -0
  29. package/lib/motion/delivery.js +112 -0
  30. package/lib/motion/edge-speech.py +90 -0
  31. package/lib/motion/entry.jsx +73 -0
  32. package/lib/motion/export.js +200 -0
  33. package/lib/motion/ffmpeg.js +42 -0
  34. package/lib/motion/fonts.js +71 -0
  35. package/lib/motion/jobs.js +113 -0
  36. package/lib/motion/json.js +4 -0
  37. package/lib/motion/media-catalog.js +59 -0
  38. package/lib/motion/mgstyles/BRIEF-TEMPLATE.md +63 -0
  39. package/lib/motion/mgstyles/briefs/01-flat-vector.md +104 -0
  40. package/lib/motion/mgstyles/briefs/02-line-art.md +97 -0
  41. package/lib/motion/mgstyles/briefs/03-isometric.md +108 -0
  42. package/lib/motion/mgstyles/briefs/05-cel-boil.md +145 -0
  43. package/lib/motion/mgstyles/briefs/06-collage.md +149 -0
  44. package/lib/motion/mgstyles/briefs/07-liquid.md +106 -0
  45. package/lib/motion/mgstyles/briefs/08-morph.md +111 -0
  46. package/lib/motion/mgstyles/briefs/09-bauhaus.md +150 -0
  47. package/lib/motion/mgstyles/briefs/10-synthwave.md +151 -0
  48. package/lib/motion/mgstyles/briefs/12-aurora-glass.md +147 -0
  49. package/lib/motion/mgstyles/briefs/18-hanazi.md +140 -0
  50. package/lib/motion/mgstyles/briefs/19-paperclip.md +156 -0
  51. package/lib/motion/mgstyles/briefs/20-pixel.md +118 -0
  52. package/lib/motion/mgstyles/briefs/22-hud.md +118 -0
  53. package/lib/motion/mgstyles/catalog.d/01-flat-vector.json +34 -0
  54. package/lib/motion/mgstyles/catalog.d/02-line-art.json +34 -0
  55. package/lib/motion/mgstyles/catalog.d/03-isometric.json +35 -0
  56. package/lib/motion/mgstyles/catalog.d/05-cel-boil.json +28 -0
  57. package/lib/motion/mgstyles/catalog.d/06-collage.json +28 -0
  58. package/lib/motion/mgstyles/catalog.d/07-liquid.json +28 -0
  59. package/lib/motion/mgstyles/catalog.d/08-morph.json +28 -0
  60. package/lib/motion/mgstyles/catalog.d/09-bauhaus.json +28 -0
  61. package/lib/motion/mgstyles/catalog.d/10-synthwave.json +28 -0
  62. package/lib/motion/mgstyles/catalog.d/12-aurora-glass.json +28 -0
  63. package/lib/motion/mgstyles/catalog.d/18-hanazi.json +28 -0
  64. package/lib/motion/mgstyles/catalog.d/19-paperclip.json +28 -0
  65. package/lib/motion/mgstyles/catalog.d/20-pixel.json +28 -0
  66. package/lib/motion/mgstyles/catalog.d/22-hud.json +28 -0
  67. package/lib/motion/mgstyles/catalog.json +1356 -0
  68. package/lib/motion/mgstyles/upstream/README.md +147 -0
  69. package/lib/motion/mgstyles/upstream/README.zh-CN.md +147 -0
  70. package/lib/motion/mgstyles/upstream/demos/01-flat-vector/cues.json +1 -0
  71. package/lib/motion/mgstyles/upstream/demos/01-flat-vector/index.html +712 -0
  72. package/lib/motion/mgstyles/upstream/demos/02-line-art/cues.json +331 -0
  73. package/lib/motion/mgstyles/upstream/demos/02-line-art/film.js +676 -0
  74. package/lib/motion/mgstyles/upstream/demos/02-line-art/index.html +18 -0
  75. package/lib/motion/mgstyles/upstream/demos/02-line-art/tools/dump_timing.mjs +26 -0
  76. package/lib/motion/mgstyles/upstream/demos/03-isometric/cues.json +109 -0
  77. package/lib/motion/mgstyles/upstream/demos/03-isometric/index.html +25 -0
  78. package/lib/motion/mgstyles/upstream/demos/03-isometric/js/anim.js +82 -0
  79. package/lib/motion/mgstyles/upstream/demos/03-isometric/js/main.js +219 -0
  80. package/lib/motion/mgstyles/upstream/demos/03-isometric/js/night5.js +180 -0
  81. package/lib/motion/mgstyles/upstream/demos/03-isometric/js/render.js +202 -0
  82. package/lib/motion/mgstyles/upstream/demos/03-isometric/js/title.js +167 -0
  83. package/lib/motion/mgstyles/upstream/demos/03-isometric/js/title5.js +294 -0
  84. package/lib/motion/mgstyles/upstream/demos/03-isometric/js/ui.js +141 -0
  85. package/lib/motion/mgstyles/upstream/demos/03-isometric/js/world.js +1399 -0
  86. package/lib/motion/mgstyles/upstream/demos/05-cel-boil/cues.json +157 -0
  87. package/lib/motion/mgstyles/upstream/demos/05-cel-boil/index.html +34 -0
  88. package/lib/motion/mgstyles/upstream/demos/05-cel-boil/js/engine.js +326 -0
  89. package/lib/motion/mgstyles/upstream/demos/05-cel-boil/js/scene.js +2036 -0
  90. package/lib/motion/mgstyles/upstream/demos/06-collage/assets/gen/layout.json +157 -0
  91. package/lib/motion/mgstyles/upstream/demos/06-collage/cues.json +80 -0
  92. package/lib/motion/mgstyles/upstream/demos/06-collage/index.html +889 -0
  93. package/lib/motion/mgstyles/upstream/demos/07-liquid/cues.json +100 -0
  94. package/lib/motion/mgstyles/upstream/demos/07-liquid/index.html +108 -0
  95. package/lib/motion/mgstyles/upstream/demos/07-liquid/js/engine.js +153 -0
  96. package/lib/motion/mgstyles/upstream/demos/07-liquid/js/links.js +64 -0
  97. package/lib/motion/mgstyles/upstream/demos/07-liquid/js/lookdev.js +46 -0
  98. package/lib/motion/mgstyles/upstream/demos/07-liquid/js/scene.js +963 -0
  99. package/lib/motion/mgstyles/upstream/demos/07-liquid/js/shaders.js +647 -0
  100. package/lib/motion/mgstyles/upstream/demos/07-liquid/js/textsdf.js +114 -0
  101. package/lib/motion/mgstyles/upstream/demos/07-liquid/js/type.js +50 -0
  102. package/lib/motion/mgstyles/upstream/demos/08-morph/cues.json +131 -0
  103. package/lib/motion/mgstyles/upstream/demos/08-morph/glyphs.json +1 -0
  104. package/lib/motion/mgstyles/upstream/demos/08-morph/index.html +901 -0
  105. package/lib/motion/mgstyles/upstream/demos/09-bauhaus/cues.json +270 -0
  106. package/lib/motion/mgstyles/upstream/demos/09-bauhaus/film.js +697 -0
  107. package/lib/motion/mgstyles/upstream/demos/09-bauhaus/index.html +12 -0
  108. package/lib/motion/mgstyles/upstream/demos/10-synthwave/cues.json +44 -0
  109. package/lib/motion/mgstyles/upstream/demos/10-synthwave/index.html +1028 -0
  110. package/lib/motion/mgstyles/upstream/demos/12-aurora-glass/cues.json +67 -0
  111. package/lib/motion/mgstyles/upstream/demos/12-aurora-glass/index.html +16 -0
  112. package/lib/motion/mgstyles/upstream/demos/12-aurora-glass/main.js +967 -0
  113. package/lib/motion/mgstyles/upstream/demos/12-aurora-glass/shaders.js +468 -0
  114. package/lib/motion/mgstyles/upstream/demos/18-hanazi/cues.json +117 -0
  115. package/lib/motion/mgstyles/upstream/demos/18-hanazi/index.html +541 -0
  116. package/lib/motion/mgstyles/upstream/demos/19-paperclip/assets/countries-110m.json +1 -0
  117. package/lib/motion/mgstyles/upstream/demos/19-paperclip/assets/stickers/meta.json +34 -0
  118. package/lib/motion/mgstyles/upstream/demos/19-paperclip/cues.json +285 -0
  119. package/lib/motion/mgstyles/upstream/demos/19-paperclip/index.html +599 -0
  120. package/lib/motion/mgstyles/upstream/demos/20-pixel/cues.json +42 -0
  121. package/lib/motion/mgstyles/upstream/demos/20-pixel/film.js +914 -0
  122. package/lib/motion/mgstyles/upstream/demos/20-pixel/index.html +18 -0
  123. package/lib/motion/mgstyles/upstream/demos/22-hud/cues.json +108 -0
  124. package/lib/motion/mgstyles/upstream/demos/22-hud/index.html +125 -0
  125. package/lib/motion/mgstyles/upstream/demos/22-hud/js/holo.js +275 -0
  126. package/lib/motion/mgstyles/upstream/demos/22-hud/js/hud.js +855 -0
  127. package/lib/motion/mgstyles/upstream/demos/22-hud/js/post.js +150 -0
  128. package/lib/motion/mgstyles/upstream/demos/22-hud/js/util.js +65 -0
  129. package/lib/motion/mgstyles/upstream/demos/22-hud/js/v5.js +142 -0
  130. package/lib/motion/mgstyles/upstream/harness/render.mjs +319 -0
  131. package/lib/motion/mgstyles/upstream/manifest.json +269 -0
  132. package/lib/motion/mgstyles/upstream/prompts/01-flat-vector.md +98 -0
  133. package/lib/motion/mgstyles/upstream/prompts/02-line-art.md +95 -0
  134. package/lib/motion/mgstyles/upstream/prompts/03-isometric.md +97 -0
  135. package/lib/motion/mgstyles/upstream/prompts/05-cel-boil.md +95 -0
  136. package/lib/motion/mgstyles/upstream/prompts/06-collage.md +95 -0
  137. package/lib/motion/mgstyles/upstream/prompts/07-liquid.md +98 -0
  138. package/lib/motion/mgstyles/upstream/prompts/08-morph.md +95 -0
  139. package/lib/motion/mgstyles/upstream/prompts/09-bauhaus.md +96 -0
  140. package/lib/motion/mgstyles/upstream/prompts/10-synthwave.md +95 -0
  141. package/lib/motion/mgstyles/upstream/prompts/12-aurora-glass.md +96 -0
  142. package/lib/motion/mgstyles/upstream/prompts/18-hanazi.md +95 -0
  143. package/lib/motion/mgstyles/upstream/prompts/19-paperclip.md +96 -0
  144. package/lib/motion/mgstyles/upstream/prompts/20-pixel.md +96 -0
  145. package/lib/motion/mgstyles/upstream/prompts/22-hud.md +95 -0
  146. package/lib/motion/mgstyles/upstream/rubric.md +51 -0
  147. package/lib/motion/mgstyles/upstream/template.md +36 -0
  148. package/lib/motion/pixelqa.js +198 -0
  149. package/lib/motion/preview.js +95 -0
  150. package/lib/motion/project.js +395 -0
  151. package/lib/motion/qa.js +290 -0
  152. package/lib/motion/reference-media.jsx +10 -0
  153. package/lib/motion/render-settings.js +112 -0
  154. package/lib/motion/render.js +159 -0
  155. package/lib/motion/scene-kit.js +183 -0
  156. package/lib/motion/sfx.js +232 -0
  157. package/lib/motion/shotcraft/ClipCard.tsx +204 -0
  158. package/lib/motion/shotcraft/catalog.json +2461 -0
  159. package/lib/motion/shotcraft/upstream/SKILL.md +286 -0
  160. package/lib/motion/shotcraft/upstream/assets/lib/Caption.tsx +54 -0
  161. package/lib/motion/shotcraft/upstream/assets/lib/ClipCard.tsx +200 -0
  162. package/lib/motion/shotcraft/upstream/assets/lib/DigitRoll.tsx +45 -0
  163. package/lib/motion/shotcraft/upstream/assets/lib/FlashCut.tsx +20 -0
  164. package/lib/motion/shotcraft/upstream/assets/lib/FlatPanel.tsx +65 -0
  165. package/lib/motion/shotcraft/upstream/assets/lib/PageCam.tsx +151 -0
  166. package/lib/motion/shotcraft/upstream/assets/lib/VerticalTicker.tsx +129 -0
  167. package/lib/motion/shotcraft/upstream/assets/lib/helpers/camera.tsx +75 -0
  168. package/lib/motion/shotcraft/upstream/assets/lib/helpers/motion.ts +40 -0
  169. package/lib/motion/shotcraft/upstream/assets/lib/helpers/rand.ts +11 -0
  170. package/lib/motion/shotcraft/upstream/assets/lib/helpers/shake.ts +10 -0
  171. package/lib/motion/shotcraft/upstream/assets/scripts/__pycache__/sfx-source.cpython-313.pyc +0 -0
  172. package/lib/motion/shotcraft/upstream/assets/scripts/build-readme-cover.mjs +49 -0
  173. package/lib/motion/shotcraft/upstream/assets/scripts/capture-template.mjs +210 -0
  174. package/lib/motion/shotcraft/upstream/assets/scripts/sfx-source.py +423 -0
  175. package/lib/motion/shotcraft/upstream/assets/scripts/smoke-render-demos.py +170 -0
  176. package/lib/motion/shotcraft/upstream/assets/scripts/test_sfx_source.py +157 -0
  177. package/lib/motion/shotcraft/upstream/demos/README.md +120 -0
  178. package/lib/motion/shotcraft/upstream/demos/_fixtures/Brand.tsx +112 -0
  179. package/lib/motion/shotcraft/upstream/demos/_fixtures/Fixtures.tsx +647 -0
  180. package/lib/motion/shotcraft/upstream/demos/_fixtures/Look.tsx +316 -0
  181. package/lib/motion/shotcraft/upstream/demos/_fixtures/Motion.tsx +82 -0
  182. package/lib/motion/shotcraft/upstream/demos/_fixtures/PageCam2D.tsx +115 -0
  183. package/lib/motion/shotcraft/upstream/demos/_fixtures/Polish.tsx +243 -0
  184. package/lib/motion/shotcraft/upstream/demos/_textures/live-layout.json +389 -0
  185. package/lib/motion/shotcraft/upstream/demos/camera/basic-3d-scene/Basic3DScene.tsx +250 -0
  186. package/lib/motion/shotcraft/upstream/demos/camera/crash-zoom-punch/CrashImpactReal.tsx +305 -0
  187. package/lib/motion/shotcraft/upstream/demos/camera/crash-zoom-punch/CrashZoomReal.tsx +246 -0
  188. package/lib/motion/shotcraft/upstream/demos/camera/cursor-flyover/CursorFlyover.tsx +390 -0
  189. package/lib/motion/shotcraft/upstream/demos/camera/depth-layer-moves/MultiplaneReal.tsx +138 -0
  190. package/lib/motion/shotcraft/upstream/demos/camera/graze-face-tour/GrazeFaceTour.tsx +542 -0
  191. package/lib/motion/shotcraft/upstream/demos/camera/overhead-camera-moves/OverheadTabletopDrop.tsx +315 -0
  192. package/lib/motion/shotcraft/upstream/demos/camera/overhead-camera-moves/TiltReveal.tsx +299 -0
  193. package/lib/motion/shotcraft/upstream/demos/camera/space-camera-moves/DroneDiveLanding.tsx +173 -0
  194. package/lib/motion/shotcraft/upstream/demos/camera/space-camera-moves/ExplodedView.tsx +348 -0
  195. package/lib/motion/shotcraft/upstream/demos/camera/steep-tilt-glide/SteepTiltGlide.tsx +366 -0
  196. package/lib/motion/shotcraft/upstream/demos/camera/tension-camera-moves/BulletTimeFreezeOrbit.tsx +279 -0
  197. package/lib/motion/shotcraft/upstream/demos/camera/tension-camera-moves/PullBackIsolation.tsx +184 -0
  198. package/lib/motion/shotcraft/upstream/demos/camera/terminal-3d/Terminal3D.tsx +390 -0
  199. package/lib/motion/shotcraft/upstream/demos/data/before-after-slider-scrub/BeforeAfterSliderScrub.tsx +155 -0
  200. package/lib/motion/shotcraft/upstream/demos/data/chart-live-moves/AxisRescaleShockV2.tsx +303 -0
  201. package/lib/motion/shotcraft/upstream/demos/data/chart-live-moves/OscilloscopeStreamV2.tsx +290 -0
  202. package/lib/motion/shotcraft/upstream/demos/data/chart-live-moves/UnitDotSwarmRegroupV2.tsx +278 -0
  203. package/lib/motion/shotcraft/upstream/demos/data/gauge-readout-moves/NeedleSweepSelftest.tsx +307 -0
  204. package/lib/motion/shotcraft/upstream/demos/data/gauge-readout-moves/TapeScrollFixedPointer.tsx +215 -0
  205. package/lib/motion/shotcraft/upstream/demos/data/scroll-brake-moves/BrakeReticleLock.tsx +299 -0
  206. package/lib/motion/shotcraft/upstream/demos/data/scroll-brake-moves/ChangelogScrollBrake.tsx +232 -0
  207. package/lib/motion/shotcraft/upstream/demos/data/timeline-travel/TimelineTravel.tsx +290 -0
  208. package/lib/motion/shotcraft/upstream/demos/effects/assemble-then-type-flyin/AssembleThenTypeFlyin.tsx +273 -0
  209. package/lib/motion/shotcraft/upstream/demos/effects/aurora-bloom-bg-flip/AuroraBloomBgFlip.tsx +167 -0
  210. package/lib/motion/shotcraft/upstream/demos/effects/brand-frame-snap/BrandFrameSnap.tsx +536 -0
  211. package/lib/motion/shotcraft/upstream/demos/effects/fui-hud-moves/ReticleLockOn.tsx +164 -0
  212. package/lib/motion/shotcraft/upstream/demos/effects/glow-flyline-moves/FlylineArc.tsx +175 -0
  213. package/lib/motion/shotcraft/upstream/demos/effects/glow-flyline-moves/GlowOrbAmbient.tsx +197 -0
  214. package/lib/motion/shotcraft/upstream/demos/effects/glow-flyline-moves/OrbFlylineRelay.tsx +241 -0
  215. package/lib/motion/shotcraft/upstream/demos/effects/icon-performance-moves/AttentionBounce.tsx +279 -0
  216. package/lib/motion/shotcraft/upstream/demos/effects/impact-feedback/AnimeImpact.tsx +230 -0
  217. package/lib/motion/shotcraft/upstream/demos/effects/impact-feedback/HitCounter.tsx +345 -0
  218. package/lib/motion/shotcraft/upstream/demos/effects/light-play-moves/SheenSweepRetry.tsx +275 -0
  219. package/lib/motion/shotcraft/upstream/demos/effects/light-play-moves/SpotlightSweepReveal.tsx +229 -0
  220. package/lib/motion/shotcraft/upstream/demos/effects/radial-ripple-phone-chips/RadialRipplePhoneChips.tsx +312 -0
  221. package/lib/motion/shotcraft/upstream/demos/effects/riso-print-hits/RisoMisregistrationHit.tsx +222 -0
  222. package/lib/motion/shotcraft/upstream/demos/effects/scan-bracket-sweep/ScanBracketSweep.tsx +295 -0
  223. package/lib/motion/shotcraft/upstream/demos/effects/scanline-annotate-focus/ScanlineAnnotateFocus.tsx +329 -0
  224. package/lib/motion/shotcraft/upstream/demos/effects/scanline-assemble-flyin/ScanlineAssembleFlyin.tsx +315 -0
  225. package/lib/motion/shotcraft/upstream/demos/effects/slam-entrance-moves/ImpactBurstKit.tsx +264 -0
  226. package/lib/motion/shotcraft/upstream/demos/effects/slam-entrance-moves/KanadaPerspectiveSnap.tsx +260 -0
  227. package/lib/motion/shotcraft/upstream/demos/effects/slam-entrance-moves/ScoreSlam.tsx +263 -0
  228. package/lib/motion/shotcraft/upstream/demos/effects/spotlight-sweep-moves/CornerSpotlightReveal.tsx +123 -0
  229. package/lib/motion/shotcraft/upstream/demos/effects/spotlight-sweep-moves/GlowWakeSleepPanel.tsx +187 -0
  230. package/lib/motion/shotcraft/upstream/demos/effects/spotlight-sweep-moves/SlideSpotlightPan.tsx +175 -0
  231. package/lib/motion/shotcraft/upstream/demos/interaction/autolayout-gap-dial/AutolayoutGapDial.tsx +305 -0
  232. package/lib/motion/shotcraft/upstream/demos/interaction/canvas-materialize-moves/DiagramCascadeBuild.tsx +350 -0
  233. package/lib/motion/shotcraft/upstream/demos/interaction/canvas-materialize-moves/PanelToCanvasMaterialize.tsx +352 -0
  234. package/lib/motion/shotcraft/upstream/demos/interaction/chip-grid-single-select-blackout/ChipGridSingleSelectBlackout.tsx +218 -0
  235. package/lib/motion/shotcraft/upstream/demos/interaction/collab-cursor-moves/CursorCastEnsemble.tsx +297 -0
  236. package/lib/motion/shotcraft/upstream/demos/interaction/glass-pill-dictation-typing/GlassPillDictationTyping.tsx +157 -0
  237. package/lib/motion/shotcraft/upstream/demos/interaction/hashtag-to-pill-materialize/HashtagToPillMaterialize.tsx +218 -0
  238. package/lib/motion/shotcraft/upstream/demos/interaction/picker-carousel-feature-cycle/PickerCarouselFeatureCycle.tsx +202 -0
  239. package/lib/motion/shotcraft/upstream/demos/interaction/segmented-thumb-hero/SegmentedThumbHero.tsx +265 -0
  240. package/lib/motion/shotcraft/upstream/demos/interaction/theme-switch-moves/PaletteThemeRipple.tsx +457 -0
  241. package/lib/motion/shotcraft/upstream/demos/interaction/theme-switch-moves/ThemeSweepToggle.tsx +446 -0
  242. package/lib/motion/shotcraft/upstream/demos/interaction/type-and-filter/TypeAndFilter.tsx +249 -0
  243. package/lib/motion/shotcraft/upstream/demos/interaction/voice-waveform-live/VoiceWaveformLive.tsx +278 -0
  244. package/lib/motion/shotcraft/upstream/demos/opening/crane-rise-reveal/CraneRiseReveal.tsx +332 -0
  245. package/lib/motion/shotcraft/upstream/demos/opening/dataviz-landscape-open/DatavizLandscapeOpen.tsx +358 -0
  246. package/lib/motion/shotcraft/upstream/demos/opening/fracture/Fracture.tsx +201 -0
  247. package/lib/motion/shotcraft/upstream/demos/opening/icon-field-colorize/IconFieldColorize.tsx +200 -0
  248. package/lib/motion/shotcraft/upstream/demos/opening/letterspace-materialize/LetterspaceMaterialize.tsx +208 -0
  249. package/lib/motion/shotcraft/upstream/demos/opening/magician-card-flourish/MagicianCardFlourish.tsx +341 -0
  250. package/lib/motion/shotcraft/upstream/demos/opening/orbit-ring-title-open/OrbitRingTitleOpen.tsx +389 -0
  251. package/lib/motion/shotcraft/upstream/demos/opening/spotlight-hero-card/SpotlightHeroCard.tsx +293 -0
  252. package/lib/motion/shotcraft/upstream/demos/opening/stroke-segment-build/StrokeSegmentBuild.tsx +183 -0
  253. package/lib/motion/shotcraft/upstream/demos/opening/text-as-mask/TextAsMask.tsx +245 -0
  254. package/lib/motion/shotcraft/upstream/demos/outro/grain-dissolve/GrainDissolve.tsx +222 -0
  255. package/lib/motion/shotcraft/upstream/demos/outro/logo-shrink-wordmark-lockup/LogoShrinkWordmarkLockup.tsx +223 -0
  256. package/lib/motion/shotcraft/upstream/demos/outro/neon-triple-marquee/NeonTripleMarquee.tsx +217 -0
  257. package/lib/motion/shotcraft/upstream/demos/outro/outro-group-photo-launch/OutroGroupPhotoLaunch.tsx +343 -0
  258. package/lib/motion/shotcraft/upstream/demos/outro/ui-strip-away-outro/UiStripAwayOutro.tsx +357 -0
  259. package/lib/motion/shotcraft/upstream/demos/outro/ui-to-brand-morph/IconFlipBloomLogo.tsx +187 -0
  260. package/lib/motion/shotcraft/upstream/demos/outro/ui-to-brand-morph/InputMorphsIntoLogo.tsx +333 -0
  261. package/lib/motion/shotcraft/upstream/demos/rhythm/beat-cut-moves/PaparazziFlash.tsx +274 -0
  262. package/lib/motion/shotcraft/upstream/demos/rhythm/beat-step-list-theme-cycle/BeatStepListThemeCycle.tsx +270 -0
  263. package/lib/motion/shotcraft/upstream/demos/rhythm/panel-grid-moves/ComicPanelSplit.tsx +430 -0
  264. package/lib/motion/shotcraft/upstream/demos/rhythm/quad-split-parallel-scenes/QuadSplitParallelScenes.tsx +252 -0
  265. package/lib/motion/shotcraft/upstream/demos/rhythm/rhythm-interrupt-moves/JumpCutPunchIn.tsx +106 -0
  266. package/lib/motion/shotcraft/upstream/demos/rhythm/sakuga-timing-shift/SakugaTimingShift.tsx +288 -0
  267. package/lib/motion/shotcraft/upstream/demos/rhythm/speed-ramp-freeze/FreezeAnnotateReal.tsx +362 -0
  268. package/lib/motion/shotcraft/upstream/demos/rhythm/trailer-grammar-moves/TrailerBumper.tsx +168 -0
  269. package/lib/motion/shotcraft/upstream/demos/transition/bottom-push-stack-wipe/BottomPushStackWipe.tsx +310 -0
  270. package/lib/motion/shotcraft/upstream/demos/transition/card-flip-reveal/CardFlipReveal.tsx +261 -0
  271. package/lib/motion/shotcraft/upstream/demos/transition/card-flock-tumble/CardFlockTumble.tsx +459 -0
  272. package/lib/motion/shotcraft/upstream/demos/transition/circle-match-iris/CircleMatchIris.tsx +294 -0
  273. package/lib/motion/shotcraft/upstream/demos/transition/gradient-transition/GradientTransition.tsx +161 -0
  274. package/lib/motion/shotcraft/upstream/demos/transition/line-carry-transition/LineCarryTransition.tsx +248 -0
  275. package/lib/motion/shotcraft/upstream/demos/transition/mosaic-reframe/MosaicReframe.tsx +359 -0
  276. package/lib/motion/shotcraft/upstream/demos/transition/page-turn-transitions/BarnDoorSplit.tsx +227 -0
  277. package/lib/motion/shotcraft/upstream/demos/transition/print-texture-transitions/InkBleedReveal.tsx +162 -0
  278. package/lib/motion/shotcraft/upstream/demos/transition/shot-transitions/PortalWipeV2.tsx +248 -0
  279. package/lib/motion/shotcraft/upstream/demos/transition/shot-transitions/WhipBrakeReal.tsx +186 -0
  280. package/lib/motion/shotcraft/upstream/demos/transition/tear-streak-transitions/GlitchDisplace.tsx +355 -0
  281. package/lib/motion/shotcraft/upstream/demos/transition/transition-hidden-cut/InvisibleCut.tsx +272 -0
  282. package/lib/motion/shotcraft/upstream/demos/transition/transition-hidden-cut/LightLeakBurn.tsx +262 -0
  283. package/lib/motion/shotcraft/upstream/demos/transition/transition-hidden-cut/VersusSlam.tsx +217 -0
  284. package/lib/motion/shotcraft/upstream/demos/transition/transition-travel/LetterformZoom.tsx +285 -0
  285. package/lib/motion/shotcraft/upstream/demos/transition/white-flash-logo-simplify-cut/WhiteFlashLogoSimplifyCut.tsx +205 -0
  286. package/lib/motion/shotcraft/upstream/demos/transition/wipe-transitions/BlindsSlice.tsx +178 -0
  287. package/lib/motion/shotcraft/upstream/demos/typography/blur-slide/BlurSlide.tsx +147 -0
  288. package/lib/motion/shotcraft/upstream/demos/typography/brace-expand/BraceExpand.tsx +143 -0
  289. package/lib/motion/shotcraft/upstream/demos/typography/brand-ink-open/BrandInkOpen.tsx +134 -0
  290. package/lib/motion/shotcraft/upstream/demos/typography/countdown-arc-scatter/CountdownArcScatter.tsx +217 -0
  291. package/lib/motion/shotcraft/upstream/demos/typography/flying-words/FlyingWords.tsx +198 -0
  292. package/lib/motion/shotcraft/upstream/demos/typography/glitch-cycle/GlitchCycle.tsx +221 -0
  293. package/lib/motion/shotcraft/upstream/demos/typography/gradient-word-sweep/GradientWordSweep.tsx +312 -0
  294. package/lib/motion/shotcraft/upstream/demos/typography/lead-word-zoom-assemble/LeadWordZoomAssemble.tsx +261 -0
  295. package/lib/motion/shotcraft/upstream/demos/typography/marker-underline-title/MarkerUnderlineTitle.tsx +121 -0
  296. package/lib/motion/shotcraft/upstream/demos/typography/outline-word-fill/OutlineWordFill.tsx +153 -0
  297. package/lib/motion/shotcraft/upstream/demos/typography/paper-title-card/PaperTitleCard.tsx +179 -0
  298. package/lib/motion/shotcraft/upstream/demos/typography/pill-chip-slot-cycle-handled/PillChipSlotCycleHandled.tsx +140 -0
  299. package/lib/motion/shotcraft/upstream/demos/typography/pill-slot-cycle/PillSlotCycle.tsx +309 -0
  300. package/lib/motion/shotcraft/upstream/demos/typography/text-column-converge/TextColumnConverge.tsx +191 -0
  301. package/lib/motion/shotcraft/upstream/demos/typography/title-demote-to-label/TitleDemoteToLabel.tsx +292 -0
  302. package/lib/motion/shotcraft/upstream/demos/typography/type-assembly-moves/LetterformDriftAssembly.tsx +135 -0
  303. package/lib/motion/shotcraft/upstream/demos/typography/type-assembly-moves/SplitTextStagger.tsx +124 -0
  304. package/lib/motion/shotcraft/upstream/demos/typography/type-assembly-moves/TextOnPath.tsx +255 -0
  305. package/lib/motion/shotcraft/upstream/demos/typography/type-entrance-moves/LetterDropPhysics.tsx +173 -0
  306. package/lib/motion/shotcraft/upstream/demos/typography/type-rhythm-sync/FontWeightPump.tsx +225 -0
  307. package/lib/motion/shotcraft/upstream/demos/typography/type-rhythm-sync/KaraokeFillSync.tsx +141 -0
  308. package/lib/motion/shotcraft/upstream/demos/typography/typewriter-moves/TypewriterErrorRetype.tsx +137 -0
  309. package/lib/motion/shotcraft/upstream/demos/typography/vertical-word-roll-blur-cycle/VerticalWordRollBlurCycle.tsx +147 -0
  310. package/lib/motion/shotcraft/upstream/demos/typography/word-relay-filmstrip/WordRelayFilmstrip.tsx +409 -0
  311. package/lib/motion/shotcraft/upstream/demos/typography/word-relay-geometry/WordRelayGeometry.tsx +280 -0
  312. package/lib/motion/shotcraft/upstream/demos/ui-entrance/avatar-bracket-carousel/AvatarBracketCarousel.tsx +271 -0
  313. package/lib/motion/shotcraft/upstream/demos/ui-entrance/bezier-source-converge-merge/BezierSourceConvergeMerge.tsx +385 -0
  314. package/lib/motion/shotcraft/upstream/demos/ui-entrance/card-stack/CardStack.tsx +312 -0
  315. package/lib/motion/shotcraft/upstream/demos/ui-entrance/carousel-3d/Carousel3D.tsx +303 -0
  316. package/lib/motion/shotcraft/upstream/demos/ui-entrance/clipcard-looping/ClipCardLooping.tsx +77 -0
  317. package/lib/motion/shotcraft/upstream/demos/ui-entrance/cloner-depth-echo/ClonerDepthEcho.tsx +311 -0
  318. package/lib/motion/shotcraft/upstream/demos/ui-entrance/deck-deal-flyin/DeckDealFlyin.tsx +307 -0
  319. package/lib/motion/shotcraft/upstream/demos/ui-entrance/doc-park-left-pill-deal/DocParkLeftPillDeal.tsx +349 -0
  320. package/lib/motion/shotcraft/upstream/demos/ui-entrance/document-typewriter-reveal/DocumentTypewriterReveal.tsx +255 -0
  321. package/lib/motion/shotcraft/upstream/demos/ui-entrance/draw-svg-trace/DrawSvgTrace.tsx +244 -0
  322. package/lib/motion/shotcraft/upstream/demos/ui-entrance/element-body-moves/ContactShadowLift.tsx +217 -0
  323. package/lib/motion/shotcraft/upstream/demos/ui-entrance/integration-hub-map/IntegrationHubMap.tsx +652 -0
  324. package/lib/motion/shotcraft/upstream/demos/ui-entrance/list-reveal/ListReveal.tsx +153 -0
  325. package/lib/motion/shotcraft/upstream/demos/ui-entrance/list-stack-press/ListStackPress.tsx +238 -0
  326. package/lib/motion/shotcraft/upstream/demos/ui-entrance/neon-frame-forerun/NeonFrameForerun.tsx +324 -0
  327. package/lib/motion/shotcraft/upstream/demos/ui-entrance/neon-frame-orbit-drop/NeonFrameForerunOrbit.tsx +354 -0
  328. package/lib/motion/shotcraft/upstream/demos/ui-entrance/page-waterfall-wall/PageWaterfallWall.tsx +101 -0
  329. package/lib/motion/shotcraft/upstream/demos/ui-entrance/page-waterfall-wall/VerticalTicker.tsx +129 -0
  330. package/lib/motion/shotcraft/upstream/demos/ui-entrance/product-card-progressive-assemble/ProductCardProgressiveAssemble.tsx +297 -0
  331. package/lib/motion/shotcraft/upstream/demos/ui-entrance/radial-wave/RadialWave.tsx +57 -0
  332. package/lib/motion/shotcraft/upstream/demos/ui-entrance/research-card-stack-scroll/ResearchCardStackScroll.tsx +155 -0
  333. package/lib/motion/shotcraft/upstream/demos/ui-entrance/row-embed/RowEmbed.tsx +175 -0
  334. package/lib/motion/shotcraft/upstream/demos/ui-entrance/value-stagger-gradient/ValueStaggerGradient.tsx +153 -0
  335. package/lib/motion/shotcraft/upstream/demos/ui-entrance/wall-reveal-moves/BentoLightUp.tsx +146 -0
  336. package/lib/motion/shotcraft/upstream/demos/ui-entrance/wall-reveal-moves/WireframeDrawOn.tsx +226 -0
  337. package/lib/motion/shotcraft/upstream/gallery/api/library.json +1 -0
  338. package/lib/motion/shotcraft/upstream/manifest.json +2715 -0
  339. package/lib/motion/shotcraft/upstream/references/aesthetic-rules.md +193 -0
  340. package/lib/motion/shotcraft/upstream/references/final-review.md +121 -0
  341. package/lib/motion/shotcraft/upstream/references/guided-free-creation.md +199 -0
  342. package/lib/motion/shotcraft/upstream/references/jianying-export.md +183 -0
  343. package/lib/motion/shotcraft/upstream/references/music-beat-sync.md +257 -0
  344. package/lib/motion/shotcraft/upstream/references/pipeline.md +382 -0
  345. package/lib/motion/shotcraft/upstream/references/publish-upload-post.md +111 -0
  346. package/lib/motion/shotcraft/upstream/references/sequences/promo-energy-arc.md +50 -0
  347. package/lib/motion/shotcraft/upstream/references/sfx-sourcing.md +95 -0
  348. package/lib/motion/shotcraft/upstream/references/shots/camera/basic-3d-scene.md +41 -0
  349. package/lib/motion/shotcraft/upstream/references/shots/camera/crash-zoom-punch.md +52 -0
  350. package/lib/motion/shotcraft/upstream/references/shots/camera/cursor-flyover.md +39 -0
  351. package/lib/motion/shotcraft/upstream/references/shots/camera/depth-layer-moves.md +39 -0
  352. package/lib/motion/shotcraft/upstream/references/shots/camera/graze-face-tour.md +57 -0
  353. package/lib/motion/shotcraft/upstream/references/shots/camera/overhead-camera-moves.md +51 -0
  354. package/lib/motion/shotcraft/upstream/references/shots/camera/space-camera-moves.md +46 -0
  355. package/lib/motion/shotcraft/upstream/references/shots/camera/steep-tilt-glide.md +52 -0
  356. package/lib/motion/shotcraft/upstream/references/shots/camera/tension-camera-moves.md +43 -0
  357. package/lib/motion/shotcraft/upstream/references/shots/camera/terminal-3d.md +42 -0
  358. package/lib/motion/shotcraft/upstream/references/shots/data/before-after-slider-scrub.md +48 -0
  359. package/lib/motion/shotcraft/upstream/references/shots/data/chart-live-moves.md +52 -0
  360. package/lib/motion/shotcraft/upstream/references/shots/data/gauge-readout-moves.md +45 -0
  361. package/lib/motion/shotcraft/upstream/references/shots/data/scroll-brake-moves.md +46 -0
  362. package/lib/motion/shotcraft/upstream/references/shots/data/timeline-travel.md +44 -0
  363. package/lib/motion/shotcraft/upstream/references/shots/effects/assemble-then-type-flyin.md +63 -0
  364. package/lib/motion/shotcraft/upstream/references/shots/effects/aurora-bloom-bg-flip.md +54 -0
  365. package/lib/motion/shotcraft/upstream/references/shots/effects/brand-frame-snap.md +58 -0
  366. package/lib/motion/shotcraft/upstream/references/shots/effects/fui-hud-moves.md +35 -0
  367. package/lib/motion/shotcraft/upstream/references/shots/effects/glow-flyline-moves.md +53 -0
  368. package/lib/motion/shotcraft/upstream/references/shots/effects/icon-performance-moves.md +42 -0
  369. package/lib/motion/shotcraft/upstream/references/shots/effects/impact-feedback.md +50 -0
  370. package/lib/motion/shotcraft/upstream/references/shots/effects/light-play-moves.md +46 -0
  371. package/lib/motion/shotcraft/upstream/references/shots/effects/radial-ripple-phone-chips.md +56 -0
  372. package/lib/motion/shotcraft/upstream/references/shots/effects/riso-print-hits.md +49 -0
  373. package/lib/motion/shotcraft/upstream/references/shots/effects/scan-bracket-sweep.md +50 -0
  374. package/lib/motion/shotcraft/upstream/references/shots/effects/scanline-annotate-focus.md +53 -0
  375. package/lib/motion/shotcraft/upstream/references/shots/effects/scanline-assemble-flyin.md +53 -0
  376. package/lib/motion/shotcraft/upstream/references/shots/effects/slam-entrance-moves.md +55 -0
  377. package/lib/motion/shotcraft/upstream/references/shots/effects/spotlight-sweep-moves.md +52 -0
  378. package/lib/motion/shotcraft/upstream/references/shots/interaction/autolayout-gap-dial.md +60 -0
  379. package/lib/motion/shotcraft/upstream/references/shots/interaction/canvas-materialize-moves.md +57 -0
  380. package/lib/motion/shotcraft/upstream/references/shots/interaction/chip-grid-single-select-blackout.md +51 -0
  381. package/lib/motion/shotcraft/upstream/references/shots/interaction/collab-cursor-moves.md +41 -0
  382. package/lib/motion/shotcraft/upstream/references/shots/interaction/glass-pill-dictation-typing.md +53 -0
  383. package/lib/motion/shotcraft/upstream/references/shots/interaction/hashtag-to-pill-materialize.md +60 -0
  384. package/lib/motion/shotcraft/upstream/references/shots/interaction/picker-carousel-feature-cycle.md +55 -0
  385. package/lib/motion/shotcraft/upstream/references/shots/interaction/segmented-thumb-hero.md +57 -0
  386. package/lib/motion/shotcraft/upstream/references/shots/interaction/theme-switch-moves.md +47 -0
  387. package/lib/motion/shotcraft/upstream/references/shots/interaction/type-and-filter.md +37 -0
  388. package/lib/motion/shotcraft/upstream/references/shots/interaction/voice-waveform-live.md +54 -0
  389. package/lib/motion/shotcraft/upstream/references/shots/opening/brand-ink-open.md +36 -0
  390. package/lib/motion/shotcraft/upstream/references/shots/opening/crane-rise-reveal.md +56 -0
  391. package/lib/motion/shotcraft/upstream/references/shots/opening/dataviz-landscape-open.md +56 -0
  392. package/lib/motion/shotcraft/upstream/references/shots/opening/fracture.md +40 -0
  393. package/lib/motion/shotcraft/upstream/references/shots/opening/icon-field-colorize.md +60 -0
  394. package/lib/motion/shotcraft/upstream/references/shots/opening/letterspace-materialize.md +57 -0
  395. package/lib/motion/shotcraft/upstream/references/shots/opening/magician-card-flourish.md +73 -0
  396. package/lib/motion/shotcraft/upstream/references/shots/opening/orbit-ring-title-open.md +87 -0
  397. package/lib/motion/shotcraft/upstream/references/shots/opening/spotlight-hero-card.md +55 -0
  398. package/lib/motion/shotcraft/upstream/references/shots/opening/stroke-segment-build.md +50 -0
  399. package/lib/motion/shotcraft/upstream/references/shots/opening/text-as-mask.md +59 -0
  400. package/lib/motion/shotcraft/upstream/references/shots/outro/grain-dissolve.md +38 -0
  401. package/lib/motion/shotcraft/upstream/references/shots/outro/logo-shrink-wordmark-lockup.md +38 -0
  402. package/lib/motion/shotcraft/upstream/references/shots/outro/neon-triple-marquee.md +59 -0
  403. package/lib/motion/shotcraft/upstream/references/shots/outro/outro-group-photo-launch.md +39 -0
  404. package/lib/motion/shotcraft/upstream/references/shots/outro/ui-strip-away-outro.md +61 -0
  405. package/lib/motion/shotcraft/upstream/references/shots/outro/ui-to-brand-morph.md +52 -0
  406. package/lib/motion/shotcraft/upstream/references/shots/rhythm/beat-cut-moves.md +50 -0
  407. package/lib/motion/shotcraft/upstream/references/shots/rhythm/beat-step-list-theme-cycle.md +66 -0
  408. package/lib/motion/shotcraft/upstream/references/shots/rhythm/panel-grid-moves.md +43 -0
  409. package/lib/motion/shotcraft/upstream/references/shots/rhythm/quad-split-parallel-scenes.md +38 -0
  410. package/lib/motion/shotcraft/upstream/references/shots/rhythm/rhythm-interrupt-moves.md +38 -0
  411. package/lib/motion/shotcraft/upstream/references/shots/rhythm/sakuga-timing-shift.md +56 -0
  412. package/lib/motion/shotcraft/upstream/references/shots/rhythm/speed-ramp-freeze.md +44 -0
  413. package/lib/motion/shotcraft/upstream/references/shots/rhythm/trailer-grammar-moves.md +38 -0
  414. package/lib/motion/shotcraft/upstream/references/shots/transition/bottom-push-stack-wipe.md +63 -0
  415. package/lib/motion/shotcraft/upstream/references/shots/transition/card-flip-reveal.md +57 -0
  416. package/lib/motion/shotcraft/upstream/references/shots/transition/card-flock-tumble.md +63 -0
  417. package/lib/motion/shotcraft/upstream/references/shots/transition/circle-match-iris.md +58 -0
  418. package/lib/motion/shotcraft/upstream/references/shots/transition/gradient-transition.md +45 -0
  419. package/lib/motion/shotcraft/upstream/references/shots/transition/line-carry-transition.md +55 -0
  420. package/lib/motion/shotcraft/upstream/references/shots/transition/mosaic-reframe.md +49 -0
  421. package/lib/motion/shotcraft/upstream/references/shots/transition/page-turn-transitions.md +42 -0
  422. package/lib/motion/shotcraft/upstream/references/shots/transition/print-texture-transitions.md +42 -0
  423. package/lib/motion/shotcraft/upstream/references/shots/transition/shot-transitions.md +45 -0
  424. package/lib/motion/shotcraft/upstream/references/shots/transition/tear-streak-transitions.md +48 -0
  425. package/lib/motion/shotcraft/upstream/references/shots/transition/transition-hidden-cut.md +55 -0
  426. package/lib/motion/shotcraft/upstream/references/shots/transition/transition-travel.md +43 -0
  427. package/lib/motion/shotcraft/upstream/references/shots/transition/white-flash-logo-simplify-cut.md +41 -0
  428. package/lib/motion/shotcraft/upstream/references/shots/transition/wipe-transitions.md +42 -0
  429. package/lib/motion/shotcraft/upstream/references/shots/typography/blur-slide.md +59 -0
  430. package/lib/motion/shotcraft/upstream/references/shots/typography/brace-expand.md +55 -0
  431. package/lib/motion/shotcraft/upstream/references/shots/typography/countdown-arc-scatter.md +65 -0
  432. package/lib/motion/shotcraft/upstream/references/shots/typography/document-typewriter-reveal.md +42 -0
  433. package/lib/motion/shotcraft/upstream/references/shots/typography/flying-words.md +64 -0
  434. package/lib/motion/shotcraft/upstream/references/shots/typography/glitch-cycle.md +64 -0
  435. package/lib/motion/shotcraft/upstream/references/shots/typography/gradient-word-sweep.md +54 -0
  436. package/lib/motion/shotcraft/upstream/references/shots/typography/lead-word-zoom-assemble.md +91 -0
  437. package/lib/motion/shotcraft/upstream/references/shots/typography/marker-underline-title.md +51 -0
  438. package/lib/motion/shotcraft/upstream/references/shots/typography/outline-word-fill.md +66 -0
  439. package/lib/motion/shotcraft/upstream/references/shots/typography/paper-title-card.md +38 -0
  440. package/lib/motion/shotcraft/upstream/references/shots/typography/pill-chip-slot-cycle-handled.md +66 -0
  441. package/lib/motion/shotcraft/upstream/references/shots/typography/pill-slot-cycle.md +58 -0
  442. package/lib/motion/shotcraft/upstream/references/shots/typography/text-column-converge.md +61 -0
  443. package/lib/motion/shotcraft/upstream/references/shots/typography/title-demote-to-label.md +64 -0
  444. package/lib/motion/shotcraft/upstream/references/shots/typography/type-assembly-moves.md +45 -0
  445. package/lib/motion/shotcraft/upstream/references/shots/typography/type-entrance-moves.md +41 -0
  446. package/lib/motion/shotcraft/upstream/references/shots/typography/type-rhythm-sync.md +51 -0
  447. package/lib/motion/shotcraft/upstream/references/shots/typography/typewriter-moves.md +42 -0
  448. package/lib/motion/shotcraft/upstream/references/shots/typography/vertical-word-roll-blur-cycle.md +63 -0
  449. package/lib/motion/shotcraft/upstream/references/shots/typography/word-relay-filmstrip.md +57 -0
  450. package/lib/motion/shotcraft/upstream/references/shots/typography/word-relay-geometry.md +75 -0
  451. package/lib/motion/shotcraft/upstream/references/shots/ui-entrance/avatar-bracket-carousel.md +59 -0
  452. package/lib/motion/shotcraft/upstream/references/shots/ui-entrance/bezier-source-converge-merge.md +67 -0
  453. package/lib/motion/shotcraft/upstream/references/shots/ui-entrance/card-stack.md +60 -0
  454. package/lib/motion/shotcraft/upstream/references/shots/ui-entrance/carousel-3d.md +66 -0
  455. package/lib/motion/shotcraft/upstream/references/shots/ui-entrance/cloner-depth-echo.md +40 -0
  456. package/lib/motion/shotcraft/upstream/references/shots/ui-entrance/deck-deal-flyin.md +45 -0
  457. package/lib/motion/shotcraft/upstream/references/shots/ui-entrance/doc-park-left-pill-deal.md +62 -0
  458. package/lib/motion/shotcraft/upstream/references/shots/ui-entrance/draw-svg-trace.md +55 -0
  459. package/lib/motion/shotcraft/upstream/references/shots/ui-entrance/element-body-moves.md +37 -0
  460. package/lib/motion/shotcraft/upstream/references/shots/ui-entrance/integration-hub-map.md +55 -0
  461. package/lib/motion/shotcraft/upstream/references/shots/ui-entrance/list-reveal.md +50 -0
  462. package/lib/motion/shotcraft/upstream/references/shots/ui-entrance/list-stack-press.md +43 -0
  463. package/lib/motion/shotcraft/upstream/references/shots/ui-entrance/neon-frame-forerun.md +55 -0
  464. package/lib/motion/shotcraft/upstream/references/shots/ui-entrance/neon-frame-orbit-drop.md +51 -0
  465. package/lib/motion/shotcraft/upstream/references/shots/ui-entrance/page-waterfall-wall.md +51 -0
  466. package/lib/motion/shotcraft/upstream/references/shots/ui-entrance/product-card-progressive-assemble.md +56 -0
  467. package/lib/motion/shotcraft/upstream/references/shots/ui-entrance/radial-wave.md +49 -0
  468. package/lib/motion/shotcraft/upstream/references/shots/ui-entrance/research-card-stack-scroll.md +57 -0
  469. package/lib/motion/shotcraft/upstream/references/shots/ui-entrance/row-embed.md +36 -0
  470. package/lib/motion/shotcraft/upstream/references/shots/ui-entrance/value-stagger-gradient.md +64 -0
  471. package/lib/motion/shotcraft/upstream/references/shots/ui-entrance/wall-reveal-moves.md +47 -0
  472. package/lib/motion/shotcraft/upstream/references/sound-design.md +170 -0
  473. package/lib/motion/shotcraft/upstream/references/workbench.md +157 -0
  474. package/lib/motion/shotcraft/upstream/template/TEMPLATE.md +103 -0
  475. package/lib/motion/shotcraft/upstream/template/THEMES.md +44 -0
  476. package/lib/motion/shotcraft/upstream/template/package.json +20 -0
  477. package/lib/motion/shotcraft/upstream/template/remotion.config.ts +6 -0
  478. package/lib/motion/shotcraft/upstream/template/src/Root.tsx +17 -0
  479. package/lib/motion/shotcraft/upstream/template/src/aifl/Caption.tsx +65 -0
  480. package/lib/motion/shotcraft/upstream/template/src/aifl/DigitRoll.tsx +48 -0
  481. package/lib/motion/shotcraft/upstream/template/src/aifl/FlashCut.tsx +24 -0
  482. package/lib/motion/shotcraft/upstream/template/src/aifl/Main.tsx +192 -0
  483. package/lib/motion/shotcraft/upstream/template/src/aifl/PaperTitleCard.tsx +111 -0
  484. package/lib/motion/shotcraft/upstream/template/src/aifl/live/PageCam.tsx +150 -0
  485. package/lib/motion/shotcraft/upstream/template/src/aifl/live/SceneDetail.tsx +158 -0
  486. package/lib/motion/shotcraft/upstream/template/src/aifl/live/SceneFlyIn.tsx +468 -0
  487. package/lib/motion/shotcraft/upstream/template/src/aifl/live/SceneOpen.tsx +552 -0
  488. package/lib/motion/shotcraft/upstream/template/src/aifl/live/SceneOutroLive.tsx +411 -0
  489. package/lib/motion/shotcraft/upstream/template/src/aifl/live/ScenePapers.tsx +200 -0
  490. package/lib/motion/shotcraft/upstream/template/src/aifl/live/SceneWbr.tsx +324 -0
  491. package/lib/motion/shotcraft/upstream/template/src/aifl/live-layout.json +389 -0
  492. package/lib/motion/shotcraft/upstream/template/src/index.ts +4 -0
  493. package/lib/motion/shotcraft/upstream/template/src/themes/ThemedFilm.tsx +7 -0
  494. package/lib/motion/shotcraft/upstream/template/src/themes/manifest.tsx +68 -0
  495. package/lib/motion/shotcraft/upstream/template/src/themes/palette-assets.json +1 -0
  496. package/lib/motion/shotcraft/upstream/template/src/themes/palette-assets.ts +24 -0
  497. package/lib/motion/shotcraft/upstream/template/src/themes/palettes.json +74 -0
  498. package/lib/motion/shotcraft/upstream/template/src/themes/visual-theme.tsx +91 -0
  499. package/lib/motion/shotcraft/upstream/template/src/workbench.ts +213 -0
  500. package/lib/motion/shotcraft/upstream/template/tsconfig.json +15 -0
  501. package/lib/motion/spec.js +113 -0
  502. package/lib/motion/style-engine.js +153 -0
  503. package/lib/motion/style-frames.js +268 -0
  504. package/lib/motion/sync.js +214 -0
  505. package/lib/motion/theme.js +121 -0
  506. package/lib/motion/voice.js +417 -0
  507. package/lib/motion-agent-files.js +82 -0
  508. package/lib/motion-agent-protocol.js +66 -0
  509. package/lib/motion-agent-tools.js +196 -0
  510. package/lib/motion-agent.js +171 -0
  511. package/lib/motion-capture.js +92 -0
  512. package/lib/motion-core.js +238 -0
  513. package/lib/motion-film-review.js +71 -0
  514. package/lib/motion-library.js +210 -0
  515. package/lib/motion-planner.js +254 -0
  516. package/lib/motion-project.js +80 -0
  517. package/lib/motion-render-worker.js +145 -0
  518. package/lib/motion-session.js +32 -0
  519. package/lib/motion-source.js +49 -0
  520. package/lib/motion-tasks.js +452 -0
  521. package/lib/motion-vision.js +43 -0
  522. package/lib/mxfile.js +27 -7
  523. package/lib/routing/node-runtime.js +14 -0
  524. package/lib/routing/router.js +211 -0
  525. package/lib/routing/vendor/README.md +96 -0
  526. package/lib/routing/vendor/libavoid-routing.js +753 -0
  527. package/lib/routing/vendor/libavoid.min.js +59 -0
  528. package/lib/shotcard-core.js +39 -0
  529. package/lib/shotcard-reference.js +56 -0
  530. package/lib/style-kernel.js +37 -3
  531. package/lib/task-patches.js +26 -2
  532. package/lib/video-export.js +17 -2
  533. package/package.json +29 -4
@@ -0,0 +1,103 @@
1
+ // 音频轨:画面和声音共用一条时间轴。
2
+ //
3
+ // 为什么不是"渲染完再用 ffmpeg 混音":混音只能调已有素材的音量,
4
+ // 改不了画面;而卡点需要"这一帧的画面事件"和"这一拍的重音"同时成立。
5
+ // 所以音频必须进 Remotion 的 plan,由 entry.jsx 在渲染时挂 <Audio>。
6
+ //
7
+ // 时间轴上的音频条目(timeline.json 的 audio[]):
8
+ // { id, file, role, from, durationInFrames, gainDb, fadeIn, fadeOut, trimStart, loop, duck }
9
+ // - file 相对 <工程>/assets/,渲染时用 staticFile(file);
10
+ // - from 是**全片帧坐标**,和 shots 同一坐标系(不写场景内相对帧);
11
+ // - gainDb 用 dB(0 = 原样,-6 = 一半),entry 里转成线性增益;
12
+ // - duck=true 时,旁白区间自动压低(BGM 专用)。
13
+
14
+ import { randomUUID } from 'node:crypto'
15
+ import { OUTPUT_AUDIO_OFFSET_F } from './beats.js'
16
+
17
+ export const AUDIO_ROLES = ['bgm', 'voice', 'sfx']
18
+ export const DUCK_DB = -12
19
+ export const DUCK_RAMP_FRAMES = 6
20
+
21
+ /** 把一条音频条目规范成渲染能吃的形状;缺字段给默认值,越界直接拒绝。 */
22
+ export function normalizeCue(raw, fps, totalFrames) {
23
+ if (!raw || typeof raw !== 'object') return null
24
+ const file = typeof raw.file === 'string' ? raw.file.replace(/\\/g, '/') : ''
25
+ if (!file || file.indexOf('..') >= 0) return null
26
+ const role = AUDIO_ROLES.includes(raw.role) ? raw.role : 'sfx'
27
+ const from = Math.max(0, Math.round(Number(raw.from) || 0))
28
+ const duration = Math.max(1, Math.round(Number(raw.durationInFrames) || Math.round(Number(raw.duration) * fps) || fps))
29
+ const gainDb = Number.isFinite(Number(raw.gainDb)) ? Number(raw.gainDb) : (role === 'bgm' ? -10 : 0)
30
+ return {
31
+ id: typeof raw.id === 'string' && raw.id ? raw.id : 'cue-' + randomUUID().slice(0, 8),
32
+ file,
33
+ role,
34
+ from: Math.min(from, Math.max(0, totalFrames - 1)),
35
+ durationInFrames: Math.min(duration, Math.max(1, totalFrames - from)),
36
+ gainDb,
37
+ fadeIn: Math.max(0, Math.round(Number(raw.fadeIn) || 0)),
38
+ fadeOut: Math.max(0, Math.round(Number(raw.fadeOut) || 0)),
39
+ trimStart: Math.max(0, Number(raw.trimStart) || 0),
40
+ loop: raw.loop === true || raw.loop === '1',
41
+ duck: role === 'bgm' ? raw.duck !== false : false,
42
+ }
43
+ }
44
+
45
+ export function readTrack(timeline) {
46
+ const fps = Number(timeline.fps) || 30
47
+ const total = Number(timeline.durationInFrames) || 0
48
+ const list = Array.isArray(timeline.audio) ? timeline.audio : []
49
+ return list.map((cue) => normalizeCue(cue, fps, total)).filter(Boolean)
50
+ }
51
+
52
+ export function writeTrack(timeline, cues) {
53
+ timeline.audio = cues
54
+ return timeline
55
+ }
56
+
57
+ export function addCue(timeline, cue) {
58
+ const cues = readTrack(timeline)
59
+ const next = normalizeCue(cue, Number(timeline.fps) || 30, Number(timeline.durationInFrames) || 0)
60
+ if (!next) throw new Error('音频条目缺少 file')
61
+ // 只替换「同一个文件 + 同一个角色 + 同一个位置」的那一条(重复调用是幂等的);
62
+ // 同一个音效放在不同时间点是两条轨,不能互相顶掉。
63
+ const filtered = cues.filter((item) => !(item.file === next.file && item.role === next.role && item.from === next.from))
64
+ filtered.push(next)
65
+ filtered.sort((a, b) => a.from - b.from)
66
+ return writeTrack(timeline, filtered)
67
+ }
68
+
69
+ export function removeCue(timeline, id) {
70
+ const cues = readTrack(timeline).filter((cue) => cue.id !== id)
71
+ return writeTrack(timeline, cues)
72
+ }
73
+
74
+ /**
75
+ * 旁白区间(全片帧),给 ducking 用。
76
+ * 提前 4 帧起、延后 8 帧收,避免声音尾巴被"掐断"。
77
+ */
78
+ export function voiceWindows(cues) {
79
+ return cues.filter((cue) => cue.role === 'voice').map((cue) => ({
80
+ from: Math.max(0, cue.from - 4),
81
+ to: cue.from + cue.durationInFrames + 8,
82
+ }))
83
+ }
84
+
85
+ /** dB → 线性增益。 */
86
+ export function dbToGain(db) {
87
+ return Math.pow(10, Number(db || 0) / 20)
88
+ }
89
+
90
+ /**
91
+ * 把一条音频钉到某个"画面事件帧"上(原版 sound-design §4.6 的公式)。
92
+ * sourcePeakLagFrames 由 motion_audio analyze 给出(peakLag × fps)。
93
+ */
94
+ export function cuePinnedAt(targetPeakFrame, sourcePeakLagFrames = 0) {
95
+ return Math.max(0, Math.round(targetPeakFrame - (sourcePeakLagFrames || 0) - OUTPUT_AUDIO_OFFSET_F))
96
+ }
97
+
98
+ /** 音频轨摘要:给工具返回值和 BRIEF 用。 */
99
+ export function describeTrack(cues, fps) {
100
+ if (!cues.length) return '(无音频)'
101
+ return cues.map((cue) => '- ' + cue.role + ' ' + cue.file + ' @ ' + (cue.from / fps).toFixed(2) + 's · '
102
+ + (cue.durationInFrames / fps).toFixed(2) + 's · ' + cue.gainDb + 'dB' + (cue.duck ? ' · 旁白避让' : '')).join('\n')
103
+ }
@@ -0,0 +1,63 @@
1
+ // 音轨电平:加一条音轨的时候就算清楚它进混音会是什么样。
2
+ //
3
+ // 这不是混音器,只是把"要不要减一点增益"这件事从**渲完再发现**挪到**加的时候就知道**。
4
+ // 两个依据:素材自带的样本峰值(ffmpeg volumedetect)和这条的增益。
5
+ //
6
+ // 为什么不能直接照抄 suggestedGainDb:
7
+ // 那个建议是把 RMS 拉到 -16dB,对**短促的打击类音效**是错的 ——
8
+ // RMS 很低但峰值很高,照抄会把峰值推到 +5dBFS(实测 fs:615308 就是这样)。
9
+ // 所以这里按峰值再夹一次:清单说多少都可以,但峰值不许过天花板。
10
+
11
+ import { dbToGain } from './audio.js'
12
+
13
+ /** 留出的天花板:峰值到 -1 dBFS 就当作"会顶"(编码器前端还有取样间过冲)。 */
14
+ export const PEAK_CEILING_DB = -1
15
+
16
+ /** 已知峰值时,这条最多还能加多少 dB。 */
17
+ export function maxGainFor(peakDb, ceilingDb = PEAK_CEILING_DB) {
18
+ if (!Number.isFinite(Number(peakDb))) return null
19
+ return Math.floor((ceilingDb - Number(peakDb)) * 10) / 10
20
+ }
21
+
22
+ /** 增益从哪来:显式 > 音频清单的建议 > 角色默认;非显式的再按峰值夹一道。 */
23
+ export function resolveGainDb({ explicitGain, manifestGain, role, peakDb } = {}) {
24
+ if (Number.isFinite(Number(explicitGain))) return { gainDb: Number(explicitGain), source: 'explicit' }
25
+ const suggested = Number.isFinite(Number(manifestGain))
26
+ const proposed = suggested ? Number(manifestGain) : (role === 'bgm' ? -10 : 0)
27
+ const source = suggested ? 'manifest' : 'role'
28
+ const limit = maxGainFor(peakDb)
29
+ if (limit !== null && proposed > limit) return { gainDb: limit, source, capped: proposed }
30
+ return { gainDb: proposed, source }
31
+ }
32
+
33
+ /** 音频清单里这个文件被建议的增益(motion_sfx get 量出来写进去的)。 */
34
+ export function suggestedGainOf(manifest, file) {
35
+ const entry = (manifest?.assets || []).find((item) => item.file === file)
36
+ const gain = entry?.analysis?.suggestedGainDb
37
+ return Number.isFinite(Number(gain)) ? Number(gain) : null
38
+ }
39
+
40
+ /** 一个素材加完增益之后的峰值;素材测不出来时给 null(不假装知道)。 */
41
+ export function cueLevel({ peakDb, gainDb } = {}) {
42
+ const known = peakDb !== null && peakDb !== undefined && Number.isFinite(Number(peakDb))
43
+ const peak = known ? Math.round((Number(peakDb) + Number(gainDb || 0)) * 10) / 10 : null
44
+ return { peakDb: peak, headroomDb: peak === null ? null : Math.round((PEAK_CEILING_DB - peak) * 10) / 10, clips: peak !== null && peak > PEAK_CEILING_DB }
45
+ }
46
+
47
+ /** 几条同时响的峰值做线性相加的保守估计(不是 LUFS,只是"会不会顶")。 */
48
+ export function mixPeakDb(cuePeaksDb) {
49
+ const sum = (cuePeaksDb || []).reduce((total, db) => (Number.isFinite(Number(db)) ? total + dbToGain(Number(db)) : total), 0)
50
+ return sum > 0 ? Math.round(20 * Math.log10(sum) * 10) / 10 : null
51
+ }
52
+
53
+ /** 给工具返回值用的人话。 */
54
+ export function describeLevel({ cue, gain, level, overlaps }) {
55
+ const source = { explicit: '显式指定', manifest: '音频清单的 suggestedGainDb', role: '角色默认' }[gain.source] || gain.source
56
+ const lines = ['增益 ' + cue.gainDb + 'dB —— ' + source
57
+ + (gain.capped === undefined ? '' : '(原文建议 ' + gain.capped + 'dB,按峰值夹到 ' + cue.gainDb + 'dB,否则会削波)')]
58
+ if (level.peakDb === null) lines.push('素材量不出峰值(空的或格式没解析),电平只能靠听')
59
+ else if (level.clips) lines.push('这一条加完峰值约 ' + level.peakDb + ' dBFS,已经顶到天花板')
60
+ else lines.push('这一条加完峰值约 ' + level.peakDb + ' dBFS,余量 ' + level.headroomDb + 'dB')
61
+ if (overlaps > 0) lines.push('这个时间段已经有 ' + overlaps + ' 条音轨,叠加后峰值只会更高')
62
+ return lines.join('\n')
63
+ }
@@ -0,0 +1,144 @@
1
+ // 混音后级:成片渲完再走一遍 loudnorm + alimiter。
2
+ //
3
+ // 为什么需要这一级:
4
+ // Remotion 把各条 <Audio> 直接相加编码成 AAC,**没有任何限幅**;
5
+ // 而画布那条旧链路(audio-media.js)混音时一直带 alimiter。
6
+ // 缺这一级的后果是可实测的:n1 那条片的真峰值一度 +1.8 dBFS(削波),
7
+ // 只能靠帧检事后发现再回头手改每一条的增益。
8
+ //
9
+ // 用的是 loudnorm 的两遍法:
10
+ // 第一遍只量(print_format=json 拿到 measured_*),
11
+ // 第二遍把测得值喂回去并开 linear=true —— 能线性就线性(不动动态),
12
+ // 线性会把真峰值顶穿时 loudnorm 自己退回动态模式。
13
+ // 最后再挂一级 alimiter 兜底,防止编码器前端出现取样间过冲。
14
+
15
+ import { existsSync, renameSync, rmSync } from 'node:fs'
16
+ import { basename, dirname, join } from 'node:path'
17
+ import { runFfmpeg } from './ffmpeg.js'
18
+
19
+ /**
20
+ * 目标响度:交付规范是 −14 LUFS / 真峰值 ≤ −1 dBTP(网络平台上传标准)。
21
+ * 早先取 −16 / −1.5 是「保守一点」的折中;既然插件出的就是交付件,默认就该是交付值。
22
+ * 要旧行为可以在 timeline.json 的 audioMix 里显式写回。
23
+ */
24
+ export const MIX_DEFAULTS = { normalize: true, lufs: -14, truePeak: -1, lra: 11 }
25
+
26
+ /** 从 loudnorm 的 stderr 里取最后一段 JSON(前面还有滤镜的日志行)。 */
27
+ export function parseLoudnormJson(text) {
28
+ const blocks = [...String(text || '').matchAll(/\{[^{}]*\}/g)]
29
+ for (let index = blocks.length - 1; index >= 0; index--) {
30
+ try {
31
+ const parsed = JSON.parse(blocks[index][0])
32
+ if (parsed && parsed.input_i !== undefined) return parsed
33
+ } catch { /* 不是 JSON,继续往前找 */ }
34
+ }
35
+ return null
36
+ }
37
+
38
+ /** 量整段的综合响度与真峰值(取 ebur128 结尾的 Summary 段)。没有音轨时返回 null 值。 */
39
+ export async function measureLoudness(file, options = {}) {
40
+ const text = await runFfmpeg(['-i', file, '-af', 'ebur128=peak=true', '-f', 'null', '-'],
41
+ { cwd: options.cwd, log: options.log || 'mix-ebur128.log', allowFail: true, timeoutMs: options.timeoutMs })
42
+ // ebur128 每 100ms 打一行进度,开头那几行的 I 是 -70(还没攒够数据);
43
+ // 只有结尾 Summary 段才是整段的结论,所以从 Summary 开始截,并取最后一个匹配。
44
+ const index = text.lastIndexOf('Summary:')
45
+ const summary = index >= 0 ? text.slice(index) : text
46
+ const grab = (pattern) => {
47
+ const matches = [...summary.matchAll(pattern)]
48
+ return matches.length ? Number(matches[matches.length - 1][1]) : null
49
+ }
50
+ return {
51
+ integratedLufs: grab(/I:\s*(-?[\d.]+)\s*LUFS/g),
52
+ truePeakDb: grab(/Peak:\s*(-?[\d.]+)\s*dBFS/g),
53
+ }
54
+ }
55
+
56
+ /**
57
+ * 把一条成片的音轨归一化到目标响度并限幅。
58
+ * 视频流用 -c:v copy 直接搬,不重编画面;只有 AAC 音频流被重编。
59
+ */
60
+ export async function normalizeMix(file, options = {}) {
61
+ // 只认这四个字段:cwd/log/timeoutMs 是调用参数,不该跟着结果一起存进 job 记录。
62
+ const picked = {}
63
+ for (const key of ['normalize', 'lufs', 'truePeak', 'lra']) if (options[key] !== undefined) picked[key] = options[key]
64
+ const settings = { ...MIX_DEFAULTS, ...picked }
65
+ if (!existsSync(file)) throw new Error('找不到要归一化的成片:' + file)
66
+ const cwd = options.cwd || dirname(file)
67
+ const log = options.log || join(cwd, 'mix.log')
68
+ const before = await measureLoudness(file, { cwd, log: log.replace(/\.log$/, '-before.log') })
69
+ if (before.integratedLufs === null) {
70
+ return { applied: false, reason: '这条片子没有音轨,跳过响度归一', before, after: before, settings }
71
+ }
72
+ // 已经在目标附近就别再动它:反复归一化会有零点几 LU 的漂移。
73
+ const close = Math.abs(before.integratedLufs - settings.lufs) <= 0.5
74
+ const safe = before.truePeakDb === null || before.truePeakDb <= settings.truePeak + 0.35
75
+ if (close && safe) {
76
+ return { applied: false, reason: '响度已在目标附近(' + before.integratedLufs + ' LUFS)', before, after: before, settings }
77
+ }
78
+
79
+ const measureText = await runFfmpeg([
80
+ '-i', file,
81
+ '-af', 'loudnorm=I=' + settings.lufs + ':TP=' + settings.truePeak + ':LRA=' + settings.lra + ':print_format=json',
82
+ '-f', 'null', '-',
83
+ ], { cwd, log: log.replace(/\.log$/, '-measure.log'), timeoutMs: options.timeoutMs })
84
+ const measured = parseLoudnormJson(measureText)
85
+ if (!measured) throw new Error('loudnorm 没给出测量结果,无法归一化')
86
+
87
+ // 限幅器比目标再低 1dB:AAC 编码会在取样间造出新的过冲。
88
+ // 但过冲的大小取决于素材(-14 LUFS 比 -16 压得更狠,过冲也更大),
89
+ // 所以不能靠固定余量赌:编完量一次,还在目标之上就按实测差值再压一轮。
90
+ let limitDb = settings.truePeak - 1.0
91
+ let after = null
92
+ const temporary = join(dirname(file), basename(file, '.mp4') + '.mixing.mp4')
93
+ for (let attempt = 0; attempt < 2; attempt += 1) {
94
+ // 第一轮:loudnorm 归一到目标 + 限幅。第二轮只挂限幅器 ——
95
+ // 文件已经是目标响度了,再跑一遍 loudnorm 会拿上一轮的测量值再加一次增益(实测 +1.2 LU)。
96
+ const limiter = 'alimiter=limit=' + Math.pow(10, limitDb / 20).toFixed(4) + ':attack=5:release=50:level=0'
97
+ const filter = attempt === 0
98
+ ? [
99
+ 'loudnorm=I=' + settings.lufs + ':TP=' + settings.truePeak + ':LRA=' + settings.lra,
100
+ 'measured_I=' + measured.input_i,
101
+ 'measured_TP=' + measured.input_tp,
102
+ 'measured_LRA=' + measured.input_lra,
103
+ 'measured_thresh=' + measured.input_thresh,
104
+ 'offset=' + (measured.target_offset || 0),
105
+ 'linear=true',
106
+ 'print_format=summary',
107
+ ].join(':') + ',' + limiter
108
+ : limiter
109
+
110
+ try {
111
+ await runFfmpeg([
112
+ '-i', file,
113
+ '-map', '0:v:0',
114
+ '-map', '0:a:0',
115
+ '-af', filter,
116
+ '-c:v', 'copy',
117
+ '-c:a', 'aac', '-b:a', '192k', '-ar', '48000',
118
+ '-movflags', '+faststart',
119
+ temporary,
120
+ ], { cwd, log, timeoutMs: options.timeoutMs })
121
+ renameSync(temporary, file)
122
+ } catch (error) {
123
+ rmSync(temporary, { force: true })
124
+ throw error
125
+ }
126
+ after = await measureLoudness(file, { cwd, log: log.replace(/\.log$/, '-after.log') })
127
+ const overshoot = after.truePeakDb === null ? 0 : after.truePeakDb - settings.truePeak
128
+ if (overshoot <= 0) break
129
+ // 第二轮只压限幅器,不再重算 loudnorm 的测量值:响度已经到位了。
130
+ limitDb -= overshoot + 0.3
131
+ }
132
+ return { applied: true, before, after, settings, measured, limitDb }
133
+ }
134
+
135
+ /** 一行给人看的结论。 */
136
+ export function describeMix(mix) {
137
+ if (!mix) return ''
138
+ if (!mix.applied) return '响度:' + (mix.reason || '未处理')
139
+ const before = mix.before.integratedLufs === null ? '—' : mix.before.integratedLufs + ' LUFS'
140
+ const after = mix.after.integratedLufs === null ? '—' : mix.after.integratedLufs + ' LUFS'
141
+ const peakBefore = mix.before.truePeakDb === null ? '—' : mix.before.truePeakDb + ' dBFS'
142
+ const peakAfter = mix.after.truePeakDb === null ? '—' : mix.after.truePeakDb + ' dBFS'
143
+ return '响度:' + before + ' → ' + after + ',真峰值 ' + peakBefore + ' → ' + peakAfter
144
+ }
@@ -0,0 +1,337 @@
1
+ // 节拍检测:把音频解成单声道 PCM,用能量包络的瞬时差找重音,用自相关估 BPM,
2
+ // 再用最小二乘把重音序列拟合成等距网格(原版 music-beat-sync 的核心一步)。
3
+ //
4
+ // 为什么必须拟合网格:beat_track 类的 BPM 标量常偏 2%+,直接按 BPM 推拍子,
5
+ // 30 秒后会漂掉一整个拍。拿到重音时刻后再回归直线 t_i = t0 + i*T,
6
+ // 才能真正拿到"可信网格",残差就是自检指标(≤±15ms 才算机器鼓点)。
7
+ //
8
+ // 结果永远带 confidence / residualMs / reliable:
9
+ // 置信度低、残差大时调用方应当只做参考,不要硬对。
10
+
11
+ import { execFile } from 'node:child_process'
12
+ import { promisify } from 'node:util'
13
+ import { existsSync } from 'node:fs'
14
+ import { ffmpegBinary } from '../video-export.js'
15
+
16
+ const exec = promisify(execFile)
17
+ const SAMPLE_RATE = 22050
18
+ const HOP = 512
19
+ const WINDOW = 1024
20
+
21
+ export async function decodePcm(file) {
22
+ if (!existsSync(file)) throw new Error('找不到音频文件:' + file)
23
+ const { stdout } = await exec(ffmpegBinary(), ['-hide_banner', '-loglevel', 'error', '-i', file, '-vn', '-ac', '1', '-ar', String(SAMPLE_RATE), '-f', 's16le', '-'], { encoding: 'buffer', windowsHide: true, timeout: 120000, maxBuffer: 256 * 1024 * 1024 })
24
+ return { pcm: stdout, sampleRate: SAMPLE_RATE, hop: HOP, window: WINDOW }
25
+ }
26
+
27
+ // 能量(对数)+ 半波整流差 = 重音包络。
28
+ export function onsetEnvelope(pcm, sampleRate = SAMPLE_RATE, hop = HOP, window = WINDOW) {
29
+ const count = Math.max(0, Math.floor((pcm.length / 2 - window) / hop))
30
+ const energy = new Float64Array(count)
31
+ for (let index = 0; index < count; index++) {
32
+ let sum = 0
33
+ const start = index * hop * 2
34
+ for (let offset = 0; offset < window * 2; offset += 2) {
35
+ const sample = pcm.readInt16LE(start + offset) / 32768
36
+ sum += sample * sample
37
+ }
38
+ energy[index] = Math.log1p(Math.sqrt(sum / window) * 12)
39
+ }
40
+ const flux = new Float64Array(count)
41
+ for (let index = 1; index < count; index++) flux[index] = Math.max(0, energy[index] - energy[index - 1])
42
+ // 三点平滑,压掉单帧毛刺
43
+ const smooth = new Float64Array(count)
44
+ for (let index = 1; index < count - 1; index++) smooth[index] = (flux[index - 1] + flux[index] * 2 + flux[index + 1]) / 4
45
+ return { flux: smooth, framesPerSecond: sampleRate / hop, seconds: (count * hop) / sampleRate, energy }
46
+ }
47
+
48
+ /** RBJ 二阶带通(分频段看鼓件用;不分频段时 kick 会把 snare 埋掉)。 */
49
+ function bandpassCoefficients(sampleRate, lo, hi) {
50
+ const f0 = Math.sqrt(lo * hi)
51
+ const q = Math.max(0.4, f0 / Math.max(1, hi - lo))
52
+ const w0 = (2 * Math.PI * f0) / sampleRate
53
+ const alpha = Math.sin(w0) / (2 * q)
54
+ const cos = Math.cos(w0)
55
+ const a0 = 1 + alpha
56
+ return {
57
+ b0: alpha / a0, b1: 0, b2: -alpha / a0, a1: (-2 * cos) / a0, a2: (1 - alpha) / a0,
58
+ }
59
+ }
60
+
61
+ function applyBiquad(samples, coefficients) {
62
+ const out = new Float64Array(samples.length)
63
+ let x1 = 0, x2 = 0, y1 = 0, y2 = 0
64
+ for (let index = 0; index < samples.length; index++) {
65
+ const x0 = samples[index]
66
+ const y0 = coefficients.b0 * x0 + coefficients.b1 * x1 + coefficients.b2 * x2 - coefficients.a1 * y1 - coefficients.a2 * y2
67
+ x2 = x1; x1 = x0; y2 = y1; y1 = y0
68
+ out[index] = y0
69
+ }
70
+ return out
71
+ }
72
+
73
+ function toSamples(pcm) {
74
+ const samples = new Float64Array(Math.floor(pcm.length / 2))
75
+ for (let index = 0; index < samples.length; index++) samples[index] = pcm.readInt16LE(index * 2) / 32768
76
+ return samples
77
+ }
78
+
79
+ /** 某个频段的能量包络(把整段 PCM 滤波后逐窗求 RMS)。 */
80
+ function bandEnvelope(samples, sampleRate, lo, hi, hop = HOP, window = WINDOW) {
81
+ const filtered = applyBiquad(samples, bandpassCoefficients(sampleRate, lo, hi))
82
+ const count = Math.max(0, Math.floor((filtered.length - window) / hop))
83
+ const envelope = new Float64Array(count)
84
+ for (let index = 0; index < count; index++) {
85
+ let sum = 0
86
+ const start = index * hop
87
+ for (let offset = 0; offset < window; offset++) sum += filtered[start + offset] ** 2
88
+ envelope[index] = Math.sqrt(sum / window)
89
+ }
90
+ return envelope
91
+ }
92
+
93
+ function autocorrelation(values, minLag, maxLag) {
94
+ let bestLag = 0, bestScore = 0
95
+ for (let lag = Math.round(minLag); lag <= Math.round(maxLag); lag++) {
96
+ let score = 0
97
+ for (let index = lag; index < values.length; index++) score += values[index] * values[index - lag]
98
+ score /= values.length - lag
99
+ if (score > bestScore) { bestScore = score; bestLag = lag }
100
+ }
101
+ return { lag: bestLag, score: bestScore }
102
+ }
103
+
104
+ /**
105
+ * 最小二乘拟合等距网格:t_i = t0 + i*T。
106
+ *
107
+ * 先把每个重音时刻归到最近格点上,再对 (i, t) 做一次直线回归;
108
+ * 迭代两轮即可稳定(第一轮用 BPM 推的 T,第二轮用回归出的 T)。
109
+ */
110
+ export function fitGrid(times, periodGuess, tolerance = 0.02) {
111
+ if (times.length < 4 || !(periodGuess > 0)) return null
112
+ let period = periodGuess
113
+ let t0 = times[0]
114
+ let inliers = times
115
+ // 两轮:用上一轮的"在格点上的重音"重新回归,把杂音(半拍、漏检)剔出去。
116
+ // 索引必须逐拍累加而不是全局 round((t-t0)/T):BPM 初值偏 2% 时,
117
+ // 几十拍之后全局取整会整体错位一格,回归出来的残差全是假的。
118
+ for (let round = 0; round < 2; round++) {
119
+ const offsets = times.map((t) => {
120
+ const ratio = (t - t0) / period
121
+ return (ratio - Math.round(ratio)) * period
122
+ })
123
+ const picked = []
124
+ for (let index = 0; index < times.length; index++) if (Math.abs(offsets[index]) <= tolerance) picked.push(times[index])
125
+ if (picked.length < Math.max(4, times.length * 0.3)) return null
126
+ inliers = picked
127
+ const indices = [0]
128
+ for (let k = 1; k < inliers.length; k++) indices.push(indices[k - 1] + Math.max(1, Math.round((inliers[k] - inliers[k - 1]) / period)))
129
+ const n = indices.length
130
+ const sumI = indices.reduce((s, value) => s + value, 0)
131
+ const sumT = inliers.reduce((s, value) => s + value, 0)
132
+ const sumII = indices.reduce((s, value) => s + value * value, 0)
133
+ const sumIT = indices.reduce((s, value, k) => s + value * inliers[k], 0)
134
+ const denominator = n * sumII - sumI * sumI
135
+ if (!denominator) return null
136
+ period = (n * sumIT - sumI * sumT) / denominator
137
+ t0 = (sumT - period * sumI) / n
138
+ if (!(period > 0.2 && period < 2)) return null
139
+ }
140
+ let residual = 0
141
+ for (const t of inliers) {
142
+ const ratio = (t - t0) / period
143
+ residual = Math.max(residual, Math.abs(ratio - Math.round(ratio)) * period)
144
+ }
145
+ return { period, t0, residualMs: residual * 1000, bpm: 60 / period, coverage: inliers.length / times.length }
146
+ }
147
+
148
+ /** 0.5x / 1x / 2x 三个候选里选覆盖真实重音最多的那个(半倍帧歧义必查)。 */
149
+ function bestGrid(times, bpm) {
150
+ const candidates = [bpm * 0.5, bpm, bpm * 2].filter((value) => value >= 55 && value <= 210)
151
+ let best = null
152
+ for (const value of candidates) {
153
+ const grid = fitGrid(times, 60 / value)
154
+ if (!grid) continue
155
+ const score = grid.coverage - Math.min(0.5, grid.residualMs / 200)
156
+ if (!best || score > best.score) best = { ...grid, score }
157
+ }
158
+ // 覆盖率太低说明"重音根本不在这条网格上"——宁可报 null,也不要给个假拍子。
159
+ if (!best || best.coverage < 0.45) return null
160
+ return best
161
+ }
162
+
163
+ /**
164
+ * 音源自身的峰值滞后:很多素材的能量峰不在文件头(渐强、前置气口),
165
+ * 按文件头对齐等于晚到。返回秒数,调用方换算成帧再扣。
166
+ */
167
+ export function peakLagSeconds(pcm, sampleRate = SAMPLE_RATE) {
168
+ const samples = toSamples(pcm)
169
+ const hop = 256
170
+ const window = 1024
171
+ const count = Math.max(1, Math.floor((samples.length - window) / hop))
172
+ const envelope = new Float64Array(count)
173
+ for (let index = 0; index < count; index++) {
174
+ let sum = 0
175
+ const start = index * hop
176
+ for (let offset = 0; offset < window; offset++) sum += samples[start + offset] ** 2
177
+ envelope[index] = Math.sqrt(sum / window)
178
+ }
179
+ let max = 0
180
+ for (const value of envelope) if (value > max) max = value
181
+ if (max <= 0) return 0
182
+ for (let index = 0; index < count; index++) {
183
+ if (envelope[index] >= max * 0.35) return (index * hop) / sampleRate
184
+ }
185
+ return 0
186
+ }
187
+
188
+ /**
189
+ * 把包络上的重音帧细化到采样级。
190
+ *
191
+ * 能量包络的峰值天然比真实攻击早半个窗(窗口把声音"提前"看见了),
192
+ * 实测能差 20–40ms——在 30fps 下就是 1 帧多,足够毁掉卡点。
193
+ * 这里回到原始采样,找攻击上升沿的第一个显著点。
194
+ */
195
+ function refineOnsets(pcm, sampleRate, frames, hop, window) {
196
+ const total = Math.floor(pcm.length / 2)
197
+ const out = []
198
+ for (const frame of frames) {
199
+ const start = Math.max(0, (frame - 2) * hop)
200
+ const end = Math.min(total, (frame + 2) * hop + window)
201
+ let peak = 0
202
+ for (let index = start; index < end; index++) peak = Math.max(peak, Math.abs(pcm.readInt16LE(index * 2) / 32768))
203
+ if (peak <= 0.002) { out.push((frame * hop) / sampleRate); continue }
204
+ let found = -1
205
+ for (let index = start; index < end; index++) {
206
+ if (Math.abs(pcm.readInt16LE(index * 2) / 32768) >= peak * 0.35) { found = index; break }
207
+ }
208
+ out.push(((found >= 0 ? found : frame * hop)) / sampleRate)
209
+ }
210
+ return out
211
+ }
212
+
213
+ export async function analyzeBeats(file, options = {}) {
214
+ const { pcm, sampleRate, hop, window } = await decodePcm(file)
215
+ const { flux, framesPerSecond, seconds, energy } = onsetEnvelope(pcm, sampleRate, hop, window)
216
+ if (!flux.length) return { bpm: null, confidence: 0, beats: [], duration: 0, method: 'onset-flux-v2' }
217
+
218
+ const minLag = framesPerSecond * 60 / (options.maxBpm || 200)
219
+ const maxLag = framesPerSecond * 60 / (options.minBpm || 60)
220
+ const { lag, score } = autocorrelation(flux, minLag, maxLag)
221
+ const roughBpm = lag ? (60 * framesPerSecond) / lag : null
222
+
223
+ let mean = 0
224
+ for (const value of flux) mean += value
225
+ mean /= flux.length
226
+ let variance = 0
227
+ for (const value of flux) variance += (value - mean) ** 2
228
+ variance /= flux.length
229
+ const threshold = mean + Math.sqrt(variance) * 0.9
230
+
231
+ const beats = [], minGap = Math.round(framesPerSecond * (options.minGap || 0.16))
232
+ for (let index = 1; index < flux.length - 1; index++) {
233
+ const value = flux[index]
234
+ if (value < threshold || value < flux[index - 1] || value < flux[index + 1]) continue
235
+ const frame = index
236
+ if (beats.length && frame - beats[beats.length - 1].frame < minGap) {
237
+ if (value > beats[beats.length - 1].strength) beats[beats.length - 1] = { frame, strength: value }
238
+ continue
239
+ }
240
+ beats.push({ frame, strength: value })
241
+ }
242
+
243
+ const strengths = beats.map((beat) => beat.strength)
244
+ const strengthMean = strengths.reduce((sum, value) => sum + value, 0) / Math.max(1, strengths.length)
245
+ const contrast = strengthMean > 0 ? Math.min(1, (strengthMean - mean) / (strengthMean || 1)) : 0
246
+ const expected = roughBpm ? Math.max(1, (seconds * roughBpm / 60) * 0.5) : Math.max(1, beats.length)
247
+ const coverage = Math.min(1, beats.length / expected)
248
+ const base = contrast * 0.6 + Math.min(1, score * 4) * 0.4
249
+ const confidence = Math.max(0, Math.min(1, base * (0.3 + 0.7 * coverage)))
250
+
251
+ // 拍点太少(氛围铺底、纯音效)时自相关也能凑出一个 BPM,但那是假的:
252
+ // 宁可报 null,也不要让上层拿一个 199 BPM 去对 15 秒里 4 个重音。
253
+ const reliable = beats.length >= 6 && confidence >= 0.35
254
+ const times = refineOnsets(pcm, sampleRate, beats.map((beat) => beat.frame), hop, window)
255
+ const grid = reliable && roughBpm ? bestGrid(times, roughBpm) : null
256
+
257
+ // 分频段把重音归类:kick 驱动"被砸"的一拍,snare 驱动"换一件事",hihat 驱动微动。
258
+ let hits = []
259
+ if (beats.length) {
260
+ const samples = toSamples(pcm)
261
+ const bands = {
262
+ kick: bandEnvelope(samples, sampleRate, 40, 160),
263
+ snare: bandEnvelope(samples, sampleRate, 1200, 3000),
264
+ hihat: bandEnvelope(samples, sampleRate, 6000, 12000),
265
+ }
266
+ hits = beats.map((beat, order) => {
267
+ const index = Math.min(bands.kick.length - 1, Math.round((beat.frame * hop) / hop))
268
+ const kick = bands.kick[index] || 0
269
+ const snare = bands.snare[index] || 0
270
+ const hihat = bands.hihat[index] || 0
271
+ const kind = kick >= snare && kick >= hihat ? 'kick' : snare >= hihat ? 'snare' : 'hihat'
272
+ return { at: Number(times[order].toFixed(3)), strength: round3(beat.strength), kind, kick: round3(kick), snare: round3(snare), hihat: round3(hihat) }
273
+ })
274
+ }
275
+
276
+ // 结构表:每 4 拍的 RMS 能量,用来给分镜排"高能段/呼吸位"。
277
+ const structure = []
278
+ if (grid) {
279
+ const barFrames = 4 * grid.period
280
+ for (let at = 0; at + barFrames <= seconds + 0.5; at += barFrames) {
281
+ const from = Math.floor((at * sampleRate) / hop)
282
+ const to = Math.min(energy.length, Math.ceil(((at + barFrames) * sampleRate) / hop))
283
+ let sum = 0, count = 0
284
+ for (let index = from; index < to; index++) { sum += Math.expm1(energy[index]); count += 1 }
285
+ structure.push({ at: Number(at.toFixed(2)), rms: Number((count ? sum / count : 0).toFixed(3)) })
286
+ }
287
+ }
288
+
289
+ const peak = beats.length ? Math.max(...strengths) : 0
290
+ return {
291
+ bpm: grid ? Number(grid.bpm.toFixed(2)) : reliable && roughBpm ? Number(roughBpm.toFixed(2)) : null,
292
+ confidence: Number(confidence.toFixed(2)),
293
+ reliable,
294
+ duration: Number(seconds.toFixed(2)),
295
+ grid: grid ? { t0: Number(grid.t0.toFixed(4)), periodMs: Number((grid.period * 1000).toFixed(2)), residualMs: Number(grid.residualMs.toFixed(1)), coverage: Number(grid.coverage.toFixed(2)) } : null,
296
+ peakLag: Number(peakLagSeconds(pcm, sampleRate).toFixed(3)),
297
+ beats: beats.map((beat, order) => ({ at: Number(times[order].toFixed(3)), strength: Number(beat.strength.toFixed(3)) })),
298
+ hits,
299
+ structure,
300
+ strongest: peak,
301
+ method: 'onset-flux-v2',
302
+ note: reliable
303
+ ? (grid && grid.residualMs <= 15 ? '网格残差 ≤15ms,可以按格点卡点' : '残差偏大(可能有变速段),按 hits 的实测时刻卡,别按 BPM 推')
304
+ : '拍点太少,估不出可信 BPM;beats 只当"重音出现的大致位置",不要硬吸附',
305
+ }
306
+ }
307
+
308
+ function round3(value) {
309
+ return Number(Number(value || 0).toFixed(3))
310
+ }
311
+
312
+ // 把 [start,start+duration] 的切点吸到最近的拍子上,越界就原样返回。
313
+ export function snapFrame(frame, beats, fps, tolerance = 4) {
314
+ let best = frame, bestDistance = Infinity
315
+ for (const beat of beats) {
316
+ const target = Math.round(beat.at * fps)
317
+ const distance = Math.abs(target - frame)
318
+ if (distance < bestDistance) { bestDistance = distance; best = target }
319
+ }
320
+ return bestDistance <= tolerance ? best : frame
321
+ }
322
+
323
+ /**
324
+ * 钉帧公式(原版 sound-design §4.6):
325
+ * Sequence.from = 目标峰值帧 − 音源峰值滞后帧 − 输出音轨偏移帧
326
+ *
327
+ * 两个偏移都不补时会叠加,实测最重一击晚 0.27s——足够被读成"没卡上"。
328
+ */
329
+ // 输出音轨的整体滞后(帧):AAC 编码器 priming 让成片里的声音比画面晚约 1.3 帧。
330
+ // 实测(tmp/test-audio.mjs,120 BPM 点击轨 → 渲染 → 抽回成片音轨量首拍):
331
+ // 源首拍 0.5000s,成片首拍 0.5430s,差 43ms = 1.29 帧@30fps。
332
+ // 换帧率按 1.28 折算(原版实测 1.27–1.29)。
333
+ export const OUTPUT_AUDIO_OFFSET_F = 1.29
334
+
335
+ export function pinFrame(targetPeakFrame, sourcePeakLagFrames = 0, outputOffsetFrames = OUTPUT_AUDIO_OFFSET_F) {
336
+ return Math.max(0, Math.round(targetPeakFrame - (sourcePeakLagFrames || 0) - (outputOffsetFrames || 0)))
337
+ }