dsh-drawai 0.4.2 → 0.4.4

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 (379) hide show
  1. package/README.md +7 -5
  2. package/lib/client.js +142 -16
  3. package/lib/index.js +7945 -7719
  4. package/lib/motion/asset-pack.js +394 -394
  5. package/lib/motion/assets.js +291 -291
  6. package/lib/motion/demo-assets-lock.js +10 -10
  7. package/lib/motion/mgstyles/briefs/23-ink-wash.md +147 -0
  8. package/lib/motion/mgstyles/catalog.d/23-ink-wash.json +28 -0
  9. package/lib/motion/mgstyles/catalog.json +127 -14
  10. package/lib/motion/mgstyles/upstream/demos/23-ink-wash/brush.js +149 -0
  11. package/lib/motion/mgstyles/upstream/demos/23-ink-wash/film.js +202 -0
  12. package/lib/motion/mgstyles/upstream/manifest.json +28 -3
  13. package/lib/motion/mgstyles/upstream/prompts/23-ink-wash.md +59 -0
  14. package/lib/motion/mgstyles/vendor/README.md +28 -28
  15. package/lib/motion/mgstyles/vendor/modules/d3/LICENSE +13 -13
  16. package/lib/motion/mgstyles/vendor/modules/d3/dist/d3.min.js +2 -2
  17. package/lib/motion/mgstyles/vendor/modules/opentype.js/LICENSE +20 -20
  18. package/lib/motion/mgstyles/vendor/modules/opentype.js/dist/opentype.mjs +14460 -14460
  19. package/lib/motion/mgstyles/vendor/modules/simplex-noise/LICENSE +21 -21
  20. package/lib/motion/mgstyles/vendor/modules/simplex-noise/dist/esm/simplex-noise.js +465 -465
  21. package/lib/motion/mgstyles/vendor/modules/topojson-client/LICENSE +13 -13
  22. package/lib/motion/mgstyles/vendor/modules/topojson-client/dist/topojson-client.min.js +2 -2
  23. package/lib/motion/project.js +5 -1
  24. package/lib/motion/qa.js +116 -6
  25. package/lib/motion/render.js +179 -179
  26. package/lib/motion/scene-kit.js +1 -1
  27. package/lib/motion/shotcraft/upstream/SKILL.md +286 -286
  28. package/lib/motion/shotcraft/upstream/assets/lib/Caption.tsx +54 -54
  29. package/lib/motion/shotcraft/upstream/assets/lib/ClipCard.tsx +200 -200
  30. package/lib/motion/shotcraft/upstream/assets/lib/DigitRoll.tsx +45 -45
  31. package/lib/motion/shotcraft/upstream/assets/lib/FlashCut.tsx +20 -20
  32. package/lib/motion/shotcraft/upstream/assets/lib/FlatPanel.tsx +65 -65
  33. package/lib/motion/shotcraft/upstream/assets/lib/PageCam.tsx +151 -151
  34. package/lib/motion/shotcraft/upstream/assets/lib/VerticalTicker.tsx +129 -129
  35. package/lib/motion/shotcraft/upstream/assets/lib/helpers/camera.tsx +75 -75
  36. package/lib/motion/shotcraft/upstream/assets/lib/helpers/motion.ts +40 -40
  37. package/lib/motion/shotcraft/upstream/assets/lib/helpers/rand.ts +11 -11
  38. package/lib/motion/shotcraft/upstream/assets/lib/helpers/shake.ts +10 -10
  39. package/lib/motion/shotcraft/upstream/assets/scripts/build-readme-cover.mjs +49 -49
  40. package/lib/motion/shotcraft/upstream/assets/scripts/capture-template.mjs +210 -210
  41. package/lib/motion/shotcraft/upstream/assets/scripts/sfx-source.py +423 -423
  42. package/lib/motion/shotcraft/upstream/assets/scripts/smoke-render-demos.py +170 -170
  43. package/lib/motion/shotcraft/upstream/assets/scripts/test_sfx_source.py +157 -157
  44. package/lib/motion/shotcraft/upstream/demos/README.md +120 -120
  45. package/lib/motion/shotcraft/upstream/demos/_fixtures/Brand.tsx +112 -112
  46. package/lib/motion/shotcraft/upstream/demos/_fixtures/Fixtures.tsx +647 -647
  47. package/lib/motion/shotcraft/upstream/demos/_fixtures/Look.tsx +316 -316
  48. package/lib/motion/shotcraft/upstream/demos/_fixtures/Motion.tsx +82 -82
  49. package/lib/motion/shotcraft/upstream/demos/_fixtures/PageCam2D.tsx +115 -115
  50. package/lib/motion/shotcraft/upstream/demos/_fixtures/Polish.tsx +243 -243
  51. package/lib/motion/shotcraft/upstream/demos/_textures/live-layout.json +388 -388
  52. package/lib/motion/shotcraft/upstream/demos/camera/basic-3d-scene/Basic3DScene.tsx +250 -250
  53. package/lib/motion/shotcraft/upstream/demos/camera/crash-zoom-punch/CrashImpactReal.tsx +305 -305
  54. package/lib/motion/shotcraft/upstream/demos/camera/crash-zoom-punch/CrashZoomReal.tsx +246 -246
  55. package/lib/motion/shotcraft/upstream/demos/camera/cursor-flyover/CursorFlyover.tsx +390 -390
  56. package/lib/motion/shotcraft/upstream/demos/camera/depth-layer-moves/MultiplaneReal.tsx +138 -138
  57. package/lib/motion/shotcraft/upstream/demos/camera/graze-face-tour/GrazeFaceTour.tsx +542 -542
  58. package/lib/motion/shotcraft/upstream/demos/camera/overhead-camera-moves/OverheadTabletopDrop.tsx +315 -315
  59. package/lib/motion/shotcraft/upstream/demos/camera/overhead-camera-moves/TiltReveal.tsx +299 -299
  60. package/lib/motion/shotcraft/upstream/demos/camera/space-camera-moves/DroneDiveLanding.tsx +173 -173
  61. package/lib/motion/shotcraft/upstream/demos/camera/space-camera-moves/ExplodedView.tsx +348 -348
  62. package/lib/motion/shotcraft/upstream/demos/camera/steep-tilt-glide/SteepTiltGlide.tsx +366 -366
  63. package/lib/motion/shotcraft/upstream/demos/camera/tension-camera-moves/BulletTimeFreezeOrbit.tsx +279 -279
  64. package/lib/motion/shotcraft/upstream/demos/camera/tension-camera-moves/PullBackIsolation.tsx +184 -184
  65. package/lib/motion/shotcraft/upstream/demos/camera/terminal-3d/Terminal3D.tsx +390 -390
  66. package/lib/motion/shotcraft/upstream/demos/data/before-after-slider-scrub/BeforeAfterSliderScrub.tsx +155 -155
  67. package/lib/motion/shotcraft/upstream/demos/data/chart-live-moves/AxisRescaleShockV2.tsx +303 -303
  68. package/lib/motion/shotcraft/upstream/demos/data/chart-live-moves/OscilloscopeStreamV2.tsx +290 -290
  69. package/lib/motion/shotcraft/upstream/demos/data/chart-live-moves/UnitDotSwarmRegroupV2.tsx +278 -278
  70. package/lib/motion/shotcraft/upstream/demos/data/gauge-readout-moves/NeedleSweepSelftest.tsx +307 -307
  71. package/lib/motion/shotcraft/upstream/demos/data/gauge-readout-moves/TapeScrollFixedPointer.tsx +215 -215
  72. package/lib/motion/shotcraft/upstream/demos/data/scroll-brake-moves/BrakeReticleLock.tsx +299 -299
  73. package/lib/motion/shotcraft/upstream/demos/data/scroll-brake-moves/ChangelogScrollBrake.tsx +232 -232
  74. package/lib/motion/shotcraft/upstream/demos/data/timeline-travel/TimelineTravel.tsx +290 -290
  75. package/lib/motion/shotcraft/upstream/demos/effects/assemble-then-type-flyin/AssembleThenTypeFlyin.tsx +273 -273
  76. package/lib/motion/shotcraft/upstream/demos/effects/aurora-bloom-bg-flip/AuroraBloomBgFlip.tsx +167 -167
  77. package/lib/motion/shotcraft/upstream/demos/effects/brand-frame-snap/BrandFrameSnap.tsx +536 -536
  78. package/lib/motion/shotcraft/upstream/demos/effects/fui-hud-moves/ReticleLockOn.tsx +164 -164
  79. package/lib/motion/shotcraft/upstream/demos/effects/glow-flyline-moves/FlylineArc.tsx +175 -175
  80. package/lib/motion/shotcraft/upstream/demos/effects/glow-flyline-moves/GlowOrbAmbient.tsx +197 -197
  81. package/lib/motion/shotcraft/upstream/demos/effects/glow-flyline-moves/OrbFlylineRelay.tsx +241 -241
  82. package/lib/motion/shotcraft/upstream/demos/effects/icon-performance-moves/AttentionBounce.tsx +279 -279
  83. package/lib/motion/shotcraft/upstream/demos/effects/impact-feedback/AnimeImpact.tsx +230 -230
  84. package/lib/motion/shotcraft/upstream/demos/effects/impact-feedback/HitCounter.tsx +345 -345
  85. package/lib/motion/shotcraft/upstream/demos/effects/light-play-moves/SheenSweepRetry.tsx +275 -275
  86. package/lib/motion/shotcraft/upstream/demos/effects/light-play-moves/SpotlightSweepReveal.tsx +229 -229
  87. package/lib/motion/shotcraft/upstream/demos/effects/radial-ripple-phone-chips/RadialRipplePhoneChips.tsx +312 -312
  88. package/lib/motion/shotcraft/upstream/demos/effects/riso-print-hits/RisoMisregistrationHit.tsx +222 -222
  89. package/lib/motion/shotcraft/upstream/demos/effects/scan-bracket-sweep/ScanBracketSweep.tsx +295 -295
  90. package/lib/motion/shotcraft/upstream/demos/effects/scanline-annotate-focus/ScanlineAnnotateFocus.tsx +329 -329
  91. package/lib/motion/shotcraft/upstream/demos/effects/scanline-assemble-flyin/ScanlineAssembleFlyin.tsx +315 -315
  92. package/lib/motion/shotcraft/upstream/demos/effects/slam-entrance-moves/ImpactBurstKit.tsx +264 -264
  93. package/lib/motion/shotcraft/upstream/demos/effects/slam-entrance-moves/KanadaPerspectiveSnap.tsx +260 -260
  94. package/lib/motion/shotcraft/upstream/demos/effects/slam-entrance-moves/ScoreSlam.tsx +263 -263
  95. package/lib/motion/shotcraft/upstream/demos/effects/spotlight-sweep-moves/CornerSpotlightReveal.tsx +123 -123
  96. package/lib/motion/shotcraft/upstream/demos/effects/spotlight-sweep-moves/GlowWakeSleepPanel.tsx +187 -187
  97. package/lib/motion/shotcraft/upstream/demos/effects/spotlight-sweep-moves/SlideSpotlightPan.tsx +175 -175
  98. package/lib/motion/shotcraft/upstream/demos/interaction/autolayout-gap-dial/AutolayoutGapDial.tsx +305 -305
  99. package/lib/motion/shotcraft/upstream/demos/interaction/canvas-materialize-moves/DiagramCascadeBuild.tsx +350 -350
  100. package/lib/motion/shotcraft/upstream/demos/interaction/canvas-materialize-moves/PanelToCanvasMaterialize.tsx +352 -352
  101. package/lib/motion/shotcraft/upstream/demos/interaction/chip-grid-single-select-blackout/ChipGridSingleSelectBlackout.tsx +218 -218
  102. package/lib/motion/shotcraft/upstream/demos/interaction/collab-cursor-moves/CursorCastEnsemble.tsx +297 -297
  103. package/lib/motion/shotcraft/upstream/demos/interaction/glass-pill-dictation-typing/GlassPillDictationTyping.tsx +157 -157
  104. package/lib/motion/shotcraft/upstream/demos/interaction/hashtag-to-pill-materialize/HashtagToPillMaterialize.tsx +218 -218
  105. package/lib/motion/shotcraft/upstream/demos/interaction/picker-carousel-feature-cycle/PickerCarouselFeatureCycle.tsx +202 -202
  106. package/lib/motion/shotcraft/upstream/demos/interaction/segmented-thumb-hero/SegmentedThumbHero.tsx +265 -265
  107. package/lib/motion/shotcraft/upstream/demos/interaction/theme-switch-moves/PaletteThemeRipple.tsx +457 -457
  108. package/lib/motion/shotcraft/upstream/demos/interaction/theme-switch-moves/ThemeSweepToggle.tsx +446 -446
  109. package/lib/motion/shotcraft/upstream/demos/interaction/type-and-filter/TypeAndFilter.tsx +249 -249
  110. package/lib/motion/shotcraft/upstream/demos/interaction/voice-waveform-live/VoiceWaveformLive.tsx +278 -278
  111. package/lib/motion/shotcraft/upstream/demos/opening/crane-rise-reveal/CraneRiseReveal.tsx +332 -332
  112. package/lib/motion/shotcraft/upstream/demos/opening/dataviz-landscape-open/DatavizLandscapeOpen.tsx +358 -358
  113. package/lib/motion/shotcraft/upstream/demos/opening/fracture/Fracture.tsx +201 -201
  114. package/lib/motion/shotcraft/upstream/demos/opening/icon-field-colorize/IconFieldColorize.tsx +200 -200
  115. package/lib/motion/shotcraft/upstream/demos/opening/letterspace-materialize/LetterspaceMaterialize.tsx +208 -208
  116. package/lib/motion/shotcraft/upstream/demos/opening/magician-card-flourish/MagicianCardFlourish.tsx +341 -341
  117. package/lib/motion/shotcraft/upstream/demos/opening/orbit-ring-title-open/OrbitRingTitleOpen.tsx +389 -389
  118. package/lib/motion/shotcraft/upstream/demos/opening/spotlight-hero-card/SpotlightHeroCard.tsx +293 -293
  119. package/lib/motion/shotcraft/upstream/demos/opening/stroke-segment-build/StrokeSegmentBuild.tsx +183 -183
  120. package/lib/motion/shotcraft/upstream/demos/opening/text-as-mask/TextAsMask.tsx +245 -245
  121. package/lib/motion/shotcraft/upstream/demos/outro/grain-dissolve/GrainDissolve.tsx +222 -222
  122. package/lib/motion/shotcraft/upstream/demos/outro/logo-shrink-wordmark-lockup/LogoShrinkWordmarkLockup.tsx +223 -223
  123. package/lib/motion/shotcraft/upstream/demos/outro/neon-triple-marquee/NeonTripleMarquee.tsx +217 -217
  124. package/lib/motion/shotcraft/upstream/demos/outro/outro-group-photo-launch/OutroGroupPhotoLaunch.tsx +343 -343
  125. package/lib/motion/shotcraft/upstream/demos/outro/ui-strip-away-outro/UiStripAwayOutro.tsx +357 -357
  126. package/lib/motion/shotcraft/upstream/demos/outro/ui-to-brand-morph/IconFlipBloomLogo.tsx +187 -187
  127. package/lib/motion/shotcraft/upstream/demos/outro/ui-to-brand-morph/InputMorphsIntoLogo.tsx +333 -333
  128. package/lib/motion/shotcraft/upstream/demos/rhythm/beat-cut-moves/PaparazziFlash.tsx +274 -274
  129. package/lib/motion/shotcraft/upstream/demos/rhythm/beat-step-list-theme-cycle/BeatStepListThemeCycle.tsx +270 -270
  130. package/lib/motion/shotcraft/upstream/demos/rhythm/panel-grid-moves/ComicPanelSplit.tsx +430 -430
  131. package/lib/motion/shotcraft/upstream/demos/rhythm/quad-split-parallel-scenes/QuadSplitParallelScenes.tsx +252 -252
  132. package/lib/motion/shotcraft/upstream/demos/rhythm/rhythm-interrupt-moves/JumpCutPunchIn.tsx +106 -106
  133. package/lib/motion/shotcraft/upstream/demos/rhythm/sakuga-timing-shift/SakugaTimingShift.tsx +288 -288
  134. package/lib/motion/shotcraft/upstream/demos/rhythm/speed-ramp-freeze/FreezeAnnotateReal.tsx +362 -362
  135. package/lib/motion/shotcraft/upstream/demos/rhythm/trailer-grammar-moves/TrailerBumper.tsx +168 -168
  136. package/lib/motion/shotcraft/upstream/demos/transition/bottom-push-stack-wipe/BottomPushStackWipe.tsx +310 -310
  137. package/lib/motion/shotcraft/upstream/demos/transition/card-flip-reveal/CardFlipReveal.tsx +261 -261
  138. package/lib/motion/shotcraft/upstream/demos/transition/card-flock-tumble/CardFlockTumble.tsx +459 -459
  139. package/lib/motion/shotcraft/upstream/demos/transition/circle-match-iris/CircleMatchIris.tsx +294 -294
  140. package/lib/motion/shotcraft/upstream/demos/transition/gradient-transition/GradientTransition.tsx +161 -161
  141. package/lib/motion/shotcraft/upstream/demos/transition/line-carry-transition/LineCarryTransition.tsx +248 -248
  142. package/lib/motion/shotcraft/upstream/demos/transition/mosaic-reframe/MosaicReframe.tsx +359 -359
  143. package/lib/motion/shotcraft/upstream/demos/transition/page-turn-transitions/BarnDoorSplit.tsx +227 -227
  144. package/lib/motion/shotcraft/upstream/demos/transition/print-texture-transitions/InkBleedReveal.tsx +162 -162
  145. package/lib/motion/shotcraft/upstream/demos/transition/shot-transitions/PortalWipeV2.tsx +248 -248
  146. package/lib/motion/shotcraft/upstream/demos/transition/shot-transitions/WhipBrakeReal.tsx +186 -186
  147. package/lib/motion/shotcraft/upstream/demos/transition/tear-streak-transitions/GlitchDisplace.tsx +355 -355
  148. package/lib/motion/shotcraft/upstream/demos/transition/transition-hidden-cut/InvisibleCut.tsx +272 -272
  149. package/lib/motion/shotcraft/upstream/demos/transition/transition-hidden-cut/LightLeakBurn.tsx +262 -262
  150. package/lib/motion/shotcraft/upstream/demos/transition/transition-hidden-cut/VersusSlam.tsx +217 -217
  151. package/lib/motion/shotcraft/upstream/demos/transition/transition-travel/LetterformZoom.tsx +285 -285
  152. package/lib/motion/shotcraft/upstream/demos/transition/white-flash-logo-simplify-cut/WhiteFlashLogoSimplifyCut.tsx +205 -205
  153. package/lib/motion/shotcraft/upstream/demos/transition/wipe-transitions/BlindsSlice.tsx +178 -178
  154. package/lib/motion/shotcraft/upstream/demos/typography/blur-slide/BlurSlide.tsx +147 -147
  155. package/lib/motion/shotcraft/upstream/demos/typography/brace-expand/BraceExpand.tsx +143 -143
  156. package/lib/motion/shotcraft/upstream/demos/typography/brand-ink-open/BrandInkOpen.tsx +134 -134
  157. package/lib/motion/shotcraft/upstream/demos/typography/countdown-arc-scatter/CountdownArcScatter.tsx +217 -217
  158. package/lib/motion/shotcraft/upstream/demos/typography/flying-words/FlyingWords.tsx +198 -198
  159. package/lib/motion/shotcraft/upstream/demos/typography/glitch-cycle/GlitchCycle.tsx +221 -221
  160. package/lib/motion/shotcraft/upstream/demos/typography/gradient-word-sweep/GradientWordSweep.tsx +312 -312
  161. package/lib/motion/shotcraft/upstream/demos/typography/lead-word-zoom-assemble/LeadWordZoomAssemble.tsx +261 -261
  162. package/lib/motion/shotcraft/upstream/demos/typography/marker-underline-title/MarkerUnderlineTitle.tsx +121 -121
  163. package/lib/motion/shotcraft/upstream/demos/typography/outline-word-fill/OutlineWordFill.tsx +153 -153
  164. package/lib/motion/shotcraft/upstream/demos/typography/paper-title-card/PaperTitleCard.tsx +179 -179
  165. package/lib/motion/shotcraft/upstream/demos/typography/pill-chip-slot-cycle-handled/PillChipSlotCycleHandled.tsx +140 -140
  166. package/lib/motion/shotcraft/upstream/demos/typography/pill-slot-cycle/PillSlotCycle.tsx +309 -309
  167. package/lib/motion/shotcraft/upstream/demos/typography/text-column-converge/TextColumnConverge.tsx +191 -191
  168. package/lib/motion/shotcraft/upstream/demos/typography/title-demote-to-label/TitleDemoteToLabel.tsx +292 -292
  169. package/lib/motion/shotcraft/upstream/demos/typography/type-assembly-moves/LetterformDriftAssembly.tsx +135 -135
  170. package/lib/motion/shotcraft/upstream/demos/typography/type-assembly-moves/SplitTextStagger.tsx +124 -124
  171. package/lib/motion/shotcraft/upstream/demos/typography/type-assembly-moves/TextOnPath.tsx +255 -255
  172. package/lib/motion/shotcraft/upstream/demos/typography/type-entrance-moves/LetterDropPhysics.tsx +173 -173
  173. package/lib/motion/shotcraft/upstream/demos/typography/type-rhythm-sync/FontWeightPump.tsx +225 -225
  174. package/lib/motion/shotcraft/upstream/demos/typography/type-rhythm-sync/KaraokeFillSync.tsx +141 -141
  175. package/lib/motion/shotcraft/upstream/demos/typography/typewriter-moves/TypewriterErrorRetype.tsx +137 -137
  176. package/lib/motion/shotcraft/upstream/demos/typography/vertical-word-roll-blur-cycle/VerticalWordRollBlurCycle.tsx +147 -147
  177. package/lib/motion/shotcraft/upstream/demos/typography/word-relay-filmstrip/WordRelayFilmstrip.tsx +409 -409
  178. package/lib/motion/shotcraft/upstream/demos/typography/word-relay-geometry/WordRelayGeometry.tsx +280 -280
  179. package/lib/motion/shotcraft/upstream/demos/ui-entrance/avatar-bracket-carousel/AvatarBracketCarousel.tsx +271 -271
  180. package/lib/motion/shotcraft/upstream/demos/ui-entrance/bezier-source-converge-merge/BezierSourceConvergeMerge.tsx +385 -385
  181. package/lib/motion/shotcraft/upstream/demos/ui-entrance/card-stack/CardStack.tsx +312 -312
  182. package/lib/motion/shotcraft/upstream/demos/ui-entrance/carousel-3d/Carousel3D.tsx +303 -303
  183. package/lib/motion/shotcraft/upstream/demos/ui-entrance/clipcard-looping/ClipCardLooping.tsx +77 -77
  184. package/lib/motion/shotcraft/upstream/demos/ui-entrance/cloner-depth-echo/ClonerDepthEcho.tsx +311 -311
  185. package/lib/motion/shotcraft/upstream/demos/ui-entrance/deck-deal-flyin/DeckDealFlyin.tsx +307 -307
  186. package/lib/motion/shotcraft/upstream/demos/ui-entrance/doc-park-left-pill-deal/DocParkLeftPillDeal.tsx +349 -349
  187. package/lib/motion/shotcraft/upstream/demos/ui-entrance/document-typewriter-reveal/DocumentTypewriterReveal.tsx +255 -255
  188. package/lib/motion/shotcraft/upstream/demos/ui-entrance/draw-svg-trace/DrawSvgTrace.tsx +244 -244
  189. package/lib/motion/shotcraft/upstream/demos/ui-entrance/element-body-moves/ContactShadowLift.tsx +217 -217
  190. package/lib/motion/shotcraft/upstream/demos/ui-entrance/integration-hub-map/IntegrationHubMap.tsx +652 -652
  191. package/lib/motion/shotcraft/upstream/demos/ui-entrance/list-reveal/ListReveal.tsx +153 -153
  192. package/lib/motion/shotcraft/upstream/demos/ui-entrance/list-stack-press/ListStackPress.tsx +238 -238
  193. package/lib/motion/shotcraft/upstream/demos/ui-entrance/neon-frame-forerun/NeonFrameForerun.tsx +324 -324
  194. package/lib/motion/shotcraft/upstream/demos/ui-entrance/neon-frame-orbit-drop/NeonFrameForerunOrbit.tsx +354 -354
  195. package/lib/motion/shotcraft/upstream/demos/ui-entrance/page-waterfall-wall/PageWaterfallWall.tsx +101 -101
  196. package/lib/motion/shotcraft/upstream/demos/ui-entrance/page-waterfall-wall/VerticalTicker.tsx +129 -129
  197. package/lib/motion/shotcraft/upstream/demos/ui-entrance/product-card-progressive-assemble/ProductCardProgressiveAssemble.tsx +297 -297
  198. package/lib/motion/shotcraft/upstream/demos/ui-entrance/radial-wave/RadialWave.tsx +57 -57
  199. package/lib/motion/shotcraft/upstream/demos/ui-entrance/research-card-stack-scroll/ResearchCardStackScroll.tsx +155 -155
  200. package/lib/motion/shotcraft/upstream/demos/ui-entrance/row-embed/RowEmbed.tsx +175 -175
  201. package/lib/motion/shotcraft/upstream/demos/ui-entrance/value-stagger-gradient/ValueStaggerGradient.tsx +153 -153
  202. package/lib/motion/shotcraft/upstream/demos/ui-entrance/wall-reveal-moves/BentoLightUp.tsx +146 -146
  203. package/lib/motion/shotcraft/upstream/demos/ui-entrance/wall-reveal-moves/WireframeDrawOn.tsx +226 -226
  204. package/lib/motion/shotcraft/upstream/gallery/api/library.json +1 -1
  205. package/lib/motion/shotcraft/upstream/references/aesthetic-rules.md +193 -193
  206. package/lib/motion/shotcraft/upstream/references/final-review.md +121 -121
  207. package/lib/motion/shotcraft/upstream/references/guided-free-creation.md +199 -199
  208. package/lib/motion/shotcraft/upstream/references/jianying-export.md +183 -183
  209. package/lib/motion/shotcraft/upstream/references/music-beat-sync.md +257 -257
  210. package/lib/motion/shotcraft/upstream/references/pipeline.md +382 -382
  211. package/lib/motion/shotcraft/upstream/references/publish-upload-post.md +111 -111
  212. package/lib/motion/shotcraft/upstream/references/sequences/promo-energy-arc.md +50 -50
  213. package/lib/motion/shotcraft/upstream/references/sfx-sourcing.md +95 -95
  214. package/lib/motion/shotcraft/upstream/references/shots/camera/basic-3d-scene.md +41 -41
  215. package/lib/motion/shotcraft/upstream/references/shots/camera/crash-zoom-punch.md +52 -52
  216. package/lib/motion/shotcraft/upstream/references/shots/camera/cursor-flyover.md +39 -39
  217. package/lib/motion/shotcraft/upstream/references/shots/camera/depth-layer-moves.md +39 -39
  218. package/lib/motion/shotcraft/upstream/references/shots/camera/graze-face-tour.md +57 -57
  219. package/lib/motion/shotcraft/upstream/references/shots/camera/overhead-camera-moves.md +51 -51
  220. package/lib/motion/shotcraft/upstream/references/shots/camera/space-camera-moves.md +46 -46
  221. package/lib/motion/shotcraft/upstream/references/shots/camera/steep-tilt-glide.md +52 -52
  222. package/lib/motion/shotcraft/upstream/references/shots/camera/tension-camera-moves.md +43 -43
  223. package/lib/motion/shotcraft/upstream/references/shots/camera/terminal-3d.md +42 -42
  224. package/lib/motion/shotcraft/upstream/references/shots/data/before-after-slider-scrub.md +48 -48
  225. package/lib/motion/shotcraft/upstream/references/shots/data/chart-live-moves.md +52 -52
  226. package/lib/motion/shotcraft/upstream/references/shots/data/gauge-readout-moves.md +45 -45
  227. package/lib/motion/shotcraft/upstream/references/shots/data/scroll-brake-moves.md +46 -46
  228. package/lib/motion/shotcraft/upstream/references/shots/data/timeline-travel.md +44 -44
  229. package/lib/motion/shotcraft/upstream/references/shots/effects/assemble-then-type-flyin.md +63 -63
  230. package/lib/motion/shotcraft/upstream/references/shots/effects/aurora-bloom-bg-flip.md +54 -54
  231. package/lib/motion/shotcraft/upstream/references/shots/effects/brand-frame-snap.md +58 -58
  232. package/lib/motion/shotcraft/upstream/references/shots/effects/fui-hud-moves.md +35 -35
  233. package/lib/motion/shotcraft/upstream/references/shots/effects/glow-flyline-moves.md +53 -53
  234. package/lib/motion/shotcraft/upstream/references/shots/effects/icon-performance-moves.md +42 -42
  235. package/lib/motion/shotcraft/upstream/references/shots/effects/impact-feedback.md +50 -50
  236. package/lib/motion/shotcraft/upstream/references/shots/effects/light-play-moves.md +46 -46
  237. package/lib/motion/shotcraft/upstream/references/shots/effects/radial-ripple-phone-chips.md +56 -56
  238. package/lib/motion/shotcraft/upstream/references/shots/effects/riso-print-hits.md +49 -49
  239. package/lib/motion/shotcraft/upstream/references/shots/effects/scan-bracket-sweep.md +50 -50
  240. package/lib/motion/shotcraft/upstream/references/shots/effects/scanline-annotate-focus.md +53 -53
  241. package/lib/motion/shotcraft/upstream/references/shots/effects/scanline-assemble-flyin.md +53 -53
  242. package/lib/motion/shotcraft/upstream/references/shots/effects/slam-entrance-moves.md +55 -55
  243. package/lib/motion/shotcraft/upstream/references/shots/effects/spotlight-sweep-moves.md +52 -52
  244. package/lib/motion/shotcraft/upstream/references/shots/interaction/autolayout-gap-dial.md +60 -60
  245. package/lib/motion/shotcraft/upstream/references/shots/interaction/canvas-materialize-moves.md +57 -57
  246. package/lib/motion/shotcraft/upstream/references/shots/interaction/chip-grid-single-select-blackout.md +51 -51
  247. package/lib/motion/shotcraft/upstream/references/shots/interaction/collab-cursor-moves.md +41 -41
  248. package/lib/motion/shotcraft/upstream/references/shots/interaction/glass-pill-dictation-typing.md +53 -53
  249. package/lib/motion/shotcraft/upstream/references/shots/interaction/hashtag-to-pill-materialize.md +60 -60
  250. package/lib/motion/shotcraft/upstream/references/shots/interaction/picker-carousel-feature-cycle.md +55 -55
  251. package/lib/motion/shotcraft/upstream/references/shots/interaction/segmented-thumb-hero.md +57 -57
  252. package/lib/motion/shotcraft/upstream/references/shots/interaction/theme-switch-moves.md +47 -47
  253. package/lib/motion/shotcraft/upstream/references/shots/interaction/type-and-filter.md +37 -37
  254. package/lib/motion/shotcraft/upstream/references/shots/interaction/voice-waveform-live.md +54 -54
  255. package/lib/motion/shotcraft/upstream/references/shots/opening/brand-ink-open.md +36 -36
  256. package/lib/motion/shotcraft/upstream/references/shots/opening/crane-rise-reveal.md +56 -56
  257. package/lib/motion/shotcraft/upstream/references/shots/opening/dataviz-landscape-open.md +56 -56
  258. package/lib/motion/shotcraft/upstream/references/shots/opening/fracture.md +40 -40
  259. package/lib/motion/shotcraft/upstream/references/shots/opening/icon-field-colorize.md +60 -60
  260. package/lib/motion/shotcraft/upstream/references/shots/opening/letterspace-materialize.md +57 -57
  261. package/lib/motion/shotcraft/upstream/references/shots/opening/magician-card-flourish.md +73 -73
  262. package/lib/motion/shotcraft/upstream/references/shots/opening/orbit-ring-title-open.md +87 -87
  263. package/lib/motion/shotcraft/upstream/references/shots/opening/spotlight-hero-card.md +55 -55
  264. package/lib/motion/shotcraft/upstream/references/shots/opening/stroke-segment-build.md +50 -50
  265. package/lib/motion/shotcraft/upstream/references/shots/opening/text-as-mask.md +59 -59
  266. package/lib/motion/shotcraft/upstream/references/shots/outro/grain-dissolve.md +38 -38
  267. package/lib/motion/shotcraft/upstream/references/shots/outro/logo-shrink-wordmark-lockup.md +38 -38
  268. package/lib/motion/shotcraft/upstream/references/shots/outro/neon-triple-marquee.md +59 -59
  269. package/lib/motion/shotcraft/upstream/references/shots/outro/outro-group-photo-launch.md +39 -39
  270. package/lib/motion/shotcraft/upstream/references/shots/outro/ui-strip-away-outro.md +61 -61
  271. package/lib/motion/shotcraft/upstream/references/shots/outro/ui-to-brand-morph.md +52 -52
  272. package/lib/motion/shotcraft/upstream/references/shots/rhythm/beat-cut-moves.md +50 -50
  273. package/lib/motion/shotcraft/upstream/references/shots/rhythm/beat-step-list-theme-cycle.md +66 -66
  274. package/lib/motion/shotcraft/upstream/references/shots/rhythm/panel-grid-moves.md +43 -43
  275. package/lib/motion/shotcraft/upstream/references/shots/rhythm/quad-split-parallel-scenes.md +38 -38
  276. package/lib/motion/shotcraft/upstream/references/shots/rhythm/rhythm-interrupt-moves.md +38 -38
  277. package/lib/motion/shotcraft/upstream/references/shots/rhythm/sakuga-timing-shift.md +56 -56
  278. package/lib/motion/shotcraft/upstream/references/shots/rhythm/speed-ramp-freeze.md +44 -44
  279. package/lib/motion/shotcraft/upstream/references/shots/rhythm/trailer-grammar-moves.md +38 -38
  280. package/lib/motion/shotcraft/upstream/references/shots/transition/bottom-push-stack-wipe.md +63 -63
  281. package/lib/motion/shotcraft/upstream/references/shots/transition/card-flip-reveal.md +57 -57
  282. package/lib/motion/shotcraft/upstream/references/shots/transition/card-flock-tumble.md +63 -63
  283. package/lib/motion/shotcraft/upstream/references/shots/transition/circle-match-iris.md +58 -58
  284. package/lib/motion/shotcraft/upstream/references/shots/transition/gradient-transition.md +45 -45
  285. package/lib/motion/shotcraft/upstream/references/shots/transition/line-carry-transition.md +55 -55
  286. package/lib/motion/shotcraft/upstream/references/shots/transition/mosaic-reframe.md +49 -49
  287. package/lib/motion/shotcraft/upstream/references/shots/transition/page-turn-transitions.md +42 -42
  288. package/lib/motion/shotcraft/upstream/references/shots/transition/print-texture-transitions.md +42 -42
  289. package/lib/motion/shotcraft/upstream/references/shots/transition/shot-transitions.md +45 -45
  290. package/lib/motion/shotcraft/upstream/references/shots/transition/tear-streak-transitions.md +48 -48
  291. package/lib/motion/shotcraft/upstream/references/shots/transition/transition-hidden-cut.md +55 -55
  292. package/lib/motion/shotcraft/upstream/references/shots/transition/transition-travel.md +43 -43
  293. package/lib/motion/shotcraft/upstream/references/shots/transition/white-flash-logo-simplify-cut.md +41 -41
  294. package/lib/motion/shotcraft/upstream/references/shots/transition/wipe-transitions.md +42 -42
  295. package/lib/motion/shotcraft/upstream/references/shots/typography/blur-slide.md +59 -59
  296. package/lib/motion/shotcraft/upstream/references/shots/typography/brace-expand.md +55 -55
  297. package/lib/motion/shotcraft/upstream/references/shots/typography/countdown-arc-scatter.md +65 -65
  298. package/lib/motion/shotcraft/upstream/references/shots/typography/document-typewriter-reveal.md +42 -42
  299. package/lib/motion/shotcraft/upstream/references/shots/typography/flying-words.md +64 -64
  300. package/lib/motion/shotcraft/upstream/references/shots/typography/glitch-cycle.md +64 -64
  301. package/lib/motion/shotcraft/upstream/references/shots/typography/gradient-word-sweep.md +54 -54
  302. package/lib/motion/shotcraft/upstream/references/shots/typography/lead-word-zoom-assemble.md +91 -91
  303. package/lib/motion/shotcraft/upstream/references/shots/typography/marker-underline-title.md +51 -51
  304. package/lib/motion/shotcraft/upstream/references/shots/typography/outline-word-fill.md +66 -66
  305. package/lib/motion/shotcraft/upstream/references/shots/typography/paper-title-card.md +38 -38
  306. package/lib/motion/shotcraft/upstream/references/shots/typography/pill-chip-slot-cycle-handled.md +66 -66
  307. package/lib/motion/shotcraft/upstream/references/shots/typography/pill-slot-cycle.md +58 -58
  308. package/lib/motion/shotcraft/upstream/references/shots/typography/text-column-converge.md +61 -61
  309. package/lib/motion/shotcraft/upstream/references/shots/typography/title-demote-to-label.md +64 -64
  310. package/lib/motion/shotcraft/upstream/references/shots/typography/type-assembly-moves.md +45 -45
  311. package/lib/motion/shotcraft/upstream/references/shots/typography/type-entrance-moves.md +41 -41
  312. package/lib/motion/shotcraft/upstream/references/shots/typography/type-rhythm-sync.md +51 -51
  313. package/lib/motion/shotcraft/upstream/references/shots/typography/typewriter-moves.md +42 -42
  314. package/lib/motion/shotcraft/upstream/references/shots/typography/vertical-word-roll-blur-cycle.md +63 -63
  315. package/lib/motion/shotcraft/upstream/references/shots/typography/word-relay-filmstrip.md +57 -57
  316. package/lib/motion/shotcraft/upstream/references/shots/typography/word-relay-geometry.md +75 -75
  317. package/lib/motion/shotcraft/upstream/references/shots/ui-entrance/avatar-bracket-carousel.md +59 -59
  318. package/lib/motion/shotcraft/upstream/references/shots/ui-entrance/bezier-source-converge-merge.md +67 -67
  319. package/lib/motion/shotcraft/upstream/references/shots/ui-entrance/card-stack.md +60 -60
  320. package/lib/motion/shotcraft/upstream/references/shots/ui-entrance/carousel-3d.md +66 -66
  321. package/lib/motion/shotcraft/upstream/references/shots/ui-entrance/cloner-depth-echo.md +40 -40
  322. package/lib/motion/shotcraft/upstream/references/shots/ui-entrance/deck-deal-flyin.md +45 -45
  323. package/lib/motion/shotcraft/upstream/references/shots/ui-entrance/doc-park-left-pill-deal.md +62 -62
  324. package/lib/motion/shotcraft/upstream/references/shots/ui-entrance/draw-svg-trace.md +55 -55
  325. package/lib/motion/shotcraft/upstream/references/shots/ui-entrance/element-body-moves.md +37 -37
  326. package/lib/motion/shotcraft/upstream/references/shots/ui-entrance/integration-hub-map.md +55 -55
  327. package/lib/motion/shotcraft/upstream/references/shots/ui-entrance/list-reveal.md +50 -50
  328. package/lib/motion/shotcraft/upstream/references/shots/ui-entrance/list-stack-press.md +43 -43
  329. package/lib/motion/shotcraft/upstream/references/shots/ui-entrance/neon-frame-forerun.md +55 -55
  330. package/lib/motion/shotcraft/upstream/references/shots/ui-entrance/neon-frame-orbit-drop.md +51 -51
  331. package/lib/motion/shotcraft/upstream/references/shots/ui-entrance/page-waterfall-wall.md +51 -51
  332. package/lib/motion/shotcraft/upstream/references/shots/ui-entrance/product-card-progressive-assemble.md +56 -56
  333. package/lib/motion/shotcraft/upstream/references/shots/ui-entrance/radial-wave.md +49 -49
  334. package/lib/motion/shotcraft/upstream/references/shots/ui-entrance/research-card-stack-scroll.md +57 -57
  335. package/lib/motion/shotcraft/upstream/references/shots/ui-entrance/row-embed.md +36 -36
  336. package/lib/motion/shotcraft/upstream/references/shots/ui-entrance/value-stagger-gradient.md +64 -64
  337. package/lib/motion/shotcraft/upstream/references/shots/ui-entrance/wall-reveal-moves.md +47 -47
  338. package/lib/motion/shotcraft/upstream/references/sound-design.md +170 -170
  339. package/lib/motion/shotcraft/upstream/references/workbench.md +157 -157
  340. package/lib/motion/shotcraft/upstream/template/TEMPLATE.md +103 -103
  341. package/lib/motion/shotcraft/upstream/template/THEMES.md +44 -44
  342. package/lib/motion/shotcraft/upstream/template/package.json +20 -20
  343. package/lib/motion/shotcraft/upstream/template/remotion.config.ts +6 -6
  344. package/lib/motion/shotcraft/upstream/template/src/Root.tsx +17 -17
  345. package/lib/motion/shotcraft/upstream/template/src/aifl/Caption.tsx +65 -65
  346. package/lib/motion/shotcraft/upstream/template/src/aifl/DigitRoll.tsx +48 -48
  347. package/lib/motion/shotcraft/upstream/template/src/aifl/FlashCut.tsx +24 -24
  348. package/lib/motion/shotcraft/upstream/template/src/aifl/Main.tsx +192 -192
  349. package/lib/motion/shotcraft/upstream/template/src/aifl/PaperTitleCard.tsx +111 -111
  350. package/lib/motion/shotcraft/upstream/template/src/aifl/live/PageCam.tsx +150 -150
  351. package/lib/motion/shotcraft/upstream/template/src/aifl/live/SceneDetail.tsx +158 -158
  352. package/lib/motion/shotcraft/upstream/template/src/aifl/live/SceneFlyIn.tsx +468 -468
  353. package/lib/motion/shotcraft/upstream/template/src/aifl/live/SceneOpen.tsx +552 -552
  354. package/lib/motion/shotcraft/upstream/template/src/aifl/live/SceneOutroLive.tsx +411 -411
  355. package/lib/motion/shotcraft/upstream/template/src/aifl/live/ScenePapers.tsx +200 -200
  356. package/lib/motion/shotcraft/upstream/template/src/aifl/live/SceneWbr.tsx +324 -324
  357. package/lib/motion/shotcraft/upstream/template/src/aifl/live-layout.json +388 -388
  358. package/lib/motion/shotcraft/upstream/template/src/index.ts +4 -4
  359. package/lib/motion/shotcraft/upstream/template/src/themes/ThemedFilm.tsx +7 -7
  360. package/lib/motion/shotcraft/upstream/template/src/themes/manifest.tsx +68 -68
  361. package/lib/motion/shotcraft/upstream/template/src/themes/palette-assets.json +1 -1
  362. package/lib/motion/shotcraft/upstream/template/src/themes/palette-assets.ts +24 -24
  363. package/lib/motion/shotcraft/upstream/template/src/themes/palettes.json +74 -74
  364. package/lib/motion/shotcraft/upstream/template/src/themes/visual-theme.tsx +91 -91
  365. package/lib/motion/shotcraft/upstream/template/src/workbench.ts +213 -213
  366. package/lib/motion/shotcraft/upstream/template/tsconfig.json +15 -15
  367. package/lib/motion/style-frames.js +306 -306
  368. package/lib/motion/sync.js +214 -214
  369. package/lib/motion-library.js +1 -1
  370. package/lib/motion-project.js +105 -105
  371. package/lib/motion-render-worker.js +174 -174
  372. package/lib/routing/router.js +222 -222
  373. package/lib/routing/vendor/README.md +96 -96
  374. package/lib/routing/vendor/libavoid-routing.js +753 -753
  375. package/lib/routing/vendor/libavoid.min.js +59 -59
  376. package/lib/svg-export.js +1344 -0
  377. package/lib/video-trace-pool.js +128 -128
  378. package/package.json +3 -2
  379. package/LICENSE +0 -4175
@@ -1,106 +1,106 @@
1
1
  /* 由 tools/build.mjs 生成 —— 请勿直接编辑;改 src/ 下的源文件。 */
2
- import { existsSync, mkdirSync, writeFileSync, readFileSync, realpathSync } from 'node:fs'
3
- import { join, dirname, resolve, relative, isAbsolute } from 'node:path'
4
- import { fileURLToPath } from 'node:url'
5
- import { videoRuntimeRequire } from './motion/video-runtime.js'
6
- import { validateMotionSources, sceneComponentPath } from './motion-source.js'
7
- import { shotcraftModuleMap } from './motion-library.js'
8
- import { enforceMotionCards } from './motion-library.js'
9
- import { enforceMgStyle } from './motion-library.js'
10
- import { validateMotionPlan } from './motion-core.js'
11
- import { parseProjectJson } from './motion/json.js'
12
-
13
- // Agent projects are edited on disk. Read their sources before validating or
14
- // building; never restore a stale inline source over an agent's changes.
15
- export function readMotionProject(directory, input) {
16
- const root=realpathSync(directory)
17
- const read=(name)=>{
18
- const file=realpathSync(join(root,name)),rel=relative(root,file)
19
- if(isAbsolute(rel)||rel==='..'||/^\.\.[\\/]/.test(rel))throw new Error('工程文件超出任务目录')
20
- return readFileSync(file,'utf8')
21
- }
22
- const raw=parseProjectJson(read('composition.json'))
23
- if(!Array.isArray(raw.shots))throw new Error('工程缺少 shots 分镜')
24
- raw.shots=raw.shots.map((shot,index)=>({...shot,sceneSource:read('scene-'+index+'.tsx'),
25
- ...(shot.sceneComponents?{sceneComponents:shot.sceneComponents.map(c=>({...c,source:read(sceneComponentPath('shot-'+(index+1),c.id))}))}:{})}))
26
- return enforceMgStyle(enforceMotionCards(validateMotionSources(validateMotionPlan(raw,input)),input),input)
27
- }
28
-
29
- // 入口与运行时 JSX 的源文件在插件里,但它们**必须被复制到工程目录**再引用:
30
- // Remotion 的 webpack 只给 .tsx?/.ts 挂 esbuild-loader,配 .jsx? 的那条规则显式
31
- // exclude /node_modules/ —— 插件装在 profile 的 node_modules 下时,入口 JSX 会被当成
32
- // 普通文本解析,报 "Unexpected token (<AbsoluteFill>)"(issue #21、#22)。
33
- const RUNTIME_ENTRY = fileURLToPath(new URL('./motion/entry.jsx', import.meta.url))
34
- const RUNTIME_REFERENCE = fileURLToPath(new URL('./motion/reference-media.jsx', import.meta.url))
35
- const GENERATED_HEADER = '// 由 dsh-drawai 生成,请勿手改;插件更新后会按源文件重新生成。\n'
36
-
37
- // 只在内容变化时写盘:保持 mtime 稳定,bundle 缓存才不会每次重建。
38
- function writeGenerated(file, source) {
39
- const desired = GENERATED_HEADER + source
40
- if (existsSync(file) && readFileSync(file, 'utf8') === desired) return
41
- mkdirSync(dirname(file), { recursive: true })
42
- writeFileSync(file, desired)
43
- }
44
-
45
- export function prepareMotionProject(composition, directory, options = {}) {
46
- validateMotionSources(composition)
47
- mkdirSync(directory, { recursive: true })
48
- const imports = [], entries = []
49
- composition.shots.forEach((shot, index) => {
50
- if (shot.recipeId !== 'scene') return
51
- const name = 'scene-' + index + '.tsx'
52
- if(!options.preserveSources)writeFileSync(join(directory, name), shot.sceneSource)
53
- for (const component of shot.sceneComponents || []) {
54
- const file = join(directory, sceneComponentPath(shot.id, component.id))
55
- mkdirSync(dirname(file), { recursive: true })
56
- if(!options.preserveSources)writeFileSync(file, component.source)
57
- }
58
- imports.push(`import Scene${index} from './${name}';`)
59
- entries.push(`${JSON.stringify(shot.id)}:Scene${index}`)
60
- })
61
- writeGenerated(join(directory, 'entry.gen.jsx'), readFileSync(RUNTIME_ENTRY, 'utf8'))
62
- writeGenerated(join(directory, 'reference-media.gen.jsx'), readFileSync(RUNTIME_REFERENCE, 'utf8'))
63
- const entryPoint = join(directory, 'index.jsx')
64
- const entrySource = `import {registerMotionRoot} from './entry.gen.jsx';\n${imports.join('\n')}\nregisterMotionRoot({${entries.join(',')}});\n`
65
- // 入口本身就是生成的:内容变了(换插件路径、增删镜头)就重写,不看 preserveSources。
66
- if(!existsSync(entryPoint) || readFileSync(entryPoint, 'utf8') !== entrySource)writeFileSync(entryPoint, entrySource)
67
- if(!options.preserveSources)writeFileSync(join(directory, 'composition.json'), JSON.stringify(composition, null, 2))
68
- if (composition.production && !options.preserveSources) {
69
- writeFileSync(join(directory, 'design.json'), JSON.stringify(composition.production, null, 2))
70
- writeFileSync(join(directory, 'storyboard.json'), JSON.stringify(composition.shots.map(({ sceneSource: _source, sceneComponents, ...shot }) => ({ ...shot,
71
- ...(sceneComponents ? { components: sceneComponents.map(({ source: _code, ...component }) => component) } : {}) })), null, 2))
72
- }
73
- const alias = Object.fromEntries(Object.entries(shotcraftModuleMap()).map(([name, url]) => [name + '$', fileURLToPath(url)]))
74
- // Demos use paths relative to the upstream repository. Generated components
75
- // live elsewhere: resolve known library paths without rewriting their source.
76
- const library = shotcraftModuleMap()
77
- const libraryImports = { apply(compiler) {
78
- compiler.hooks.normalModuleFactory.tap('DrawAiShotcraftImports', (factory) => {
79
- factory.hooks.beforeResolve.tap('DrawAiShotcraftImports', (request) => {
80
- if (!request?.request?.startsWith('.')) return
81
- const local = relative(resolve(directory), resolve(request.context))
82
- if (isAbsolute(local) || local === '..' || /^\.\.[\\/]/.test(local)) return
83
- const stem = resolve(request.context, request.request)
84
- if (['', '.tsx', '.ts', '.jsx', '.js', '/index.tsx', '/index.ts', '/index.js'].some((ext) => existsSync(stem + ext))) return
85
- const path = request.request.replaceAll('\\', '/').replace(/^(?:\.\.?\/)+/, '').replace(/\.(tsx?|jsx?)$/, '')
86
- const name = path.startsWith('demos/') ? '@shotcraft/' + path
87
- : path.startsWith('_fixtures/') ? '@shotcraft/demos/' + path
88
- : path.startsWith('assets/lib/') ? '@shotcraft/lib/' + path.slice('assets/lib/'.length)
89
- : path.startsWith('lib/') ? '@shotcraft/' + path : ''
90
- if (Object.hasOwn(library, name)) request.request = fileURLToPath(library[name])
91
- })
92
- })
93
- } }
94
- alias['@drawai/motion$'] = join(directory, 'reference-media.gen.jsx')
95
- // Resolve the runtime lazily: creating a project only writes its files, and
96
- // the video runtime may not be initialized yet (it is installed on first use).
97
- const runtimeModules = () => {
98
- const require = videoRuntimeRequire(options.runtimeDirectory)
99
- return [...new Set(['react', 'remotion', '@remotion/motion-blur'].map((name) => dirname(require.resolve(name + '/package.json')).replace(/[\\/]@remotion[\\/][^\\/]+$/, '').replace(/[\\/](?:react|remotion)$/, '')))]
100
- }
101
- return { entryPoint, webpackOverride: (config) => {
102
- const modules = runtimeModules()
103
- return { ...config, plugins: [...(config.plugins || []), libraryImports], resolve: { ...config.resolve,
104
- alias: { ...config.resolve?.alias, ...alias }, modules: [...modules, ...(config.resolve?.modules || ['node_modules'])] } }
105
- } }
106
- }
2
+ import { existsSync, mkdirSync, writeFileSync, readFileSync, realpathSync } from 'node:fs'
3
+ import { join, dirname, resolve, relative, isAbsolute } from 'node:path'
4
+ import { fileURLToPath } from 'node:url'
5
+ import { videoRuntimeRequire } from './motion/video-runtime.js'
6
+ import { validateMotionSources, sceneComponentPath } from './motion-source.js'
7
+ import { shotcraftModuleMap } from './motion-library.js'
8
+ import { enforceMotionCards } from './motion-library.js'
9
+ import { enforceMgStyle } from './motion-library.js'
10
+ import { validateMotionPlan } from './motion-core.js'
11
+ import { parseProjectJson } from './motion/json.js'
12
+
13
+ // Agent projects are edited on disk. Read their sources before validating or
14
+ // building; never restore a stale inline source over an agent's changes.
15
+ export function readMotionProject(directory, input) {
16
+ const root=realpathSync(directory)
17
+ const read=(name)=>{
18
+ const file=realpathSync(join(root,name)),rel=relative(root,file)
19
+ if(isAbsolute(rel)||rel==='..'||/^\.\.[\\/]/.test(rel))throw new Error('工程文件超出任务目录')
20
+ return readFileSync(file,'utf8')
21
+ }
22
+ const raw=parseProjectJson(read('composition.json'))
23
+ if(!Array.isArray(raw.shots))throw new Error('工程缺少 shots 分镜')
24
+ raw.shots=raw.shots.map((shot,index)=>({...shot,sceneSource:read('scene-'+index+'.tsx'),
25
+ ...(shot.sceneComponents?{sceneComponents:shot.sceneComponents.map(c=>({...c,source:read(sceneComponentPath('shot-'+(index+1),c.id))}))}:{})}))
26
+ return enforceMgStyle(enforceMotionCards(validateMotionSources(validateMotionPlan(raw,input)),input),input)
27
+ }
28
+
29
+ // 入口与运行时 JSX 的源文件在插件里,但它们**必须被复制到工程目录**再引用:
30
+ // Remotion 的 webpack 只给 .tsx?/.ts 挂 esbuild-loader,配 .jsx? 的那条规则显式
31
+ // exclude /node_modules/ —— 插件装在 profile 的 node_modules 下时,入口 JSX 会被当成
32
+ // 普通文本解析,报 "Unexpected token (<AbsoluteFill>)"(issue #21、#22)。
33
+ const RUNTIME_ENTRY = fileURLToPath(new URL('./motion/entry.jsx', import.meta.url))
34
+ const RUNTIME_REFERENCE = fileURLToPath(new URL('./motion/reference-media.jsx', import.meta.url))
35
+ const GENERATED_HEADER = '// 由 dsh-drawai 生成,请勿手改;插件更新后会按源文件重新生成。\n'
36
+
37
+ // 只在内容变化时写盘:保持 mtime 稳定,bundle 缓存才不会每次重建。
38
+ function writeGenerated(file, source) {
39
+ const desired = GENERATED_HEADER + source
40
+ if (existsSync(file) && readFileSync(file, 'utf8') === desired) return
41
+ mkdirSync(dirname(file), { recursive: true })
42
+ writeFileSync(file, desired)
43
+ }
44
+
45
+ export function prepareMotionProject(composition, directory, options = {}) {
46
+ validateMotionSources(composition)
47
+ mkdirSync(directory, { recursive: true })
48
+ const imports = [], entries = []
49
+ composition.shots.forEach((shot, index) => {
50
+ if (shot.recipeId !== 'scene') return
51
+ const name = 'scene-' + index + '.tsx'
52
+ if(!options.preserveSources)writeFileSync(join(directory, name), shot.sceneSource)
53
+ for (const component of shot.sceneComponents || []) {
54
+ const file = join(directory, sceneComponentPath(shot.id, component.id))
55
+ mkdirSync(dirname(file), { recursive: true })
56
+ if(!options.preserveSources)writeFileSync(file, component.source)
57
+ }
58
+ imports.push(`import Scene${index} from './${name}';`)
59
+ entries.push(`${JSON.stringify(shot.id)}:Scene${index}`)
60
+ })
61
+ writeGenerated(join(directory, 'entry.gen.jsx'), readFileSync(RUNTIME_ENTRY, 'utf8'))
62
+ writeGenerated(join(directory, 'reference-media.gen.jsx'), readFileSync(RUNTIME_REFERENCE, 'utf8'))
63
+ const entryPoint = join(directory, 'index.jsx')
64
+ const entrySource = `import {registerMotionRoot} from './entry.gen.jsx';\n${imports.join('\n')}\nregisterMotionRoot({${entries.join(',')}});\n`
65
+ // 入口本身就是生成的:内容变了(换插件路径、增删镜头)就重写,不看 preserveSources。
66
+ if(!existsSync(entryPoint) || readFileSync(entryPoint, 'utf8') !== entrySource)writeFileSync(entryPoint, entrySource)
67
+ if(!options.preserveSources)writeFileSync(join(directory, 'composition.json'), JSON.stringify(composition, null, 2))
68
+ if (composition.production && !options.preserveSources) {
69
+ writeFileSync(join(directory, 'design.json'), JSON.stringify(composition.production, null, 2))
70
+ writeFileSync(join(directory, 'storyboard.json'), JSON.stringify(composition.shots.map(({ sceneSource: _source, sceneComponents, ...shot }) => ({ ...shot,
71
+ ...(sceneComponents ? { components: sceneComponents.map(({ source: _code, ...component }) => component) } : {}) })), null, 2))
72
+ }
73
+ const alias = Object.fromEntries(Object.entries(shotcraftModuleMap()).map(([name, url]) => [name + '$', fileURLToPath(url)]))
74
+ // Demos use paths relative to the upstream repository. Generated components
75
+ // live elsewhere: resolve known library paths without rewriting their source.
76
+ const library = shotcraftModuleMap()
77
+ const libraryImports = { apply(compiler) {
78
+ compiler.hooks.normalModuleFactory.tap('DrawAiShotcraftImports', (factory) => {
79
+ factory.hooks.beforeResolve.tap('DrawAiShotcraftImports', (request) => {
80
+ if (!request?.request?.startsWith('.')) return
81
+ const local = relative(resolve(directory), resolve(request.context))
82
+ if (isAbsolute(local) || local === '..' || /^\.\.[\\/]/.test(local)) return
83
+ const stem = resolve(request.context, request.request)
84
+ if (['', '.tsx', '.ts', '.jsx', '.js', '/index.tsx', '/index.ts', '/index.js'].some((ext) => existsSync(stem + ext))) return
85
+ const path = request.request.replaceAll('\\', '/').replace(/^(?:\.\.?\/)+/, '').replace(/\.(tsx?|jsx?)$/, '')
86
+ const name = path.startsWith('demos/') ? '@shotcraft/' + path
87
+ : path.startsWith('_fixtures/') ? '@shotcraft/demos/' + path
88
+ : path.startsWith('assets/lib/') ? '@shotcraft/lib/' + path.slice('assets/lib/'.length)
89
+ : path.startsWith('lib/') ? '@shotcraft/' + path : ''
90
+ if (Object.hasOwn(library, name)) request.request = fileURLToPath(library[name])
91
+ })
92
+ })
93
+ } }
94
+ alias['@drawai/motion$'] = join(directory, 'reference-media.gen.jsx')
95
+ // Resolve the runtime lazily: creating a project only writes its files, and
96
+ // the video runtime may not be initialized yet (it is installed on first use).
97
+ const runtimeModules = () => {
98
+ const require = videoRuntimeRequire(options.runtimeDirectory)
99
+ return [...new Set(['react', 'remotion', '@remotion/motion-blur'].map((name) => dirname(require.resolve(name + '/package.json')).replace(/[\\/]@remotion[\\/][^\\/]+$/, '').replace(/[\\/](?:react|remotion)$/, '')))]
100
+ }
101
+ return { entryPoint, webpackOverride: (config) => {
102
+ const modules = runtimeModules()
103
+ return { ...config, plugins: [...(config.plugins || []), libraryImports], resolve: { ...config.resolve,
104
+ alias: { ...config.resolve?.alias, ...alias }, modules: [...modules, ...(config.resolve?.modules || ['node_modules'])] } }
105
+ } }
106
+ }
@@ -1,175 +1,175 @@
1
1
  /* 由 tools/build.mjs 生成 —— 请勿直接编辑;改 src/ 下的源文件。 */
2
- import { ensureVideoRuntime, videoRuntimeRequire } from './motion/video-runtime.js'
3
- import { existsSync, rmSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs'
4
- import { dirname, join, relative, isAbsolute, resolve } from 'node:path'
5
- import { createHash } from 'node:crypto'
6
- import { prepareMotionProject } from './motion-project.js'
7
- import { runFfmpeg } from './motion/ffmpeg.js'
8
-
9
- let cancelled = false, running = false
10
- const runtimeAbort = new AbortController()
11
- let cancel = () => {}
12
- const send = (message) => { if (process.connected) process.send(message) }
13
- process.on('message', async (request) => {
14
- if (request.type === 'cancel') { cancelled = true; runtimeAbort.abort(new Error('已取消动效生成')); cancel(); return }
15
- if (running) return
16
- running = true
17
- let browser
18
- const outDir = request.bundleDir || join(dirname(request.output), 'bundle')
19
- const cacheFile = join(outDir, '.drawai-bundle-key')
20
- try {
21
- const runtimeDirectory = await ensureVideoRuntime({signal:runtimeAbort.signal, onProgress:message => send({type:'progress', ...message, progress:0})})
22
- if (cancelled) throw new Error('已取消动效生成')
23
- const require = videoRuntimeRequire(runtimeDirectory)
24
- const { bundle } = require('@remotion/bundler')
25
- const { openBrowser, selectComposition, renderMedia, renderStill, makeCancelSignal } = require('@remotion/renderer')
26
- const cancellation = makeCancelSignal()
27
- const cancelSignal = cancellation.cancelSignal
28
- cancel = cancellation.cancel
29
- // 同一个工程连续预览/渲染时复用上次的 bundle:预览从 30 秒降到 3 秒,
30
- // 改一个场景文件只会让 key 变化一次。request.bundleKey 由调用方按源码计算。
31
- const reuse = request.reuseBundle === true && request.bundleKey && existsSync(join(outDir, 'index.html'))
32
- && existsSync(cacheFile) && readFileSync(cacheFile, 'utf8').trim() === request.bundleKey
33
- let serveUrl
34
- if (reuse) {
35
- serveUrl = outDir
36
- send({ type: 'progress', phase: 'bundling', progress: .1 })
37
- } else {
38
- const project = prepareMotionProject(request.composition, request.projectDirectory || join(dirname(request.output), 'project'), {preserveSources:request.preserveSources===true, runtimeDirectory})
39
- serveUrl = await bundle({ ...project, outDir, ignoreRegisterRootWarning: true,
40
- publicDir: request.publicDir, onProgress: (progress) => send({ type: 'progress', phase: 'bundling', progress: progress / 100 * .1 }) })
41
- const index = join(outDir, 'index.html')
42
- writeFileSync(index, readFileSync(index, 'utf8').replace('<head>', '<head><meta http-equiv="Content-Security-Policy" content="default-src \'self\' blob: data:; script-src \'self\' \'unsafe-inline\' \'unsafe-eval\'; style-src \'self\' \'unsafe-inline\'; connect-src \'self\'; img-src \'self\' blob: data:; media-src \'self\' blob:; frame-src \'none\'; object-src \'none\'">'))
43
- if (request.bundleKey) writeFileSync(cacheFile, request.bundleKey)
44
- }
45
- if (cancelled) throw new Error('已取消动效生成')
46
- // Use a configured browser, or a local browser before downloading Chromium.
47
- // 参数名必须是 puppeteerInstance:@remotion/renderer 4.x 只认这个名字,
48
- // 写成 browserInstance 会被静默忽略,然后它自己去下载 chrome-headless-shell(内网/无外网必炸)。
49
- // 同时把 browserExecutable 一并传下去:万一实例没用上,也还有本机浏览器可退回,不去联网。
50
- const candidates = [
51
- request.browserExecutable, process.env.DRAWAI_CHROME_PATH,
52
- // Windows
53
- 'C:/Program Files/Google/Chrome/Application/chrome.exe', 'C:/Program Files (x86)/Microsoft/Edge/Application/msedge.exe',
54
- // macOS(插件装在 Mac 上时,浏览器不在 PATH 里,也不在 Windows 的默认位置)
55
- '/Applications/Google Chrome.app/Contents/MacOS/Google Chrome',
56
- '/Applications/Chromium.app/Contents/MacOS/Chromium',
57
- '/Applications/Microsoft Edge.app/Contents/MacOS/Microsoft Edge',
58
- // Linux
59
- '/usr/bin/google-chrome', '/usr/bin/chromium', '/usr/bin/chromium-browser', '/usr/bin/microsoft-edge',
60
- ]
61
- const browserExecutable = candidates.find((path) => path && existsSync(path))
62
- if (!browserExecutable) {
63
- // 没有本机浏览器时 Remotion 会去下载 chrome-headless-shell:内网/无外网会卡很久。
64
- // 先把这件事说出来,别让它看起来像"卡死"(issue #22)。
65
- send({ type: 'progress', phase: 'browser', progress: 0, message: '没找到本机 Chrome / Edge,正在下载内置浏览器;首次会慢一些' })
66
- }
67
- browser = await openBrowser('chrome', { browserExecutable, logLevel: 'error' }).catch((error) => {
68
- const detail = error?.message || String(error)
69
- throw new Error(browserExecutable
70
- ? `打开本机浏览器失败(${browserExecutable}):${detail}`
71
- : `未找到本机 Chrome / Edge,下载 chrome-headless-shell 也失败:${detail}。请安装 Chrome,或用 DRAWAI_CHROME_PATH 指定浏览器路径。`)
72
- })
73
- if (cancelled) throw new Error('已取消动效生成')
74
- const inputProps = { plan: request.composition, assets: request.assets }
75
- const composition = await selectComposition({ serveUrl, id: 'DrawAiMotion', inputProps, puppeteerInstance: browser, browserExecutable })
76
- if(request.operation==='compile'){send({type:'done',compiled:true});return}
77
- send({ type: 'progress', phase: 'reviewing', progress: .1 })
78
- const qaDirectory = request.qaDirectory || join(dirname(request.output), 'qa'); mkdirSync(qaDirectory, { recursive: true })
79
- const audioCues = Array.isArray(request.composition?.audio) ? request.composition.audio : []
80
- const qa = { status: 'render-checks-passed', visualReview: 'manual-required', audio: audioCues.length ? 'mixed:' + audioCues.length : 'silent', frames: [], warnings: [] }
81
- if(request.operation==='still'){
82
- const frames=[]
83
- for(const frame of request.frames){
84
- if(!Number.isInteger(frame)||frame<0||frame>=composition.durationInFrames)throw new Error('截图帧超出成片')
85
- const filename='agent-frame-'+frame+'.png'
86
- await renderStill({serveUrl,composition,inputProps,output:join(qaDirectory,filename),frame,puppeteerInstance:browser,browserExecutable,cancelSignal,logLevel:'error'})
87
- frames.push({frame,filename,sha256:createHash('sha256').update(readFileSync(join(qaDirectory,filename))).digest('hex')})
88
- }
89
- send({type:'done',frames});return
90
- }
91
- for (const shot of request.composition.shots) {
92
- const hashes = []
93
- for (const fraction of (shot.motionCards?.length ? [0, .25, .5, .75, .98] : [.25, .7])) {
94
- const frame = shot.startFrame + Math.min(shot.durationFrames - 1, Math.floor(shot.durationFrames * fraction))
95
- const filename = shot.id + '-' + frame + '.png'
96
- await renderStill({ serveUrl, composition, inputProps, output: join(qaDirectory, filename), frame, puppeteerInstance: browser, browserExecutable, cancelSignal, logLevel: 'error', timeoutInMilliseconds: 30000 })
97
- const bytes = readFileSync(join(qaDirectory, filename))
98
- const hash = createHash('sha256').update(bytes).digest('hex'); hashes.push(hash)
99
- qa.frames.push({ shotId: shot.id, frame, filename, fraction, sha256: hash })
100
- }
101
- if (hashes[0] === hashes[1]) qa.warnings.push(shot.id + ': 两张采样帧相同,请检查主运动或停留段')
102
- }
103
- // 渲染规格随请求进来(由 render.js 的 resolveRenderSettings 解析过):
104
- // 编码参数、色彩标签、超采样倍数、并发都在这里唯一地落到 renderMedia 上。
105
- const settings = request.render || {}
106
- qa.render = settings
107
- writeFileSync(join(qaDirectory, 'report.json'), JSON.stringify(qa, null, 2))
108
- const scale = Number(settings.scale) > 1 ? Math.round(Number(settings.scale)) : 1
109
- const finalOutput = request.output
110
- // 超采样:Remotion 的 scale 是「渲成更大」,不会替你缩回来。
111
- // 所以大图先落到临时文件,再用一次 lanczos + bt709 的母版编码缩回目标尺寸。
112
- const rawOutput = scale > 1 ? join(dirname(finalOutput), '.supersample.mp4') : finalOutput
113
- const startedAt = Date.now()
114
- let seenFrames = 0
115
- const totalFrames = request.frameRange
116
- ? request.frameRange[1] - request.frameRange[0] + 1
117
- : composition.durationInFrames
118
- await renderMedia({ serveUrl, composition, inputProps, outputLocation: rawOutput,
119
- codec: settings.codec || 'h264',
120
- ...(settings.crf ? { crf: settings.crf } : {}),
121
- ...(settings.preset ? { x264Preset: settings.preset } : {}),
122
- ...(settings.pixelFormat ? { pixelFormat: settings.pixelFormat } : {}),
123
- ...(settings.colorSpace ? { colorSpace: settings.colorSpace } : {}),
124
- ...(settings.audioBitrate ? { audioBitrate: settings.audioBitrate } : {}),
125
- scale,
126
- concurrency: settings.concurrency || 2,
127
- ...(request.frameRange?{frameRange:request.frameRange}:{}),
128
- puppeteerInstance: browser, browserExecutable, cancelSignal, logLevel: 'error',
129
- onProgress: ({ progress, renderedFrames }) => {
130
- const done = Number(renderedFrames) || 0
131
- if (done > seenFrames) seenFrames = done
132
- const elapsed = (Date.now() - startedAt) / 1000
133
- const perFrame = seenFrames > 0 ? elapsed / seenFrames : null
134
- // 单帧耗时是「样本数该开多大」的唯一依据:没有它,超采样只能靠猜。
135
- send({ type: 'progress', phase: 'rendering', progress: .1 + progress * .85,
136
- frames: { done: seenFrames, total: totalFrames,
137
- perFrameSeconds: perFrame ? Math.round(perFrame * 1000) / 1000 : null,
138
- etaSeconds: perFrame ? Math.round(perFrame * Math.max(totalFrames - seenFrames, 0)) : null } })
139
- } })
140
- if (scale > 1) {
141
- send({ type: 'progress', phase: 'encoding', progress: .96 })
142
- // 这一趟本来就是重编码,顺手把 bt709 三标签补全。
143
- // 注意:这个 ffmpeg 构建里 -color_primaries / -color_trc 当**输出选项**不生效
144
- // (连续实测:只剩 colorspace 标签,primaries/trc 仍是 unknown),必须走 setparams 滤镜改帧元数据。
145
- const colorFilter = settings.colorSpace === 'bt709'
146
- ? ',setparams=color_primaries=bt709:color_trc=bt709:colorspace=bt709' : ''
147
- await runFfmpeg(['-i', rawOutput,
148
- '-vf', 'scale=' + composition.width + ':' + composition.height + ':flags=lanczos+accurate_rnd+full_chroma_int' + colorFilter,
149
- '-c:v', 'libx264', '-crf', String(settings.crf || 14), '-preset', String(settings.preset || 'slow'),
150
- '-profile:v', 'high', '-pix_fmt', 'yuv420p',
151
- '-colorspace', 'bt709', '-color_primaries', 'bt709', '-color_trc', 'bt709',
152
- '-c:a', 'copy', '-movflags', '+faststart', finalOutput],
153
- { cwd: dirname(finalOutput), log: join(dirname(finalOutput), 'encode.log'), timeoutMs: 900000 })
154
- rmSync(rawOutput, { force: true })
155
- qa.supersample = { from: composition.width * scale + 'x' + composition.height * scale, to: composition.width + 'x' + composition.height }
156
- qa.colorTags = settings.colorSpace === 'bt709' ? 'bt709(三标签齐全)' : '未标注'
157
- } else if (settings.colorSpace === 'bt709') {
158
- // Remotion 只写 colorspace,primaries/trc 留 unknown;要齐全只能再重编一次,
159
- // 而重编会让画质掉一代——所以 1× 下宁可把这件事说清楚,不偷偷多编一遍。
160
- qa.colorTags = '仅 colorspace=bt709(primaries/trc 为 unknown;母版级标签请设 render.scale=2)'
161
- qa.warnings.push('色彩标签只写了 colorspace:要 primaries/trc 齐全(严格 QC)请用 render.scale=2,那一趟重编码会一并补齐')
162
- }
163
- const posterShot = request.composition.shots.find((shot) => shot.referenceIds.length) || request.composition.shots[0]
164
- await renderStill({ serveUrl, composition, inputProps, output: request.poster,
165
- frame: posterShot.startFrame + Math.floor(posterShot.durationFrames / 2), puppeteerInstance: browser, browserExecutable, cancelSignal, logLevel: 'error' })
166
- writeFileSync(join(qaDirectory, 'report.json'), JSON.stringify(qa, null, 2))
167
- send({ type: 'done', qa })
168
- } catch (error) { send({ type: 'error', error: error.message || String(error) }); process.exitCode = 1 }
169
- finally {
170
- await browser?.close({ silent: true }).catch(() => {})
171
- const cleanup = relative(resolve(dirname(request.output)), resolve(outDir))
172
- if (request.keepBundle !== true && cleanup === 'bundle' && !isAbsolute(cleanup)) rmSync(outDir, { recursive: true, force: true })
173
- process.disconnect()
174
- }
175
- })
2
+ import { ensureVideoRuntime, videoRuntimeRequire } from './motion/video-runtime.js'
3
+ import { existsSync, rmSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs'
4
+ import { dirname, join, relative, isAbsolute, resolve } from 'node:path'
5
+ import { createHash } from 'node:crypto'
6
+ import { prepareMotionProject } from './motion-project.js'
7
+ import { runFfmpeg } from './motion/ffmpeg.js'
8
+
9
+ let cancelled = false, running = false
10
+ const runtimeAbort = new AbortController()
11
+ let cancel = () => {}
12
+ const send = (message) => { if (process.connected) process.send(message) }
13
+ process.on('message', async (request) => {
14
+ if (request.type === 'cancel') { cancelled = true; runtimeAbort.abort(new Error('已取消动效生成')); cancel(); return }
15
+ if (running) return
16
+ running = true
17
+ let browser
18
+ const outDir = request.bundleDir || join(dirname(request.output), 'bundle')
19
+ const cacheFile = join(outDir, '.drawai-bundle-key')
20
+ try {
21
+ const runtimeDirectory = await ensureVideoRuntime({signal:runtimeAbort.signal, onProgress:message => send({type:'progress', ...message, progress:0})})
22
+ if (cancelled) throw new Error('已取消动效生成')
23
+ const require = videoRuntimeRequire(runtimeDirectory)
24
+ const { bundle } = require('@remotion/bundler')
25
+ const { openBrowser, selectComposition, renderMedia, renderStill, makeCancelSignal } = require('@remotion/renderer')
26
+ const cancellation = makeCancelSignal()
27
+ const cancelSignal = cancellation.cancelSignal
28
+ cancel = cancellation.cancel
29
+ // 同一个工程连续预览/渲染时复用上次的 bundle:预览从 30 秒降到 3 秒,
30
+ // 改一个场景文件只会让 key 变化一次。request.bundleKey 由调用方按源码计算。
31
+ const reuse = request.reuseBundle === true && request.bundleKey && existsSync(join(outDir, 'index.html'))
32
+ && existsSync(cacheFile) && readFileSync(cacheFile, 'utf8').trim() === request.bundleKey
33
+ let serveUrl
34
+ if (reuse) {
35
+ serveUrl = outDir
36
+ send({ type: 'progress', phase: 'bundling', progress: .1 })
37
+ } else {
38
+ const project = prepareMotionProject(request.composition, request.projectDirectory || join(dirname(request.output), 'project'), {preserveSources:request.preserveSources===true, runtimeDirectory})
39
+ serveUrl = await bundle({ ...project, outDir, ignoreRegisterRootWarning: true,
40
+ publicDir: request.publicDir, onProgress: (progress) => send({ type: 'progress', phase: 'bundling', progress: progress / 100 * .1 }) })
41
+ const index = join(outDir, 'index.html')
42
+ writeFileSync(index, readFileSync(index, 'utf8').replace('<head>', '<head><meta http-equiv="Content-Security-Policy" content="default-src \'self\' blob: data:; script-src \'self\' \'unsafe-inline\' \'unsafe-eval\'; style-src \'self\' \'unsafe-inline\'; connect-src \'self\'; img-src \'self\' blob: data:; media-src \'self\' blob:; frame-src \'none\'; object-src \'none\'">'))
43
+ if (request.bundleKey) writeFileSync(cacheFile, request.bundleKey)
44
+ }
45
+ if (cancelled) throw new Error('已取消动效生成')
46
+ // Use a configured browser, or a local browser before downloading Chromium.
47
+ // 参数名必须是 puppeteerInstance:@remotion/renderer 4.x 只认这个名字,
48
+ // 写成 browserInstance 会被静默忽略,然后它自己去下载 chrome-headless-shell(内网/无外网必炸)。
49
+ // 同时把 browserExecutable 一并传下去:万一实例没用上,也还有本机浏览器可退回,不去联网。
50
+ const candidates = [
51
+ request.browserExecutable, process.env.DRAWAI_CHROME_PATH,
52
+ // Windows
53
+ 'C:/Program Files/Google/Chrome/Application/chrome.exe', 'C:/Program Files (x86)/Microsoft/Edge/Application/msedge.exe',
54
+ // macOS(插件装在 Mac 上时,浏览器不在 PATH 里,也不在 Windows 的默认位置)
55
+ '/Applications/Google Chrome.app/Contents/MacOS/Google Chrome',
56
+ '/Applications/Chromium.app/Contents/MacOS/Chromium',
57
+ '/Applications/Microsoft Edge.app/Contents/MacOS/Microsoft Edge',
58
+ // Linux
59
+ '/usr/bin/google-chrome', '/usr/bin/chromium', '/usr/bin/chromium-browser', '/usr/bin/microsoft-edge',
60
+ ]
61
+ const browserExecutable = candidates.find((path) => path && existsSync(path))
62
+ if (!browserExecutable) {
63
+ // 没有本机浏览器时 Remotion 会去下载 chrome-headless-shell:内网/无外网会卡很久。
64
+ // 先把这件事说出来,别让它看起来像"卡死"(issue #22)。
65
+ send({ type: 'progress', phase: 'browser', progress: 0, message: '没找到本机 Chrome / Edge,正在下载内置浏览器;首次会慢一些' })
66
+ }
67
+ browser = await openBrowser('chrome', { browserExecutable, logLevel: 'error' }).catch((error) => {
68
+ const detail = error?.message || String(error)
69
+ throw new Error(browserExecutable
70
+ ? `打开本机浏览器失败(${browserExecutable}):${detail}`
71
+ : `未找到本机 Chrome / Edge,下载 chrome-headless-shell 也失败:${detail}。请安装 Chrome,或用 DRAWAI_CHROME_PATH 指定浏览器路径。`)
72
+ })
73
+ if (cancelled) throw new Error('已取消动效生成')
74
+ const inputProps = { plan: request.composition, assets: request.assets }
75
+ const composition = await selectComposition({ serveUrl, id: 'DrawAiMotion', inputProps, puppeteerInstance: browser, browserExecutable })
76
+ if(request.operation==='compile'){send({type:'done',compiled:true});return}
77
+ send({ type: 'progress', phase: 'reviewing', progress: .1 })
78
+ const qaDirectory = request.qaDirectory || join(dirname(request.output), 'qa'); mkdirSync(qaDirectory, { recursive: true })
79
+ const audioCues = Array.isArray(request.composition?.audio) ? request.composition.audio : []
80
+ const qa = { status: 'render-checks-passed', visualReview: 'manual-required', audio: audioCues.length ? 'mixed:' + audioCues.length : 'silent', frames: [], warnings: [] }
81
+ if(request.operation==='still'){
82
+ const frames=[]
83
+ for(const frame of request.frames){
84
+ if(!Number.isInteger(frame)||frame<0||frame>=composition.durationInFrames)throw new Error('截图帧超出成片')
85
+ const filename='agent-frame-'+frame+'.png'
86
+ await renderStill({serveUrl,composition,inputProps,output:join(qaDirectory,filename),frame,puppeteerInstance:browser,browserExecutable,cancelSignal,logLevel:'error'})
87
+ frames.push({frame,filename,sha256:createHash('sha256').update(readFileSync(join(qaDirectory,filename))).digest('hex')})
88
+ }
89
+ send({type:'done',frames});return
90
+ }
91
+ for (const shot of request.composition.shots) {
92
+ const hashes = []
93
+ for (const fraction of (shot.motionCards?.length ? [0, .25, .5, .75, .98] : [.25, .7])) {
94
+ const frame = shot.startFrame + Math.min(shot.durationFrames - 1, Math.floor(shot.durationFrames * fraction))
95
+ const filename = shot.id + '-' + frame + '.png'
96
+ await renderStill({ serveUrl, composition, inputProps, output: join(qaDirectory, filename), frame, puppeteerInstance: browser, browserExecutable, cancelSignal, logLevel: 'error', timeoutInMilliseconds: 30000 })
97
+ const bytes = readFileSync(join(qaDirectory, filename))
98
+ const hash = createHash('sha256').update(bytes).digest('hex'); hashes.push(hash)
99
+ qa.frames.push({ shotId: shot.id, frame, filename, fraction, sha256: hash })
100
+ }
101
+ if (hashes[0] === hashes[1]) qa.warnings.push(shot.id + ': 两张采样帧相同,请检查主运动或停留段')
102
+ }
103
+ // 渲染规格随请求进来(由 render.js 的 resolveRenderSettings 解析过):
104
+ // 编码参数、色彩标签、超采样倍数、并发都在这里唯一地落到 renderMedia 上。
105
+ const settings = request.render || {}
106
+ qa.render = settings
107
+ writeFileSync(join(qaDirectory, 'report.json'), JSON.stringify(qa, null, 2))
108
+ const scale = Number(settings.scale) > 1 ? Math.round(Number(settings.scale)) : 1
109
+ const finalOutput = request.output
110
+ // 超采样:Remotion 的 scale 是「渲成更大」,不会替你缩回来。
111
+ // 所以大图先落到临时文件,再用一次 lanczos + bt709 的母版编码缩回目标尺寸。
112
+ const rawOutput = scale > 1 ? join(dirname(finalOutput), '.supersample.mp4') : finalOutput
113
+ const startedAt = Date.now()
114
+ let seenFrames = 0
115
+ const totalFrames = request.frameRange
116
+ ? request.frameRange[1] - request.frameRange[0] + 1
117
+ : composition.durationInFrames
118
+ await renderMedia({ serveUrl, composition, inputProps, outputLocation: rawOutput,
119
+ codec: settings.codec || 'h264',
120
+ ...(settings.crf ? { crf: settings.crf } : {}),
121
+ ...(settings.preset ? { x264Preset: settings.preset } : {}),
122
+ ...(settings.pixelFormat ? { pixelFormat: settings.pixelFormat } : {}),
123
+ ...(settings.colorSpace ? { colorSpace: settings.colorSpace } : {}),
124
+ ...(settings.audioBitrate ? { audioBitrate: settings.audioBitrate } : {}),
125
+ scale,
126
+ concurrency: settings.concurrency || 2,
127
+ ...(request.frameRange?{frameRange:request.frameRange}:{}),
128
+ puppeteerInstance: browser, browserExecutable, cancelSignal, logLevel: 'error',
129
+ onProgress: ({ progress, renderedFrames }) => {
130
+ const done = Number(renderedFrames) || 0
131
+ if (done > seenFrames) seenFrames = done
132
+ const elapsed = (Date.now() - startedAt) / 1000
133
+ const perFrame = seenFrames > 0 ? elapsed / seenFrames : null
134
+ // 单帧耗时是「样本数该开多大」的唯一依据:没有它,超采样只能靠猜。
135
+ send({ type: 'progress', phase: 'rendering', progress: .1 + progress * .85,
136
+ frames: { done: seenFrames, total: totalFrames,
137
+ perFrameSeconds: perFrame ? Math.round(perFrame * 1000) / 1000 : null,
138
+ etaSeconds: perFrame ? Math.round(perFrame * Math.max(totalFrames - seenFrames, 0)) : null } })
139
+ } })
140
+ if (scale > 1) {
141
+ send({ type: 'progress', phase: 'encoding', progress: .96 })
142
+ // 这一趟本来就是重编码,顺手把 bt709 三标签补全。
143
+ // 注意:这个 ffmpeg 构建里 -color_primaries / -color_trc 当**输出选项**不生效
144
+ // (连续实测:只剩 colorspace 标签,primaries/trc 仍是 unknown),必须走 setparams 滤镜改帧元数据。
145
+ const colorFilter = settings.colorSpace === 'bt709'
146
+ ? ',setparams=color_primaries=bt709:color_trc=bt709:colorspace=bt709' : ''
147
+ await runFfmpeg(['-i', rawOutput,
148
+ '-vf', 'scale=' + composition.width + ':' + composition.height + ':flags=lanczos+accurate_rnd+full_chroma_int' + colorFilter,
149
+ '-c:v', 'libx264', '-crf', String(settings.crf || 14), '-preset', String(settings.preset || 'slow'),
150
+ '-profile:v', 'high', '-pix_fmt', 'yuv420p',
151
+ '-colorspace', 'bt709', '-color_primaries', 'bt709', '-color_trc', 'bt709',
152
+ '-c:a', 'copy', '-movflags', '+faststart', finalOutput],
153
+ { cwd: dirname(finalOutput), log: join(dirname(finalOutput), 'encode.log'), timeoutMs: 900000 })
154
+ rmSync(rawOutput, { force: true })
155
+ qa.supersample = { from: composition.width * scale + 'x' + composition.height * scale, to: composition.width + 'x' + composition.height }
156
+ qa.colorTags = settings.colorSpace === 'bt709' ? 'bt709(三标签齐全)' : '未标注'
157
+ } else if (settings.colorSpace === 'bt709') {
158
+ // Remotion 只写 colorspace,primaries/trc 留 unknown;要齐全只能再重编一次,
159
+ // 而重编会让画质掉一代——所以 1× 下宁可把这件事说清楚,不偷偷多编一遍。
160
+ qa.colorTags = '仅 colorspace=bt709(primaries/trc 为 unknown;母版级标签请设 render.scale=2)'
161
+ qa.warnings.push('色彩标签只写了 colorspace:要 primaries/trc 齐全(严格 QC)请用 render.scale=2,那一趟重编码会一并补齐')
162
+ }
163
+ const posterShot = request.composition.shots.find((shot) => shot.referenceIds.length) || request.composition.shots[0]
164
+ await renderStill({ serveUrl, composition, inputProps, output: request.poster,
165
+ frame: posterShot.startFrame + Math.floor(posterShot.durationFrames / 2), puppeteerInstance: browser, browserExecutable, cancelSignal, logLevel: 'error' })
166
+ writeFileSync(join(qaDirectory, 'report.json'), JSON.stringify(qa, null, 2))
167
+ send({ type: 'done', qa })
168
+ } catch (error) { send({ type: 'error', error: error.message || String(error) }); process.exitCode = 1 }
169
+ finally {
170
+ await browser?.close({ silent: true }).catch(() => {})
171
+ const cleanup = relative(resolve(dirname(request.output)), resolve(outDir))
172
+ if (request.keepBundle !== true && cleanup === 'bundle' && !isAbsolute(cleanup)) rmSync(outDir, { recursive: true, force: true })
173
+ process.disconnect()
174
+ }
175
+ })