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.
- package/LICENSE +4142 -0
- package/README.md +43 -10
- package/lib/audio-media.js +147 -0
- package/lib/audio-service.js +66 -0
- package/lib/canvas-format.js +134 -0
- package/lib/client.js +5283 -223
- package/lib/comfyui-default-workflow.mjs +1825 -0
- package/lib/comfyui-workflow.mjs +426 -0
- package/lib/document-commands.js +12 -0
- package/lib/document-core.js +29 -0
- package/lib/drawing-tasks.js +26 -0
- package/lib/imagegen.mjs +596 -0
- package/lib/index.js +1758 -129
- package/lib/media.js +26 -13
- package/lib/mgstyle-core.js +75 -0
- package/lib/motion/apply.js +180 -0
- package/lib/motion/asset-pack-lock.js +10 -0
- package/lib/motion/asset-pack.js +335 -0
- package/lib/motion/assets.js +290 -0
- package/lib/motion/audio.js +103 -0
- package/lib/motion/audiolevel.js +63 -0
- package/lib/motion/audiomix.js +144 -0
- package/lib/motion/beats.js +337 -0
- package/lib/motion/builtin-assets.js +62 -0
- package/lib/motion/canvas-spec.js +442 -0
- package/lib/motion/capture.js +95 -0
- package/lib/motion/cards.js +80 -0
- package/lib/motion/collect.js +447 -0
- package/lib/motion/delivery.js +112 -0
- package/lib/motion/edge-speech.py +90 -0
- package/lib/motion/entry.jsx +73 -0
- package/lib/motion/export.js +200 -0
- package/lib/motion/ffmpeg.js +42 -0
- package/lib/motion/fonts.js +71 -0
- package/lib/motion/jobs.js +113 -0
- package/lib/motion/json.js +4 -0
- package/lib/motion/media-catalog.js +59 -0
- package/lib/motion/mgstyles/BRIEF-TEMPLATE.md +63 -0
- package/lib/motion/mgstyles/briefs/01-flat-vector.md +104 -0
- package/lib/motion/mgstyles/briefs/02-line-art.md +97 -0
- package/lib/motion/mgstyles/briefs/03-isometric.md +108 -0
- package/lib/motion/mgstyles/briefs/05-cel-boil.md +145 -0
- package/lib/motion/mgstyles/briefs/06-collage.md +149 -0
- package/lib/motion/mgstyles/briefs/07-liquid.md +106 -0
- package/lib/motion/mgstyles/briefs/08-morph.md +111 -0
- package/lib/motion/mgstyles/briefs/09-bauhaus.md +150 -0
- package/lib/motion/mgstyles/briefs/10-synthwave.md +151 -0
- package/lib/motion/mgstyles/briefs/12-aurora-glass.md +147 -0
- package/lib/motion/mgstyles/briefs/18-hanazi.md +140 -0
- package/lib/motion/mgstyles/briefs/19-paperclip.md +156 -0
- package/lib/motion/mgstyles/briefs/20-pixel.md +118 -0
- package/lib/motion/mgstyles/briefs/22-hud.md +118 -0
- package/lib/motion/mgstyles/catalog.d/01-flat-vector.json +34 -0
- package/lib/motion/mgstyles/catalog.d/02-line-art.json +34 -0
- package/lib/motion/mgstyles/catalog.d/03-isometric.json +35 -0
- package/lib/motion/mgstyles/catalog.d/05-cel-boil.json +28 -0
- package/lib/motion/mgstyles/catalog.d/06-collage.json +28 -0
- package/lib/motion/mgstyles/catalog.d/07-liquid.json +28 -0
- package/lib/motion/mgstyles/catalog.d/08-morph.json +28 -0
- package/lib/motion/mgstyles/catalog.d/09-bauhaus.json +28 -0
- package/lib/motion/mgstyles/catalog.d/10-synthwave.json +28 -0
- package/lib/motion/mgstyles/catalog.d/12-aurora-glass.json +28 -0
- package/lib/motion/mgstyles/catalog.d/18-hanazi.json +28 -0
- package/lib/motion/mgstyles/catalog.d/19-paperclip.json +28 -0
- package/lib/motion/mgstyles/catalog.d/20-pixel.json +28 -0
- package/lib/motion/mgstyles/catalog.d/22-hud.json +28 -0
- package/lib/motion/mgstyles/catalog.json +1356 -0
- package/lib/motion/mgstyles/upstream/README.md +147 -0
- package/lib/motion/mgstyles/upstream/README.zh-CN.md +147 -0
- package/lib/motion/mgstyles/upstream/demos/01-flat-vector/cues.json +1 -0
- package/lib/motion/mgstyles/upstream/demos/01-flat-vector/index.html +712 -0
- package/lib/motion/mgstyles/upstream/demos/02-line-art/cues.json +331 -0
- package/lib/motion/mgstyles/upstream/demos/02-line-art/film.js +676 -0
- package/lib/motion/mgstyles/upstream/demos/02-line-art/index.html +18 -0
- package/lib/motion/mgstyles/upstream/demos/02-line-art/tools/dump_timing.mjs +26 -0
- package/lib/motion/mgstyles/upstream/demos/03-isometric/cues.json +109 -0
- package/lib/motion/mgstyles/upstream/demos/03-isometric/index.html +25 -0
- package/lib/motion/mgstyles/upstream/demos/03-isometric/js/anim.js +82 -0
- package/lib/motion/mgstyles/upstream/demos/03-isometric/js/main.js +219 -0
- package/lib/motion/mgstyles/upstream/demos/03-isometric/js/night5.js +180 -0
- package/lib/motion/mgstyles/upstream/demos/03-isometric/js/render.js +202 -0
- package/lib/motion/mgstyles/upstream/demos/03-isometric/js/title.js +167 -0
- package/lib/motion/mgstyles/upstream/demos/03-isometric/js/title5.js +294 -0
- package/lib/motion/mgstyles/upstream/demos/03-isometric/js/ui.js +141 -0
- package/lib/motion/mgstyles/upstream/demos/03-isometric/js/world.js +1399 -0
- package/lib/motion/mgstyles/upstream/demos/05-cel-boil/cues.json +157 -0
- package/lib/motion/mgstyles/upstream/demos/05-cel-boil/index.html +34 -0
- package/lib/motion/mgstyles/upstream/demos/05-cel-boil/js/engine.js +326 -0
- package/lib/motion/mgstyles/upstream/demos/05-cel-boil/js/scene.js +2036 -0
- package/lib/motion/mgstyles/upstream/demos/06-collage/assets/gen/layout.json +157 -0
- package/lib/motion/mgstyles/upstream/demos/06-collage/cues.json +80 -0
- package/lib/motion/mgstyles/upstream/demos/06-collage/index.html +889 -0
- package/lib/motion/mgstyles/upstream/demos/07-liquid/cues.json +100 -0
- package/lib/motion/mgstyles/upstream/demos/07-liquid/index.html +108 -0
- package/lib/motion/mgstyles/upstream/demos/07-liquid/js/engine.js +153 -0
- package/lib/motion/mgstyles/upstream/demos/07-liquid/js/links.js +64 -0
- package/lib/motion/mgstyles/upstream/demos/07-liquid/js/lookdev.js +46 -0
- package/lib/motion/mgstyles/upstream/demos/07-liquid/js/scene.js +963 -0
- package/lib/motion/mgstyles/upstream/demos/07-liquid/js/shaders.js +647 -0
- package/lib/motion/mgstyles/upstream/demos/07-liquid/js/textsdf.js +114 -0
- package/lib/motion/mgstyles/upstream/demos/07-liquid/js/type.js +50 -0
- package/lib/motion/mgstyles/upstream/demos/08-morph/cues.json +131 -0
- package/lib/motion/mgstyles/upstream/demos/08-morph/glyphs.json +1 -0
- package/lib/motion/mgstyles/upstream/demos/08-morph/index.html +901 -0
- package/lib/motion/mgstyles/upstream/demos/09-bauhaus/cues.json +270 -0
- package/lib/motion/mgstyles/upstream/demos/09-bauhaus/film.js +697 -0
- package/lib/motion/mgstyles/upstream/demos/09-bauhaus/index.html +12 -0
- package/lib/motion/mgstyles/upstream/demos/10-synthwave/cues.json +44 -0
- package/lib/motion/mgstyles/upstream/demos/10-synthwave/index.html +1028 -0
- package/lib/motion/mgstyles/upstream/demos/12-aurora-glass/cues.json +67 -0
- package/lib/motion/mgstyles/upstream/demos/12-aurora-glass/index.html +16 -0
- package/lib/motion/mgstyles/upstream/demos/12-aurora-glass/main.js +967 -0
- package/lib/motion/mgstyles/upstream/demos/12-aurora-glass/shaders.js +468 -0
- package/lib/motion/mgstyles/upstream/demos/18-hanazi/cues.json +117 -0
- package/lib/motion/mgstyles/upstream/demos/18-hanazi/index.html +541 -0
- package/lib/motion/mgstyles/upstream/demos/19-paperclip/assets/countries-110m.json +1 -0
- package/lib/motion/mgstyles/upstream/demos/19-paperclip/assets/stickers/meta.json +34 -0
- package/lib/motion/mgstyles/upstream/demos/19-paperclip/cues.json +285 -0
- package/lib/motion/mgstyles/upstream/demos/19-paperclip/index.html +599 -0
- package/lib/motion/mgstyles/upstream/demos/20-pixel/cues.json +42 -0
- package/lib/motion/mgstyles/upstream/demos/20-pixel/film.js +914 -0
- package/lib/motion/mgstyles/upstream/demos/20-pixel/index.html +18 -0
- package/lib/motion/mgstyles/upstream/demos/22-hud/cues.json +108 -0
- package/lib/motion/mgstyles/upstream/demos/22-hud/index.html +125 -0
- package/lib/motion/mgstyles/upstream/demos/22-hud/js/holo.js +275 -0
- package/lib/motion/mgstyles/upstream/demos/22-hud/js/hud.js +855 -0
- package/lib/motion/mgstyles/upstream/demos/22-hud/js/post.js +150 -0
- package/lib/motion/mgstyles/upstream/demos/22-hud/js/util.js +65 -0
- package/lib/motion/mgstyles/upstream/demos/22-hud/js/v5.js +142 -0
- package/lib/motion/mgstyles/upstream/harness/render.mjs +319 -0
- package/lib/motion/mgstyles/upstream/manifest.json +269 -0
- package/lib/motion/mgstyles/upstream/prompts/01-flat-vector.md +98 -0
- package/lib/motion/mgstyles/upstream/prompts/02-line-art.md +95 -0
- package/lib/motion/mgstyles/upstream/prompts/03-isometric.md +97 -0
- package/lib/motion/mgstyles/upstream/prompts/05-cel-boil.md +95 -0
- package/lib/motion/mgstyles/upstream/prompts/06-collage.md +95 -0
- package/lib/motion/mgstyles/upstream/prompts/07-liquid.md +98 -0
- package/lib/motion/mgstyles/upstream/prompts/08-morph.md +95 -0
- package/lib/motion/mgstyles/upstream/prompts/09-bauhaus.md +96 -0
- package/lib/motion/mgstyles/upstream/prompts/10-synthwave.md +95 -0
- package/lib/motion/mgstyles/upstream/prompts/12-aurora-glass.md +96 -0
- package/lib/motion/mgstyles/upstream/prompts/18-hanazi.md +95 -0
- package/lib/motion/mgstyles/upstream/prompts/19-paperclip.md +96 -0
- package/lib/motion/mgstyles/upstream/prompts/20-pixel.md +96 -0
- package/lib/motion/mgstyles/upstream/prompts/22-hud.md +95 -0
- package/lib/motion/mgstyles/upstream/rubric.md +51 -0
- package/lib/motion/mgstyles/upstream/template.md +36 -0
- package/lib/motion/pixelqa.js +198 -0
- package/lib/motion/preview.js +95 -0
- package/lib/motion/project.js +395 -0
- package/lib/motion/qa.js +290 -0
- package/lib/motion/reference-media.jsx +10 -0
- package/lib/motion/render-settings.js +112 -0
- package/lib/motion/render.js +159 -0
- package/lib/motion/scene-kit.js +183 -0
- package/lib/motion/sfx.js +232 -0
- package/lib/motion/shotcraft/ClipCard.tsx +204 -0
- package/lib/motion/shotcraft/catalog.json +2461 -0
- package/lib/motion/shotcraft/upstream/SKILL.md +286 -0
- package/lib/motion/shotcraft/upstream/assets/lib/Caption.tsx +54 -0
- package/lib/motion/shotcraft/upstream/assets/lib/ClipCard.tsx +200 -0
- package/lib/motion/shotcraft/upstream/assets/lib/DigitRoll.tsx +45 -0
- package/lib/motion/shotcraft/upstream/assets/lib/FlashCut.tsx +20 -0
- package/lib/motion/shotcraft/upstream/assets/lib/FlatPanel.tsx +65 -0
- package/lib/motion/shotcraft/upstream/assets/lib/PageCam.tsx +151 -0
- package/lib/motion/shotcraft/upstream/assets/lib/VerticalTicker.tsx +129 -0
- package/lib/motion/shotcraft/upstream/assets/lib/helpers/camera.tsx +75 -0
- package/lib/motion/shotcraft/upstream/assets/lib/helpers/motion.ts +40 -0
- package/lib/motion/shotcraft/upstream/assets/lib/helpers/rand.ts +11 -0
- package/lib/motion/shotcraft/upstream/assets/lib/helpers/shake.ts +10 -0
- package/lib/motion/shotcraft/upstream/assets/scripts/__pycache__/sfx-source.cpython-313.pyc +0 -0
- package/lib/motion/shotcraft/upstream/assets/scripts/build-readme-cover.mjs +49 -0
- package/lib/motion/shotcraft/upstream/assets/scripts/capture-template.mjs +210 -0
- package/lib/motion/shotcraft/upstream/assets/scripts/sfx-source.py +423 -0
- package/lib/motion/shotcraft/upstream/assets/scripts/smoke-render-demos.py +170 -0
- package/lib/motion/shotcraft/upstream/assets/scripts/test_sfx_source.py +157 -0
- package/lib/motion/shotcraft/upstream/demos/README.md +120 -0
- package/lib/motion/shotcraft/upstream/demos/_fixtures/Brand.tsx +112 -0
- package/lib/motion/shotcraft/upstream/demos/_fixtures/Fixtures.tsx +647 -0
- package/lib/motion/shotcraft/upstream/demos/_fixtures/Look.tsx +316 -0
- package/lib/motion/shotcraft/upstream/demos/_fixtures/Motion.tsx +82 -0
- package/lib/motion/shotcraft/upstream/demos/_fixtures/PageCam2D.tsx +115 -0
- package/lib/motion/shotcraft/upstream/demos/_fixtures/Polish.tsx +243 -0
- package/lib/motion/shotcraft/upstream/demos/_textures/live-layout.json +389 -0
- package/lib/motion/shotcraft/upstream/demos/camera/basic-3d-scene/Basic3DScene.tsx +250 -0
- package/lib/motion/shotcraft/upstream/demos/camera/crash-zoom-punch/CrashImpactReal.tsx +305 -0
- package/lib/motion/shotcraft/upstream/demos/camera/crash-zoom-punch/CrashZoomReal.tsx +246 -0
- package/lib/motion/shotcraft/upstream/demos/camera/cursor-flyover/CursorFlyover.tsx +390 -0
- package/lib/motion/shotcraft/upstream/demos/camera/depth-layer-moves/MultiplaneReal.tsx +138 -0
- package/lib/motion/shotcraft/upstream/demos/camera/graze-face-tour/GrazeFaceTour.tsx +542 -0
- package/lib/motion/shotcraft/upstream/demos/camera/overhead-camera-moves/OverheadTabletopDrop.tsx +315 -0
- package/lib/motion/shotcraft/upstream/demos/camera/overhead-camera-moves/TiltReveal.tsx +299 -0
- package/lib/motion/shotcraft/upstream/demos/camera/space-camera-moves/DroneDiveLanding.tsx +173 -0
- package/lib/motion/shotcraft/upstream/demos/camera/space-camera-moves/ExplodedView.tsx +348 -0
- package/lib/motion/shotcraft/upstream/demos/camera/steep-tilt-glide/SteepTiltGlide.tsx +366 -0
- package/lib/motion/shotcraft/upstream/demos/camera/tension-camera-moves/BulletTimeFreezeOrbit.tsx +279 -0
- package/lib/motion/shotcraft/upstream/demos/camera/tension-camera-moves/PullBackIsolation.tsx +184 -0
- package/lib/motion/shotcraft/upstream/demos/camera/terminal-3d/Terminal3D.tsx +390 -0
- package/lib/motion/shotcraft/upstream/demos/data/before-after-slider-scrub/BeforeAfterSliderScrub.tsx +155 -0
- package/lib/motion/shotcraft/upstream/demos/data/chart-live-moves/AxisRescaleShockV2.tsx +303 -0
- package/lib/motion/shotcraft/upstream/demos/data/chart-live-moves/OscilloscopeStreamV2.tsx +290 -0
- package/lib/motion/shotcraft/upstream/demos/data/chart-live-moves/UnitDotSwarmRegroupV2.tsx +278 -0
- package/lib/motion/shotcraft/upstream/demos/data/gauge-readout-moves/NeedleSweepSelftest.tsx +307 -0
- package/lib/motion/shotcraft/upstream/demos/data/gauge-readout-moves/TapeScrollFixedPointer.tsx +215 -0
- package/lib/motion/shotcraft/upstream/demos/data/scroll-brake-moves/BrakeReticleLock.tsx +299 -0
- package/lib/motion/shotcraft/upstream/demos/data/scroll-brake-moves/ChangelogScrollBrake.tsx +232 -0
- package/lib/motion/shotcraft/upstream/demos/data/timeline-travel/TimelineTravel.tsx +290 -0
- package/lib/motion/shotcraft/upstream/demos/effects/assemble-then-type-flyin/AssembleThenTypeFlyin.tsx +273 -0
- package/lib/motion/shotcraft/upstream/demos/effects/aurora-bloom-bg-flip/AuroraBloomBgFlip.tsx +167 -0
- package/lib/motion/shotcraft/upstream/demos/effects/brand-frame-snap/BrandFrameSnap.tsx +536 -0
- package/lib/motion/shotcraft/upstream/demos/effects/fui-hud-moves/ReticleLockOn.tsx +164 -0
- package/lib/motion/shotcraft/upstream/demos/effects/glow-flyline-moves/FlylineArc.tsx +175 -0
- package/lib/motion/shotcraft/upstream/demos/effects/glow-flyline-moves/GlowOrbAmbient.tsx +197 -0
- package/lib/motion/shotcraft/upstream/demos/effects/glow-flyline-moves/OrbFlylineRelay.tsx +241 -0
- package/lib/motion/shotcraft/upstream/demos/effects/icon-performance-moves/AttentionBounce.tsx +279 -0
- package/lib/motion/shotcraft/upstream/demos/effects/impact-feedback/AnimeImpact.tsx +230 -0
- package/lib/motion/shotcraft/upstream/demos/effects/impact-feedback/HitCounter.tsx +345 -0
- package/lib/motion/shotcraft/upstream/demos/effects/light-play-moves/SheenSweepRetry.tsx +275 -0
- package/lib/motion/shotcraft/upstream/demos/effects/light-play-moves/SpotlightSweepReveal.tsx +229 -0
- package/lib/motion/shotcraft/upstream/demos/effects/radial-ripple-phone-chips/RadialRipplePhoneChips.tsx +312 -0
- package/lib/motion/shotcraft/upstream/demos/effects/riso-print-hits/RisoMisregistrationHit.tsx +222 -0
- package/lib/motion/shotcraft/upstream/demos/effects/scan-bracket-sweep/ScanBracketSweep.tsx +295 -0
- package/lib/motion/shotcraft/upstream/demos/effects/scanline-annotate-focus/ScanlineAnnotateFocus.tsx +329 -0
- package/lib/motion/shotcraft/upstream/demos/effects/scanline-assemble-flyin/ScanlineAssembleFlyin.tsx +315 -0
- package/lib/motion/shotcraft/upstream/demos/effects/slam-entrance-moves/ImpactBurstKit.tsx +264 -0
- package/lib/motion/shotcraft/upstream/demos/effects/slam-entrance-moves/KanadaPerspectiveSnap.tsx +260 -0
- package/lib/motion/shotcraft/upstream/demos/effects/slam-entrance-moves/ScoreSlam.tsx +263 -0
- package/lib/motion/shotcraft/upstream/demos/effects/spotlight-sweep-moves/CornerSpotlightReveal.tsx +123 -0
- package/lib/motion/shotcraft/upstream/demos/effects/spotlight-sweep-moves/GlowWakeSleepPanel.tsx +187 -0
- package/lib/motion/shotcraft/upstream/demos/effects/spotlight-sweep-moves/SlideSpotlightPan.tsx +175 -0
- package/lib/motion/shotcraft/upstream/demos/interaction/autolayout-gap-dial/AutolayoutGapDial.tsx +305 -0
- package/lib/motion/shotcraft/upstream/demos/interaction/canvas-materialize-moves/DiagramCascadeBuild.tsx +350 -0
- package/lib/motion/shotcraft/upstream/demos/interaction/canvas-materialize-moves/PanelToCanvasMaterialize.tsx +352 -0
- package/lib/motion/shotcraft/upstream/demos/interaction/chip-grid-single-select-blackout/ChipGridSingleSelectBlackout.tsx +218 -0
- package/lib/motion/shotcraft/upstream/demos/interaction/collab-cursor-moves/CursorCastEnsemble.tsx +297 -0
- package/lib/motion/shotcraft/upstream/demos/interaction/glass-pill-dictation-typing/GlassPillDictationTyping.tsx +157 -0
- package/lib/motion/shotcraft/upstream/demos/interaction/hashtag-to-pill-materialize/HashtagToPillMaterialize.tsx +218 -0
- package/lib/motion/shotcraft/upstream/demos/interaction/picker-carousel-feature-cycle/PickerCarouselFeatureCycle.tsx +202 -0
- package/lib/motion/shotcraft/upstream/demos/interaction/segmented-thumb-hero/SegmentedThumbHero.tsx +265 -0
- package/lib/motion/shotcraft/upstream/demos/interaction/theme-switch-moves/PaletteThemeRipple.tsx +457 -0
- package/lib/motion/shotcraft/upstream/demos/interaction/theme-switch-moves/ThemeSweepToggle.tsx +446 -0
- package/lib/motion/shotcraft/upstream/demos/interaction/type-and-filter/TypeAndFilter.tsx +249 -0
- package/lib/motion/shotcraft/upstream/demos/interaction/voice-waveform-live/VoiceWaveformLive.tsx +278 -0
- package/lib/motion/shotcraft/upstream/demos/opening/crane-rise-reveal/CraneRiseReveal.tsx +332 -0
- package/lib/motion/shotcraft/upstream/demos/opening/dataviz-landscape-open/DatavizLandscapeOpen.tsx +358 -0
- package/lib/motion/shotcraft/upstream/demos/opening/fracture/Fracture.tsx +201 -0
- package/lib/motion/shotcraft/upstream/demos/opening/icon-field-colorize/IconFieldColorize.tsx +200 -0
- package/lib/motion/shotcraft/upstream/demos/opening/letterspace-materialize/LetterspaceMaterialize.tsx +208 -0
- package/lib/motion/shotcraft/upstream/demos/opening/magician-card-flourish/MagicianCardFlourish.tsx +341 -0
- package/lib/motion/shotcraft/upstream/demos/opening/orbit-ring-title-open/OrbitRingTitleOpen.tsx +389 -0
- package/lib/motion/shotcraft/upstream/demos/opening/spotlight-hero-card/SpotlightHeroCard.tsx +293 -0
- package/lib/motion/shotcraft/upstream/demos/opening/stroke-segment-build/StrokeSegmentBuild.tsx +183 -0
- package/lib/motion/shotcraft/upstream/demos/opening/text-as-mask/TextAsMask.tsx +245 -0
- package/lib/motion/shotcraft/upstream/demos/outro/grain-dissolve/GrainDissolve.tsx +222 -0
- package/lib/motion/shotcraft/upstream/demos/outro/logo-shrink-wordmark-lockup/LogoShrinkWordmarkLockup.tsx +223 -0
- package/lib/motion/shotcraft/upstream/demos/outro/neon-triple-marquee/NeonTripleMarquee.tsx +217 -0
- package/lib/motion/shotcraft/upstream/demos/outro/outro-group-photo-launch/OutroGroupPhotoLaunch.tsx +343 -0
- package/lib/motion/shotcraft/upstream/demos/outro/ui-strip-away-outro/UiStripAwayOutro.tsx +357 -0
- package/lib/motion/shotcraft/upstream/demos/outro/ui-to-brand-morph/IconFlipBloomLogo.tsx +187 -0
- package/lib/motion/shotcraft/upstream/demos/outro/ui-to-brand-morph/InputMorphsIntoLogo.tsx +333 -0
- package/lib/motion/shotcraft/upstream/demos/rhythm/beat-cut-moves/PaparazziFlash.tsx +274 -0
- package/lib/motion/shotcraft/upstream/demos/rhythm/beat-step-list-theme-cycle/BeatStepListThemeCycle.tsx +270 -0
- package/lib/motion/shotcraft/upstream/demos/rhythm/panel-grid-moves/ComicPanelSplit.tsx +430 -0
- package/lib/motion/shotcraft/upstream/demos/rhythm/quad-split-parallel-scenes/QuadSplitParallelScenes.tsx +252 -0
- package/lib/motion/shotcraft/upstream/demos/rhythm/rhythm-interrupt-moves/JumpCutPunchIn.tsx +106 -0
- package/lib/motion/shotcraft/upstream/demos/rhythm/sakuga-timing-shift/SakugaTimingShift.tsx +288 -0
- package/lib/motion/shotcraft/upstream/demos/rhythm/speed-ramp-freeze/FreezeAnnotateReal.tsx +362 -0
- package/lib/motion/shotcraft/upstream/demos/rhythm/trailer-grammar-moves/TrailerBumper.tsx +168 -0
- package/lib/motion/shotcraft/upstream/demos/transition/bottom-push-stack-wipe/BottomPushStackWipe.tsx +310 -0
- package/lib/motion/shotcraft/upstream/demos/transition/card-flip-reveal/CardFlipReveal.tsx +261 -0
- package/lib/motion/shotcraft/upstream/demos/transition/card-flock-tumble/CardFlockTumble.tsx +459 -0
- package/lib/motion/shotcraft/upstream/demos/transition/circle-match-iris/CircleMatchIris.tsx +294 -0
- package/lib/motion/shotcraft/upstream/demos/transition/gradient-transition/GradientTransition.tsx +161 -0
- package/lib/motion/shotcraft/upstream/demos/transition/line-carry-transition/LineCarryTransition.tsx +248 -0
- package/lib/motion/shotcraft/upstream/demos/transition/mosaic-reframe/MosaicReframe.tsx +359 -0
- package/lib/motion/shotcraft/upstream/demos/transition/page-turn-transitions/BarnDoorSplit.tsx +227 -0
- package/lib/motion/shotcraft/upstream/demos/transition/print-texture-transitions/InkBleedReveal.tsx +162 -0
- package/lib/motion/shotcraft/upstream/demos/transition/shot-transitions/PortalWipeV2.tsx +248 -0
- package/lib/motion/shotcraft/upstream/demos/transition/shot-transitions/WhipBrakeReal.tsx +186 -0
- package/lib/motion/shotcraft/upstream/demos/transition/tear-streak-transitions/GlitchDisplace.tsx +355 -0
- package/lib/motion/shotcraft/upstream/demos/transition/transition-hidden-cut/InvisibleCut.tsx +272 -0
- package/lib/motion/shotcraft/upstream/demos/transition/transition-hidden-cut/LightLeakBurn.tsx +262 -0
- package/lib/motion/shotcraft/upstream/demos/transition/transition-hidden-cut/VersusSlam.tsx +217 -0
- package/lib/motion/shotcraft/upstream/demos/transition/transition-travel/LetterformZoom.tsx +285 -0
- package/lib/motion/shotcraft/upstream/demos/transition/white-flash-logo-simplify-cut/WhiteFlashLogoSimplifyCut.tsx +205 -0
- package/lib/motion/shotcraft/upstream/demos/transition/wipe-transitions/BlindsSlice.tsx +178 -0
- package/lib/motion/shotcraft/upstream/demos/typography/blur-slide/BlurSlide.tsx +147 -0
- package/lib/motion/shotcraft/upstream/demos/typography/brace-expand/BraceExpand.tsx +143 -0
- package/lib/motion/shotcraft/upstream/demos/typography/brand-ink-open/BrandInkOpen.tsx +134 -0
- package/lib/motion/shotcraft/upstream/demos/typography/countdown-arc-scatter/CountdownArcScatter.tsx +217 -0
- package/lib/motion/shotcraft/upstream/demos/typography/flying-words/FlyingWords.tsx +198 -0
- package/lib/motion/shotcraft/upstream/demos/typography/glitch-cycle/GlitchCycle.tsx +221 -0
- package/lib/motion/shotcraft/upstream/demos/typography/gradient-word-sweep/GradientWordSweep.tsx +312 -0
- package/lib/motion/shotcraft/upstream/demos/typography/lead-word-zoom-assemble/LeadWordZoomAssemble.tsx +261 -0
- package/lib/motion/shotcraft/upstream/demos/typography/marker-underline-title/MarkerUnderlineTitle.tsx +121 -0
- package/lib/motion/shotcraft/upstream/demos/typography/outline-word-fill/OutlineWordFill.tsx +153 -0
- package/lib/motion/shotcraft/upstream/demos/typography/paper-title-card/PaperTitleCard.tsx +179 -0
- package/lib/motion/shotcraft/upstream/demos/typography/pill-chip-slot-cycle-handled/PillChipSlotCycleHandled.tsx +140 -0
- package/lib/motion/shotcraft/upstream/demos/typography/pill-slot-cycle/PillSlotCycle.tsx +309 -0
- package/lib/motion/shotcraft/upstream/demos/typography/text-column-converge/TextColumnConverge.tsx +191 -0
- package/lib/motion/shotcraft/upstream/demos/typography/title-demote-to-label/TitleDemoteToLabel.tsx +292 -0
- package/lib/motion/shotcraft/upstream/demos/typography/type-assembly-moves/LetterformDriftAssembly.tsx +135 -0
- package/lib/motion/shotcraft/upstream/demos/typography/type-assembly-moves/SplitTextStagger.tsx +124 -0
- package/lib/motion/shotcraft/upstream/demos/typography/type-assembly-moves/TextOnPath.tsx +255 -0
- package/lib/motion/shotcraft/upstream/demos/typography/type-entrance-moves/LetterDropPhysics.tsx +173 -0
- package/lib/motion/shotcraft/upstream/demos/typography/type-rhythm-sync/FontWeightPump.tsx +225 -0
- package/lib/motion/shotcraft/upstream/demos/typography/type-rhythm-sync/KaraokeFillSync.tsx +141 -0
- package/lib/motion/shotcraft/upstream/demos/typography/typewriter-moves/TypewriterErrorRetype.tsx +137 -0
- package/lib/motion/shotcraft/upstream/demos/typography/vertical-word-roll-blur-cycle/VerticalWordRollBlurCycle.tsx +147 -0
- package/lib/motion/shotcraft/upstream/demos/typography/word-relay-filmstrip/WordRelayFilmstrip.tsx +409 -0
- package/lib/motion/shotcraft/upstream/demos/typography/word-relay-geometry/WordRelayGeometry.tsx +280 -0
- package/lib/motion/shotcraft/upstream/demos/ui-entrance/avatar-bracket-carousel/AvatarBracketCarousel.tsx +271 -0
- package/lib/motion/shotcraft/upstream/demos/ui-entrance/bezier-source-converge-merge/BezierSourceConvergeMerge.tsx +385 -0
- package/lib/motion/shotcraft/upstream/demos/ui-entrance/card-stack/CardStack.tsx +312 -0
- package/lib/motion/shotcraft/upstream/demos/ui-entrance/carousel-3d/Carousel3D.tsx +303 -0
- package/lib/motion/shotcraft/upstream/demos/ui-entrance/clipcard-looping/ClipCardLooping.tsx +77 -0
- package/lib/motion/shotcraft/upstream/demos/ui-entrance/cloner-depth-echo/ClonerDepthEcho.tsx +311 -0
- package/lib/motion/shotcraft/upstream/demos/ui-entrance/deck-deal-flyin/DeckDealFlyin.tsx +307 -0
- package/lib/motion/shotcraft/upstream/demos/ui-entrance/doc-park-left-pill-deal/DocParkLeftPillDeal.tsx +349 -0
- package/lib/motion/shotcraft/upstream/demos/ui-entrance/document-typewriter-reveal/DocumentTypewriterReveal.tsx +255 -0
- package/lib/motion/shotcraft/upstream/demos/ui-entrance/draw-svg-trace/DrawSvgTrace.tsx +244 -0
- package/lib/motion/shotcraft/upstream/demos/ui-entrance/element-body-moves/ContactShadowLift.tsx +217 -0
- package/lib/motion/shotcraft/upstream/demos/ui-entrance/integration-hub-map/IntegrationHubMap.tsx +652 -0
- package/lib/motion/shotcraft/upstream/demos/ui-entrance/list-reveal/ListReveal.tsx +153 -0
- package/lib/motion/shotcraft/upstream/demos/ui-entrance/list-stack-press/ListStackPress.tsx +238 -0
- package/lib/motion/shotcraft/upstream/demos/ui-entrance/neon-frame-forerun/NeonFrameForerun.tsx +324 -0
- package/lib/motion/shotcraft/upstream/demos/ui-entrance/neon-frame-orbit-drop/NeonFrameForerunOrbit.tsx +354 -0
- package/lib/motion/shotcraft/upstream/demos/ui-entrance/page-waterfall-wall/PageWaterfallWall.tsx +101 -0
- package/lib/motion/shotcraft/upstream/demos/ui-entrance/page-waterfall-wall/VerticalTicker.tsx +129 -0
- package/lib/motion/shotcraft/upstream/demos/ui-entrance/product-card-progressive-assemble/ProductCardProgressiveAssemble.tsx +297 -0
- package/lib/motion/shotcraft/upstream/demos/ui-entrance/radial-wave/RadialWave.tsx +57 -0
- package/lib/motion/shotcraft/upstream/demos/ui-entrance/research-card-stack-scroll/ResearchCardStackScroll.tsx +155 -0
- package/lib/motion/shotcraft/upstream/demos/ui-entrance/row-embed/RowEmbed.tsx +175 -0
- package/lib/motion/shotcraft/upstream/demos/ui-entrance/value-stagger-gradient/ValueStaggerGradient.tsx +153 -0
- package/lib/motion/shotcraft/upstream/demos/ui-entrance/wall-reveal-moves/BentoLightUp.tsx +146 -0
- package/lib/motion/shotcraft/upstream/demos/ui-entrance/wall-reveal-moves/WireframeDrawOn.tsx +226 -0
- package/lib/motion/shotcraft/upstream/gallery/api/library.json +1 -0
- package/lib/motion/shotcraft/upstream/manifest.json +2715 -0
- package/lib/motion/shotcraft/upstream/references/aesthetic-rules.md +193 -0
- package/lib/motion/shotcraft/upstream/references/final-review.md +121 -0
- package/lib/motion/shotcraft/upstream/references/guided-free-creation.md +199 -0
- package/lib/motion/shotcraft/upstream/references/jianying-export.md +183 -0
- package/lib/motion/shotcraft/upstream/references/music-beat-sync.md +257 -0
- package/lib/motion/shotcraft/upstream/references/pipeline.md +382 -0
- package/lib/motion/shotcraft/upstream/references/publish-upload-post.md +111 -0
- package/lib/motion/shotcraft/upstream/references/sequences/promo-energy-arc.md +50 -0
- package/lib/motion/shotcraft/upstream/references/sfx-sourcing.md +95 -0
- package/lib/motion/shotcraft/upstream/references/shots/camera/basic-3d-scene.md +41 -0
- package/lib/motion/shotcraft/upstream/references/shots/camera/crash-zoom-punch.md +52 -0
- package/lib/motion/shotcraft/upstream/references/shots/camera/cursor-flyover.md +39 -0
- package/lib/motion/shotcraft/upstream/references/shots/camera/depth-layer-moves.md +39 -0
- package/lib/motion/shotcraft/upstream/references/shots/camera/graze-face-tour.md +57 -0
- package/lib/motion/shotcraft/upstream/references/shots/camera/overhead-camera-moves.md +51 -0
- package/lib/motion/shotcraft/upstream/references/shots/camera/space-camera-moves.md +46 -0
- package/lib/motion/shotcraft/upstream/references/shots/camera/steep-tilt-glide.md +52 -0
- package/lib/motion/shotcraft/upstream/references/shots/camera/tension-camera-moves.md +43 -0
- package/lib/motion/shotcraft/upstream/references/shots/camera/terminal-3d.md +42 -0
- package/lib/motion/shotcraft/upstream/references/shots/data/before-after-slider-scrub.md +48 -0
- package/lib/motion/shotcraft/upstream/references/shots/data/chart-live-moves.md +52 -0
- package/lib/motion/shotcraft/upstream/references/shots/data/gauge-readout-moves.md +45 -0
- package/lib/motion/shotcraft/upstream/references/shots/data/scroll-brake-moves.md +46 -0
- package/lib/motion/shotcraft/upstream/references/shots/data/timeline-travel.md +44 -0
- package/lib/motion/shotcraft/upstream/references/shots/effects/assemble-then-type-flyin.md +63 -0
- package/lib/motion/shotcraft/upstream/references/shots/effects/aurora-bloom-bg-flip.md +54 -0
- package/lib/motion/shotcraft/upstream/references/shots/effects/brand-frame-snap.md +58 -0
- package/lib/motion/shotcraft/upstream/references/shots/effects/fui-hud-moves.md +35 -0
- package/lib/motion/shotcraft/upstream/references/shots/effects/glow-flyline-moves.md +53 -0
- package/lib/motion/shotcraft/upstream/references/shots/effects/icon-performance-moves.md +42 -0
- package/lib/motion/shotcraft/upstream/references/shots/effects/impact-feedback.md +50 -0
- package/lib/motion/shotcraft/upstream/references/shots/effects/light-play-moves.md +46 -0
- package/lib/motion/shotcraft/upstream/references/shots/effects/radial-ripple-phone-chips.md +56 -0
- package/lib/motion/shotcraft/upstream/references/shots/effects/riso-print-hits.md +49 -0
- package/lib/motion/shotcraft/upstream/references/shots/effects/scan-bracket-sweep.md +50 -0
- package/lib/motion/shotcraft/upstream/references/shots/effects/scanline-annotate-focus.md +53 -0
- package/lib/motion/shotcraft/upstream/references/shots/effects/scanline-assemble-flyin.md +53 -0
- package/lib/motion/shotcraft/upstream/references/shots/effects/slam-entrance-moves.md +55 -0
- package/lib/motion/shotcraft/upstream/references/shots/effects/spotlight-sweep-moves.md +52 -0
- package/lib/motion/shotcraft/upstream/references/shots/interaction/autolayout-gap-dial.md +60 -0
- package/lib/motion/shotcraft/upstream/references/shots/interaction/canvas-materialize-moves.md +57 -0
- package/lib/motion/shotcraft/upstream/references/shots/interaction/chip-grid-single-select-blackout.md +51 -0
- package/lib/motion/shotcraft/upstream/references/shots/interaction/collab-cursor-moves.md +41 -0
- package/lib/motion/shotcraft/upstream/references/shots/interaction/glass-pill-dictation-typing.md +53 -0
- package/lib/motion/shotcraft/upstream/references/shots/interaction/hashtag-to-pill-materialize.md +60 -0
- package/lib/motion/shotcraft/upstream/references/shots/interaction/picker-carousel-feature-cycle.md +55 -0
- package/lib/motion/shotcraft/upstream/references/shots/interaction/segmented-thumb-hero.md +57 -0
- package/lib/motion/shotcraft/upstream/references/shots/interaction/theme-switch-moves.md +47 -0
- package/lib/motion/shotcraft/upstream/references/shots/interaction/type-and-filter.md +37 -0
- package/lib/motion/shotcraft/upstream/references/shots/interaction/voice-waveform-live.md +54 -0
- package/lib/motion/shotcraft/upstream/references/shots/opening/brand-ink-open.md +36 -0
- package/lib/motion/shotcraft/upstream/references/shots/opening/crane-rise-reveal.md +56 -0
- package/lib/motion/shotcraft/upstream/references/shots/opening/dataviz-landscape-open.md +56 -0
- package/lib/motion/shotcraft/upstream/references/shots/opening/fracture.md +40 -0
- package/lib/motion/shotcraft/upstream/references/shots/opening/icon-field-colorize.md +60 -0
- package/lib/motion/shotcraft/upstream/references/shots/opening/letterspace-materialize.md +57 -0
- package/lib/motion/shotcraft/upstream/references/shots/opening/magician-card-flourish.md +73 -0
- package/lib/motion/shotcraft/upstream/references/shots/opening/orbit-ring-title-open.md +87 -0
- package/lib/motion/shotcraft/upstream/references/shots/opening/spotlight-hero-card.md +55 -0
- package/lib/motion/shotcraft/upstream/references/shots/opening/stroke-segment-build.md +50 -0
- package/lib/motion/shotcraft/upstream/references/shots/opening/text-as-mask.md +59 -0
- package/lib/motion/shotcraft/upstream/references/shots/outro/grain-dissolve.md +38 -0
- package/lib/motion/shotcraft/upstream/references/shots/outro/logo-shrink-wordmark-lockup.md +38 -0
- package/lib/motion/shotcraft/upstream/references/shots/outro/neon-triple-marquee.md +59 -0
- package/lib/motion/shotcraft/upstream/references/shots/outro/outro-group-photo-launch.md +39 -0
- package/lib/motion/shotcraft/upstream/references/shots/outro/ui-strip-away-outro.md +61 -0
- package/lib/motion/shotcraft/upstream/references/shots/outro/ui-to-brand-morph.md +52 -0
- package/lib/motion/shotcraft/upstream/references/shots/rhythm/beat-cut-moves.md +50 -0
- package/lib/motion/shotcraft/upstream/references/shots/rhythm/beat-step-list-theme-cycle.md +66 -0
- package/lib/motion/shotcraft/upstream/references/shots/rhythm/panel-grid-moves.md +43 -0
- package/lib/motion/shotcraft/upstream/references/shots/rhythm/quad-split-parallel-scenes.md +38 -0
- package/lib/motion/shotcraft/upstream/references/shots/rhythm/rhythm-interrupt-moves.md +38 -0
- package/lib/motion/shotcraft/upstream/references/shots/rhythm/sakuga-timing-shift.md +56 -0
- package/lib/motion/shotcraft/upstream/references/shots/rhythm/speed-ramp-freeze.md +44 -0
- package/lib/motion/shotcraft/upstream/references/shots/rhythm/trailer-grammar-moves.md +38 -0
- package/lib/motion/shotcraft/upstream/references/shots/transition/bottom-push-stack-wipe.md +63 -0
- package/lib/motion/shotcraft/upstream/references/shots/transition/card-flip-reveal.md +57 -0
- package/lib/motion/shotcraft/upstream/references/shots/transition/card-flock-tumble.md +63 -0
- package/lib/motion/shotcraft/upstream/references/shots/transition/circle-match-iris.md +58 -0
- package/lib/motion/shotcraft/upstream/references/shots/transition/gradient-transition.md +45 -0
- package/lib/motion/shotcraft/upstream/references/shots/transition/line-carry-transition.md +55 -0
- package/lib/motion/shotcraft/upstream/references/shots/transition/mosaic-reframe.md +49 -0
- package/lib/motion/shotcraft/upstream/references/shots/transition/page-turn-transitions.md +42 -0
- package/lib/motion/shotcraft/upstream/references/shots/transition/print-texture-transitions.md +42 -0
- package/lib/motion/shotcraft/upstream/references/shots/transition/shot-transitions.md +45 -0
- package/lib/motion/shotcraft/upstream/references/shots/transition/tear-streak-transitions.md +48 -0
- package/lib/motion/shotcraft/upstream/references/shots/transition/transition-hidden-cut.md +55 -0
- package/lib/motion/shotcraft/upstream/references/shots/transition/transition-travel.md +43 -0
- package/lib/motion/shotcraft/upstream/references/shots/transition/white-flash-logo-simplify-cut.md +41 -0
- package/lib/motion/shotcraft/upstream/references/shots/transition/wipe-transitions.md +42 -0
- package/lib/motion/shotcraft/upstream/references/shots/typography/blur-slide.md +59 -0
- package/lib/motion/shotcraft/upstream/references/shots/typography/brace-expand.md +55 -0
- package/lib/motion/shotcraft/upstream/references/shots/typography/countdown-arc-scatter.md +65 -0
- package/lib/motion/shotcraft/upstream/references/shots/typography/document-typewriter-reveal.md +42 -0
- package/lib/motion/shotcraft/upstream/references/shots/typography/flying-words.md +64 -0
- package/lib/motion/shotcraft/upstream/references/shots/typography/glitch-cycle.md +64 -0
- package/lib/motion/shotcraft/upstream/references/shots/typography/gradient-word-sweep.md +54 -0
- package/lib/motion/shotcraft/upstream/references/shots/typography/lead-word-zoom-assemble.md +91 -0
- package/lib/motion/shotcraft/upstream/references/shots/typography/marker-underline-title.md +51 -0
- package/lib/motion/shotcraft/upstream/references/shots/typography/outline-word-fill.md +66 -0
- package/lib/motion/shotcraft/upstream/references/shots/typography/paper-title-card.md +38 -0
- package/lib/motion/shotcraft/upstream/references/shots/typography/pill-chip-slot-cycle-handled.md +66 -0
- package/lib/motion/shotcraft/upstream/references/shots/typography/pill-slot-cycle.md +58 -0
- package/lib/motion/shotcraft/upstream/references/shots/typography/text-column-converge.md +61 -0
- package/lib/motion/shotcraft/upstream/references/shots/typography/title-demote-to-label.md +64 -0
- package/lib/motion/shotcraft/upstream/references/shots/typography/type-assembly-moves.md +45 -0
- package/lib/motion/shotcraft/upstream/references/shots/typography/type-entrance-moves.md +41 -0
- package/lib/motion/shotcraft/upstream/references/shots/typography/type-rhythm-sync.md +51 -0
- package/lib/motion/shotcraft/upstream/references/shots/typography/typewriter-moves.md +42 -0
- package/lib/motion/shotcraft/upstream/references/shots/typography/vertical-word-roll-blur-cycle.md +63 -0
- package/lib/motion/shotcraft/upstream/references/shots/typography/word-relay-filmstrip.md +57 -0
- package/lib/motion/shotcraft/upstream/references/shots/typography/word-relay-geometry.md +75 -0
- package/lib/motion/shotcraft/upstream/references/shots/ui-entrance/avatar-bracket-carousel.md +59 -0
- package/lib/motion/shotcraft/upstream/references/shots/ui-entrance/bezier-source-converge-merge.md +67 -0
- package/lib/motion/shotcraft/upstream/references/shots/ui-entrance/card-stack.md +60 -0
- package/lib/motion/shotcraft/upstream/references/shots/ui-entrance/carousel-3d.md +66 -0
- package/lib/motion/shotcraft/upstream/references/shots/ui-entrance/cloner-depth-echo.md +40 -0
- package/lib/motion/shotcraft/upstream/references/shots/ui-entrance/deck-deal-flyin.md +45 -0
- package/lib/motion/shotcraft/upstream/references/shots/ui-entrance/doc-park-left-pill-deal.md +62 -0
- package/lib/motion/shotcraft/upstream/references/shots/ui-entrance/draw-svg-trace.md +55 -0
- package/lib/motion/shotcraft/upstream/references/shots/ui-entrance/element-body-moves.md +37 -0
- package/lib/motion/shotcraft/upstream/references/shots/ui-entrance/integration-hub-map.md +55 -0
- package/lib/motion/shotcraft/upstream/references/shots/ui-entrance/list-reveal.md +50 -0
- package/lib/motion/shotcraft/upstream/references/shots/ui-entrance/list-stack-press.md +43 -0
- package/lib/motion/shotcraft/upstream/references/shots/ui-entrance/neon-frame-forerun.md +55 -0
- package/lib/motion/shotcraft/upstream/references/shots/ui-entrance/neon-frame-orbit-drop.md +51 -0
- package/lib/motion/shotcraft/upstream/references/shots/ui-entrance/page-waterfall-wall.md +51 -0
- package/lib/motion/shotcraft/upstream/references/shots/ui-entrance/product-card-progressive-assemble.md +56 -0
- package/lib/motion/shotcraft/upstream/references/shots/ui-entrance/radial-wave.md +49 -0
- package/lib/motion/shotcraft/upstream/references/shots/ui-entrance/research-card-stack-scroll.md +57 -0
- package/lib/motion/shotcraft/upstream/references/shots/ui-entrance/row-embed.md +36 -0
- package/lib/motion/shotcraft/upstream/references/shots/ui-entrance/value-stagger-gradient.md +64 -0
- package/lib/motion/shotcraft/upstream/references/shots/ui-entrance/wall-reveal-moves.md +47 -0
- package/lib/motion/shotcraft/upstream/references/sound-design.md +170 -0
- package/lib/motion/shotcraft/upstream/references/workbench.md +157 -0
- package/lib/motion/shotcraft/upstream/template/TEMPLATE.md +103 -0
- package/lib/motion/shotcraft/upstream/template/THEMES.md +44 -0
- package/lib/motion/shotcraft/upstream/template/package.json +20 -0
- package/lib/motion/shotcraft/upstream/template/remotion.config.ts +6 -0
- package/lib/motion/shotcraft/upstream/template/src/Root.tsx +17 -0
- package/lib/motion/shotcraft/upstream/template/src/aifl/Caption.tsx +65 -0
- package/lib/motion/shotcraft/upstream/template/src/aifl/DigitRoll.tsx +48 -0
- package/lib/motion/shotcraft/upstream/template/src/aifl/FlashCut.tsx +24 -0
- package/lib/motion/shotcraft/upstream/template/src/aifl/Main.tsx +192 -0
- package/lib/motion/shotcraft/upstream/template/src/aifl/PaperTitleCard.tsx +111 -0
- package/lib/motion/shotcraft/upstream/template/src/aifl/live/PageCam.tsx +150 -0
- package/lib/motion/shotcraft/upstream/template/src/aifl/live/SceneDetail.tsx +158 -0
- package/lib/motion/shotcraft/upstream/template/src/aifl/live/SceneFlyIn.tsx +468 -0
- package/lib/motion/shotcraft/upstream/template/src/aifl/live/SceneOpen.tsx +552 -0
- package/lib/motion/shotcraft/upstream/template/src/aifl/live/SceneOutroLive.tsx +411 -0
- package/lib/motion/shotcraft/upstream/template/src/aifl/live/ScenePapers.tsx +200 -0
- package/lib/motion/shotcraft/upstream/template/src/aifl/live/SceneWbr.tsx +324 -0
- package/lib/motion/shotcraft/upstream/template/src/aifl/live-layout.json +389 -0
- package/lib/motion/shotcraft/upstream/template/src/index.ts +4 -0
- package/lib/motion/shotcraft/upstream/template/src/themes/ThemedFilm.tsx +7 -0
- package/lib/motion/shotcraft/upstream/template/src/themes/manifest.tsx +68 -0
- package/lib/motion/shotcraft/upstream/template/src/themes/palette-assets.json +1 -0
- package/lib/motion/shotcraft/upstream/template/src/themes/palette-assets.ts +24 -0
- package/lib/motion/shotcraft/upstream/template/src/themes/palettes.json +74 -0
- package/lib/motion/shotcraft/upstream/template/src/themes/visual-theme.tsx +91 -0
- package/lib/motion/shotcraft/upstream/template/src/workbench.ts +213 -0
- package/lib/motion/shotcraft/upstream/template/tsconfig.json +15 -0
- package/lib/motion/spec.js +113 -0
- package/lib/motion/style-engine.js +153 -0
- package/lib/motion/style-frames.js +268 -0
- package/lib/motion/sync.js +214 -0
- package/lib/motion/theme.js +121 -0
- package/lib/motion/voice.js +417 -0
- package/lib/motion-agent-files.js +82 -0
- package/lib/motion-agent-protocol.js +66 -0
- package/lib/motion-agent-tools.js +196 -0
- package/lib/motion-agent.js +171 -0
- package/lib/motion-capture.js +92 -0
- package/lib/motion-core.js +238 -0
- package/lib/motion-film-review.js +71 -0
- package/lib/motion-library.js +210 -0
- package/lib/motion-planner.js +254 -0
- package/lib/motion-project.js +80 -0
- package/lib/motion-render-worker.js +145 -0
- package/lib/motion-session.js +32 -0
- package/lib/motion-source.js +49 -0
- package/lib/motion-tasks.js +452 -0
- package/lib/motion-vision.js +43 -0
- package/lib/mxfile.js +27 -7
- package/lib/routing/node-runtime.js +14 -0
- package/lib/routing/router.js +211 -0
- package/lib/routing/vendor/README.md +96 -0
- package/lib/routing/vendor/libavoid-routing.js +753 -0
- package/lib/routing/vendor/libavoid.min.js +59 -0
- package/lib/shotcard-core.js +39 -0
- package/lib/shotcard-reference.js +56 -0
- package/lib/style-kernel.js +37 -3
- package/lib/task-patches.js +26 -2
- package/lib/video-export.js +17 -2
- package/package.json +29 -4
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
// Small original, deterministic assets for common motion-graphics treatments.
|
|
2
|
+
// Generated locally; the project owns its copy and users can edit it freely.
|
|
3
|
+
import { createHash } from 'node:crypto'
|
|
4
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs'
|
|
5
|
+
import { join } from 'node:path'
|
|
6
|
+
import { readAssets, writeAssets } from './assets.js'
|
|
7
|
+
import { parseProjectJson } from './json.js'
|
|
8
|
+
|
|
9
|
+
const paper = `<svg xmlns="http://www.w3.org/2000/svg" width="512" height="512" viewBox="0 0 512 512"><filter id="n"><feTurbulence type="fractalNoise" baseFrequency="0.55" numOctaves="4" seed="11" stitchTiles="stitch"/><feColorMatrix type="saturate" values="0"/></filter><rect width="512" height="512" fill="#eee5d3"/><rect width="512" height="512" filter="url(#n)" opacity=".16" style="mix-blend-mode:multiply"/></svg>`
|
|
10
|
+
const grain = `<svg xmlns="http://www.w3.org/2000/svg" width="256" height="256" viewBox="0 0 256 256"><filter id="n"><feTurbulence type="fractalNoise" baseFrequency="0.9" numOctaves="3" seed="23" stitchTiles="stitch"/><feColorMatrix type="saturate" values="0"/></filter><rect width="256" height="256" filter="url(#n)"/></svg>`
|
|
11
|
+
const grid = `<svg xmlns="http://www.w3.org/2000/svg" width="128" height="128" viewBox="0 0 128 128"><path d="M0 0H128 M0 0V128 M32 0V128 M64 0V128 M96 0V128 M0 32H128 M0 64H128 M0 96H128" fill="none" stroke="#ffffff" stroke-opacity=".22" stroke-width="1"/></svg>`
|
|
12
|
+
|
|
13
|
+
// 16-bit mono PCM; deterministic oscillator/noise with short fades at both ends.
|
|
14
|
+
function wave(seconds, sample) {
|
|
15
|
+
const rate = 44100, count = Math.floor(rate * seconds), data = Buffer.alloc(44 + count * 2)
|
|
16
|
+
data.write('RIFF', 0); data.writeUInt32LE(data.length - 8, 4); data.write('WAVEfmt ', 8)
|
|
17
|
+
data.writeUInt32LE(16, 16); data.writeUInt16LE(1, 20); data.writeUInt16LE(1, 22)
|
|
18
|
+
data.writeUInt32LE(rate, 24); data.writeUInt32LE(rate * 2, 28); data.writeUInt16LE(2, 32); data.writeUInt16LE(16, 34)
|
|
19
|
+
data.write('data', 36); data.writeUInt32LE(count * 2, 40)
|
|
20
|
+
let state = 20261106, smooth = 0
|
|
21
|
+
for (let i = 0; i < count; i++) {
|
|
22
|
+
state = (Math.imul(state, 1664525) + 1013904223) >>> 0
|
|
23
|
+
const noise = state / 2147483648 - 1
|
|
24
|
+
smooth += (noise - smooth) * .15
|
|
25
|
+
const t = i / rate, envelope = Math.min(1, t / .012, (seconds - t) / .06)
|
|
26
|
+
const value = Math.max(-1, Math.min(1, sample(t, seconds, smooth) * envelope))
|
|
27
|
+
data.writeInt16LE(Math.round(value * 32767), 44 + i * 2)
|
|
28
|
+
}
|
|
29
|
+
return data
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const ASSETS = [
|
|
33
|
+
{ file: 'builtin/paper.svg', caption: '暖色纸张纹理,可平铺', kind: 'vector', data: () => Buffer.from(paper) },
|
|
34
|
+
{ file: 'builtin/grain.svg', caption: '灰度颗粒叠层,建议低不透明度混合', kind: 'vector', data: () => Buffer.from(grain) },
|
|
35
|
+
{ file: 'builtin/grid.svg', caption: '透明白色网格,可平铺', kind: 'vector', data: () => Buffer.from(grid) },
|
|
36
|
+
{ file: 'builtin/whoosh.wav', caption: '合成的短扫过声', kind: 'audio', seconds: .6, data: () => wave(.6, (t, d, n) => n * .75 * Math.sin(Math.PI * t / d)) },
|
|
37
|
+
{ file: 'builtin/impact.wav', caption: '合成的短低频落点声', kind: 'audio', seconds: .4, data: () => wave(.4, (t, d, n) => (Math.sin(2 * Math.PI * (115 * t - 70 * t * t)) * .55 + n * .18) * Math.exp(-10 * t)) },
|
|
38
|
+
]
|
|
39
|
+
|
|
40
|
+
export function writeBuiltinAssets(projectDir) {
|
|
41
|
+
const dir = join(projectDir, 'assets'), index = join(dir, 'manifest.json'), added = []
|
|
42
|
+
// Never repair a damaged user manifest by overwriting it with an empty one.
|
|
43
|
+
if (existsSync(index)) {
|
|
44
|
+
try { if (!Array.isArray(parseProjectJson(readFileSync(index, 'utf8')).items)) return added } catch { return added }
|
|
45
|
+
}
|
|
46
|
+
const manifest = readAssets(projectDir)
|
|
47
|
+
for (const item of ASSETS) {
|
|
48
|
+
const target = join(dir, item.file), data = item.data()
|
|
49
|
+
const sha256 = createHash('sha256').update(data).digest('hex')
|
|
50
|
+
mkdirSync(join(dir, 'builtin'), { recursive: true })
|
|
51
|
+
if (!existsSync(target)) writeFileSync(target, data)
|
|
52
|
+
// Do not replace or mislabel a user-edited file on a repeated create.
|
|
53
|
+
if (createHash('sha256').update(readFileSync(target)).digest('hex') !== sha256) continue
|
|
54
|
+
if (manifest.items.some((entry) => entry.file === item.file)) continue
|
|
55
|
+
manifest.items.push({ id: 'drawai-' + item.file.slice(8).replace(/\.[^.]+$/, ''), file: item.file,
|
|
56
|
+
bytes: data.length, sha256, kind: item.kind, caption: item.caption,
|
|
57
|
+
source: 'DrawAI 原创程序生成(MIT)', ...(item.seconds ? { seconds: item.seconds } : {}) })
|
|
58
|
+
added.push(item.file)
|
|
59
|
+
}
|
|
60
|
+
if (added.length) writeAssets(projectDir, manifest)
|
|
61
|
+
return added
|
|
62
|
+
}
|
|
@@ -0,0 +1,442 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 画布上的「视频规格三块」:分镜表 / 风格 / 旁白。
|
|
3
|
+
*
|
|
4
|
+
* 为什么要有这个模块:分镜表以前只活在对话里(或 storyboard.md 里),
|
|
5
|
+
* 于是模型很容易把"每一镜"写成画布上的**一个文本节点**——八镜就是八条,
|
|
6
|
+
* 排版一散,人看不出这是一张表;`motion_project create` 也无从读它。
|
|
7
|
+
*
|
|
8
|
+
* 这里的约定(与 docs/motion-video-playbook.md §4.1 对齐,实现以本文件为准):
|
|
9
|
+
*
|
|
10
|
+
* 【分镜表】 一张 Markdown 六列表:镜头 | 帧区间 | 镜头卡 | 这一镜要做什么 |
|
|
11
|
+
* 屏幕上出现什么 | 文案 / 声音。时长写成 `172f`(帧)或 `7.2s`。
|
|
12
|
+
* 【风格】 一行一个键值:page #F7F3EA / serif "Noto Serif SC" / materials: kraft
|
|
13
|
+
* 【旁白】 `## shot-N` 分节,一句一行(交给 motion_voice 合成)
|
|
14
|
+
*
|
|
15
|
+
* **一张就是一张**:同一张画布上出现第二个【分镜表】、或者一堆"一镜一个节点",
|
|
16
|
+
* 都在 validate 里报错 —— 这是这个流程最容易走偏的地方。
|
|
17
|
+
*/
|
|
18
|
+
export const SPEC_MARKERS = {
|
|
19
|
+
storyboard: '【分镜表】',
|
|
20
|
+
style: '【风格】',
|
|
21
|
+
narration: '【旁白】',
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const SEPARATOR = /^\|?\s*:?-{2,}:?\s*(\|\s*:?-{2,}:?\s*)*\|?$/
|
|
25
|
+
const TIME_PREFIX = /^\d+(?:\.\d+)?\s*(?:–|—|-|~|至)\s*\d+(?:\.\d+)?\s*s?\b/
|
|
26
|
+
|
|
27
|
+
export function specNodeOf(doc, marker) {
|
|
28
|
+
const nodes = Array.isArray(doc?.nodes) ? doc.nodes : []
|
|
29
|
+
for (const node of nodes) {
|
|
30
|
+
const label = typeof node.label === 'string' ? node.label : ''
|
|
31
|
+
if (firstLine(label) === marker) return node
|
|
32
|
+
}
|
|
33
|
+
return null
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function firstLine(label) {
|
|
37
|
+
return String(label || '').trim().split('\n')[0].trim()
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function cellText(raw) {
|
|
41
|
+
return String(raw === undefined || raw === null ? '' : raw)
|
|
42
|
+
.replace(/\*\*/g, '')
|
|
43
|
+
.replace(/`/g, '')
|
|
44
|
+
.trim()
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Cells are stored in the Markdown label so the canvas, AI and video project
|
|
48
|
+
// share one source of truth. Escape backslashes first, then pipes and newlines;
|
|
49
|
+
// a cell can therefore be edited without creating a new row or column.
|
|
50
|
+
export function escapeStoryboardCell(value) {
|
|
51
|
+
return String(value === undefined || value === null ? '' : value)
|
|
52
|
+
.replace(/\\/g, '\\\\')
|
|
53
|
+
.replace(/\|/g, '\\|')
|
|
54
|
+
.replace(/\r\n?/g, '\n')
|
|
55
|
+
.replace(/\n/g, '\\n')
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// 行尾不带 `|` 也认:手写的表经常漏最后一竖。反斜杠只解码表格约定的
|
|
59
|
+
// 三种转义,其他反斜杠保留,以兼容已有文档中的路径等普通文本。
|
|
60
|
+
function rowCells(line, escaped = false) {
|
|
61
|
+
let text = String(line || '').trim()
|
|
62
|
+
if (text.startsWith('|')) text = text.slice(1)
|
|
63
|
+
if (!escaped) {
|
|
64
|
+
if (text.endsWith('|')) text = text.slice(0, -1)
|
|
65
|
+
return text.split('|').map(cellText)
|
|
66
|
+
}
|
|
67
|
+
const cells = []
|
|
68
|
+
let cell = ''
|
|
69
|
+
let lastWasSeparator = false
|
|
70
|
+
for (let index = 0; index < text.length; index += 1) {
|
|
71
|
+
const char = text[index]
|
|
72
|
+
if (char === '\\' && index + 1 < text.length) {
|
|
73
|
+
const next = text[index + 1]
|
|
74
|
+
if (next === '\\' || next === '|' || next === 'n') {
|
|
75
|
+
cell += next === 'n' ? '\n' : next
|
|
76
|
+
index += 1
|
|
77
|
+
lastWasSeparator = false
|
|
78
|
+
continue
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
if (char === '|') {
|
|
82
|
+
cells.push(cell.trim())
|
|
83
|
+
cell = ''
|
|
84
|
+
lastWasSeparator = true
|
|
85
|
+
} else {
|
|
86
|
+
cell += char
|
|
87
|
+
if (!/\s/.test(char)) lastWasSeparator = false
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
if (!lastWasSeparator || cell.trim().length > 0) cells.push(cell.trim())
|
|
91
|
+
return cells
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* 找出三块规格节点,并顺手诊断"分镜被拆成多条"这件事。
|
|
96
|
+
* @returns {{storyboard: object|null, style: object|null, narration: object|null, split: object[], extras: object[]}}
|
|
97
|
+
*/
|
|
98
|
+
export function findSpecNodes(doc) {
|
|
99
|
+
const nodes = Array.isArray(doc?.nodes) ? doc.nodes : []
|
|
100
|
+
const out = { storyboard: null, style: null, narration: null, split: [], extras: [] }
|
|
101
|
+
for (const node of nodes) {
|
|
102
|
+
const label = typeof node.label === 'string' ? node.label : ''
|
|
103
|
+
const head = firstLine(label)
|
|
104
|
+
if (head === SPEC_MARKERS.storyboard) {
|
|
105
|
+
if (out.storyboard === null) out.storyboard = node
|
|
106
|
+
else out.extras.push(node)
|
|
107
|
+
continue
|
|
108
|
+
}
|
|
109
|
+
if (head === SPEC_MARKERS.style) {
|
|
110
|
+
if (out.style === null) out.style = node
|
|
111
|
+
else out.extras.push(node)
|
|
112
|
+
continue
|
|
113
|
+
}
|
|
114
|
+
if (head === SPEC_MARKERS.narration) {
|
|
115
|
+
if (out.narration === null) out.narration = node
|
|
116
|
+
else out.extras.push(node)
|
|
117
|
+
continue
|
|
118
|
+
}
|
|
119
|
+
// 一镜一节点的痕迹:以时间区间开头、带「|」的一行字("0.0–0.5s 静物|…")。
|
|
120
|
+
if (TIME_PREFIX.test(head) && /[||]/.test(label)) out.split.push(node)
|
|
121
|
+
}
|
|
122
|
+
return out
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/** 把 `172f` / `7.2s` / `0–35f` / `0.0–0.5s` 解析成帧。 */
|
|
126
|
+
export function parseFrames(raw, fps = 30) {
|
|
127
|
+
const text = String(raw || '').replace(/\s+/g, '').replace(/帧/g, 'f')
|
|
128
|
+
const rate = Number(fps) > 0 ? Number(fps) : 30
|
|
129
|
+
const single = text.match(/^(\d+(?:\.\d+)?)(f|s)?$/)
|
|
130
|
+
if (single) {
|
|
131
|
+
const value = Number(single[1])
|
|
132
|
+
const unit = single[2] || 'f'
|
|
133
|
+
return { startFrame: null, endFrame: null, durationFrames: Math.max(1, Math.round(unit === 's' ? value * rate : value)) }
|
|
134
|
+
}
|
|
135
|
+
const span = text.match(/^(\d+(?:\.\d+)?)(f|s)?(?:–|—|-|~|至)(\d+(?:\.\d+)?)(f|s)?$/)
|
|
136
|
+
if (!span) return null
|
|
137
|
+
const unit = span[4] || span[2] || 'f'
|
|
138
|
+
const toFrames = (value) => Math.round(unit === 's' ? Number(value) * rate : Number(value))
|
|
139
|
+
// "0.0–0.5s":区间是**端点**(0 到 0.5 秒),不是"从 0 开始持续 0.5 秒"。
|
|
140
|
+
const a = toFrames(span[1])
|
|
141
|
+
const b = toFrames(span[3])
|
|
142
|
+
return { startFrame: Math.min(a, b), endFrame: Math.max(a, b), durationFrames: Math.abs(b - a) + (unit === 's' ? 0 : 1) }
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
function shotIdOf(raw, index) {
|
|
146
|
+
const text = cellText(raw)
|
|
147
|
+
const num = text.match(/(\d+)/)
|
|
148
|
+
return num ? 'shot-' + String(Number(num[1])) : 'shot-' + (index + 1)
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* 解析【分镜表】节点里的那张 Markdown 表。
|
|
153
|
+
* @returns {{rows: object[], problems: string[]}}
|
|
154
|
+
*/
|
|
155
|
+
export function parseStoryboard(label, options = {}) {
|
|
156
|
+
const fps = Number(options.fps) > 0 ? Number(options.fps) : 30
|
|
157
|
+
const escaped = options.escaped === true
|
|
158
|
+
const lines = String(label || '').split('\n')
|
|
159
|
+
const tableLines = lines.filter((line) => line.trim().startsWith('|'))
|
|
160
|
+
const problems = []
|
|
161
|
+
const rows = []
|
|
162
|
+
if (tableLines.length === 0) {
|
|
163
|
+
problems.push('【分镜表】里没有 Markdown 表格(每行以 | 开头)')
|
|
164
|
+
return { rows, problems }
|
|
165
|
+
}
|
|
166
|
+
const body = tableLines.filter((line) => SEPARATOR.test(line.trim()) === false)
|
|
167
|
+
const header = body.shift()
|
|
168
|
+
const headerCells = rowCells(header, escaped)
|
|
169
|
+
if (headerCells.length !== 6) problems.push('表头应该是六列(镜头 | 帧区间 | 镜头卡 | 这一镜要做什么 | 屏幕上出现什么 | 文案 / 声音),现在是 ' + headerCells.length + ' 列')
|
|
170
|
+
let cursor = 0
|
|
171
|
+
for (const line of body) {
|
|
172
|
+
const cells = rowCells(line, escaped)
|
|
173
|
+
if (cells.length === 0) continue
|
|
174
|
+
if (cells.length !== 6) {
|
|
175
|
+
problems.push('第 ' + (rows.length + 1) + ' 行有 ' + cells.length + ' 列(应为 6 列):' + line.trim().slice(0, 40))
|
|
176
|
+
}
|
|
177
|
+
const shot = shotIdOf(cells[0], rows.length)
|
|
178
|
+
const frames = parseFrames(cellText(cells[1]), fps)
|
|
179
|
+
const durationFrames = frames === null ? 0 : frames.durationFrames
|
|
180
|
+
const startFrame = frames === null || frames.startFrame === null ? cursor : frames.startFrame
|
|
181
|
+
if (frames === null) problems.push(shot + ' 的帧区间读不出来("' + cells[1] + '"):写成 `0–35f` 或 `1.5s`')
|
|
182
|
+
rows.push({
|
|
183
|
+
shot,
|
|
184
|
+
startFrame,
|
|
185
|
+
durationFrames,
|
|
186
|
+
endFrame: startFrame + Math.max(0, durationFrames) - 1,
|
|
187
|
+
card: cells[2] === '—' || cells[2] === '-' ? '' : cells[2],
|
|
188
|
+
intent: cells[3] === '—' ? '' : cells[3],
|
|
189
|
+
onScreen: cells[4] === '—' ? '' : cells[4],
|
|
190
|
+
copy: cells[5] === '—' ? '' : cells[5],
|
|
191
|
+
})
|
|
192
|
+
cursor = startFrame + Math.max(1, durationFrames)
|
|
193
|
+
}
|
|
194
|
+
return { rows, problems }
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/** 【风格】节点:`page #F7F3EA`、`serif "Noto Serif SC"`、`materials: kraft, ink`。 */
|
|
198
|
+
export function parseStyle(label) {
|
|
199
|
+
const tokens = {}
|
|
200
|
+
const fonts = {}
|
|
201
|
+
const materials = []
|
|
202
|
+
const problems = []
|
|
203
|
+
for (const raw of String(label || '').split('\n')) {
|
|
204
|
+
const line = raw.trim()
|
|
205
|
+
if (line.length === 0 || line === SPEC_MARKERS.style) continue
|
|
206
|
+
const pair = line.match(/^([A-Za-z][A-Za-z0-9_-]*)\s*[:=]?\s+(.+)$/)
|
|
207
|
+
if (!pair) continue
|
|
208
|
+
const key = pair[1].toLowerCase()
|
|
209
|
+
const value = pair[2].trim().replace(/^["'「]|["'」]$/g, '')
|
|
210
|
+
if (key === 'materials') {
|
|
211
|
+
materials.push(...value.split(/[,,、]/).map((item) => item.trim()).filter(Boolean))
|
|
212
|
+
continue
|
|
213
|
+
}
|
|
214
|
+
if (key === 'serif' || key === 'sans' || key === 'mono' || key === 'font') {
|
|
215
|
+
fonts[key === 'font' ? 'sans' : key] = value
|
|
216
|
+
continue
|
|
217
|
+
}
|
|
218
|
+
// 一行里塞了多个键(`page #F7F3EA accent #C0FF00`)非常常见,而且解析起来没有歧义:
|
|
219
|
+
// 直接全收下。旧版把这种行判成"不是颜色",错误信息指向完全错误的地方。
|
|
220
|
+
const pairs = [...line.matchAll(/([A-Za-z][A-Za-z0-9_-]*)\s*[:=]?\s*(#[0-9a-f]{3,8})\b/gi)]
|
|
221
|
+
if (pairs.length > 0) {
|
|
222
|
+
for (const match of pairs) tokens[match[1].toLowerCase()] = match[2]
|
|
223
|
+
continue
|
|
224
|
+
}
|
|
225
|
+
problems.push('【风格】里 "' + line.slice(0, 40) + '" 没认出颜色或字体(写成 `page #F7F3EA` 或 `serif "Noto Serif SC"`)')
|
|
226
|
+
}
|
|
227
|
+
return { tokens, fonts, materials, problems }
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
/** 【旁白】节点:`## shot-1` 分节,每句一行。 */
|
|
231
|
+
export function parseNarration(label) {
|
|
232
|
+
const sections = []
|
|
233
|
+
const problems = []
|
|
234
|
+
let current = null
|
|
235
|
+
for (const raw of String(label || '').split('\n')) {
|
|
236
|
+
const line = raw.trim()
|
|
237
|
+
if (line.length === 0 || line === SPEC_MARKERS.narration) continue
|
|
238
|
+
const heading = line.match(/^#{1,3}\s*(shot[-\s]?\d+)\s*$/i)
|
|
239
|
+
if (heading) {
|
|
240
|
+
current = { shot: 'shot-' + String(Number(heading[1].match(/\d+/)[0])), lines: [] }
|
|
241
|
+
sections.push(current)
|
|
242
|
+
continue
|
|
243
|
+
}
|
|
244
|
+
if (current === null) {
|
|
245
|
+
problems.push('【旁白】里 "' + line.slice(0, 24) + '" 前面没有 ## shot-N 分节')
|
|
246
|
+
continue
|
|
247
|
+
}
|
|
248
|
+
current.lines.push(line)
|
|
249
|
+
}
|
|
250
|
+
return { sections, problems }
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
/**
|
|
254
|
+
* 读画布上的规格三块。`fps` 用来把 `0.5s` 换算成帧(优先用视频节点上存的值)。
|
|
255
|
+
*/
|
|
256
|
+
export function readCanvasSpec(doc, options = {}) {
|
|
257
|
+
const found = findSpecNodes(doc)
|
|
258
|
+
const fps = Number(options.fps) > 0 ? Number(options.fps) : 30
|
|
259
|
+
const storyboard = found.storyboard ? parseStoryboard(found.storyboard.label, {
|
|
260
|
+
fps, escaped: found.storyboard.data?.drawaiTableEscaped === '1',
|
|
261
|
+
}) : { rows: [], problems: [] }
|
|
262
|
+
const style = found.style ? parseStyle(found.style.label) : { tokens: {}, fonts: {}, materials: [], problems: [] }
|
|
263
|
+
const narration = found.narration ? parseNarration(found.narration.label) : { sections: [], problems: [] }
|
|
264
|
+
const problems = [
|
|
265
|
+
...(found.storyboard ? [] : ['画布上还没有【分镜表】节点:先 motion_spec{action:"scaffold"} 建一张,再往里填各镜']),
|
|
266
|
+
...storyboard.problems,
|
|
267
|
+
...style.problems,
|
|
268
|
+
...narration.problems,
|
|
269
|
+
]
|
|
270
|
+
if (found.split.length > 0) {
|
|
271
|
+
problems.push('分镜被拆成了 ' + found.split.length + ' 个节点(' + found.split.map((node) => node.id).join(', ')
|
|
272
|
+
+ '):分镜表只能有一张,用一张表把每一镜写成一行')
|
|
273
|
+
}
|
|
274
|
+
if (found.extras.length > 0) {
|
|
275
|
+
problems.push('有多余的规格节点(' + found.extras.map((node) => node.id).join(', ') + '):【分镜表】/【风格】/【旁白】各只该有一个')
|
|
276
|
+
}
|
|
277
|
+
return { storyboard: storyboard.rows, style, narration, problems, nodes: { storyboard: found.storyboard?.id ?? null, style: found.style?.id ?? null, narration: found.narration?.id ?? null } }
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
/**
|
|
281
|
+
* 校验画布规格跟视频节点对不对得上(motion_spec{action:"validate"} 的结果)。
|
|
282
|
+
* @returns {{errors: string[], warnings: string[]}}
|
|
283
|
+
*/
|
|
284
|
+
export function validateCanvasSpec(doc, options = {}) {
|
|
285
|
+
const spec = options.spec || {}
|
|
286
|
+
const fps = Number(spec.fps) > 0 ? Number(spec.fps) : 30
|
|
287
|
+
const totalFrames = Number(spec.totalFrames) > 0 ? Number(spec.totalFrames) : (Number(spec.duration) > 0 ? Math.round(Number(spec.duration) * fps) : 0)
|
|
288
|
+
const read = readCanvasSpec(doc, { fps })
|
|
289
|
+
const errors = []
|
|
290
|
+
const warnings = []
|
|
291
|
+
const found = findSpecNodes(doc)
|
|
292
|
+
if (found.split.length > 0) errors.push('分镜表被拆成了 ' + found.split.length + ' 条:' + found.split.map((node) => node.id).join(', ') + '(只留一张【分镜表】)')
|
|
293
|
+
if (found.extras.length > 0) errors.push('重复的规格节点:' + found.extras.map((node) => node.id).join(', '))
|
|
294
|
+
if (found.storyboard === null) errors.push('缺【分镜表】节点')
|
|
295
|
+
const rows = read.storyboard
|
|
296
|
+
if (found.storyboard !== null) {
|
|
297
|
+
if (rows.length === 0) errors.push('【分镜表】里没有数据行')
|
|
298
|
+
let cursor = 0
|
|
299
|
+
for (const [index, row] of rows.entries()) {
|
|
300
|
+
if (row.shot !== 'shot-' + (index + 1)) errors.push('镜号要连号:第 ' + (index + 1) + ' 行是 ' + row.shot)
|
|
301
|
+
if (row.durationFrames <= 0) errors.push(row.shot + ' 没有有效时长')
|
|
302
|
+
if (row.startFrame !== cursor) warnings.push(row.shot + ' 从第 ' + row.startFrame + ' 帧开始,但上一镜结束在 ' + (cursor - 1) + ' 帧(表里的帧区间要连续)')
|
|
303
|
+
cursor = row.startFrame + Math.max(1, row.durationFrames)
|
|
304
|
+
if (!row.intent) warnings.push(row.shot + ' 没写"这一镜要做什么"')
|
|
305
|
+
if (!row.onScreen) warnings.push(row.shot + ' 没写"屏幕上出现什么"')
|
|
306
|
+
}
|
|
307
|
+
if (totalFrames > 0 && cursor !== totalFrames) {
|
|
308
|
+
const delta = totalFrames - cursor
|
|
309
|
+
errors.push('各镜加起来是 ' + cursor + ' 帧,视频节点是 ' + totalFrames + ' 帧(' + (delta > 0 ? '少 ' : '多 ') + Math.abs(delta) + ' 帧):'
|
|
310
|
+
+ '两者必须相等。两种改法任选:把' + (delta > 0 ? '最后一镜延长 ' + delta + ' 帧' : '镜头删减/缩短共 ' + (-delta) + ' 帧')
|
|
311
|
+
+ ',或把视频节点时长改成 ' + (cursor / fps) + ' 秒(motion_project create 的 spec.duration,现有工程改 timeline.json 的 durationInFrames)')
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
if (read.style.tokens && Object.keys(read.style.tokens).length === 0 && found.style !== null) {
|
|
315
|
+
warnings.push('【风格】里没有认出任何颜色(写成 `page #F7F3EA`)')
|
|
316
|
+
}
|
|
317
|
+
return { errors, warnings, read }
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
/** 一行分镜 → 表格行(帧区间写成 `0–35f`)。 */
|
|
321
|
+
export function storyboardRowLine(row) {
|
|
322
|
+
const start = Number(row.startFrame) || 0
|
|
323
|
+
const duration = Math.max(1, Number(row.durationFrames) || 1)
|
|
324
|
+
return '| ' + [row.shot, start + '–' + (start + duration - 1) + 'f', row.card || '—', row.intent || '—', row.onScreen || '—', row.copy || '—']
|
|
325
|
+
.map(escapeStoryboardCell).join(' | ') + ' |'
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
export const STORYBOARD_HEADER = '| 镜头 | 帧区间 | 镜头卡 | 这一镜要做什么 | 屏幕上出现什么 | 文案 / 声音 |\n| --- | --- | --- | --- | --- | --- |'
|
|
329
|
+
|
|
330
|
+
/**
|
|
331
|
+
* 把外部给的分镜(数组或 markdown)排成一张表。
|
|
332
|
+
* 数组项的 frames / duration 优先;否则在 totalFrames 里均分。
|
|
333
|
+
*/
|
|
334
|
+
export function storyboardTable(input, options = {}) {
|
|
335
|
+
const fps = Number(options.fps) > 0 ? Number(options.fps) : 30
|
|
336
|
+
if (typeof input === 'string') {
|
|
337
|
+
const text = input.trim().replace(/^【分镜表】\s*\n/, '')
|
|
338
|
+
if (text.startsWith('|')) {
|
|
339
|
+
const lines = text.split('\n')
|
|
340
|
+
// A complete Markdown table already contains its header and separator.
|
|
341
|
+
// Row-only input still receives the canonical header as before.
|
|
342
|
+
if (lines.length > 1 && SEPARATOR.test(lines[1].trim())) return text
|
|
343
|
+
return STORYBOARD_HEADER + '\n' + text
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
const list = Array.isArray(input) ? input : []
|
|
347
|
+
const total = Number(options.totalFrames) > 0 ? Number(options.totalFrames) : 0
|
|
348
|
+
const per = list.length > 0 && total > 0 ? Math.floor(total / list.length) : Math.round(fps * 2)
|
|
349
|
+
const rows = []
|
|
350
|
+
let cursor = 0
|
|
351
|
+
list.forEach((item, index) => {
|
|
352
|
+
const row = typeof item === 'string' ? { intent: item } : (item || {})
|
|
353
|
+
const own = Number(row.durationFrames) > 0 ? Number(row.durationFrames) : (Number(row.frames) > 0 ? Number(row.frames) : per)
|
|
354
|
+
const duration = index === list.length - 1 && total > 0 ? Math.max(1, total - cursor) : Math.max(1, own)
|
|
355
|
+
rows.push({
|
|
356
|
+
shot: 'shot-' + (index + 1),
|
|
357
|
+
startFrame: cursor,
|
|
358
|
+
durationFrames: duration,
|
|
359
|
+
card: row.card || '',
|
|
360
|
+
intent: row.intent || row.what || row.beats || '',
|
|
361
|
+
onScreen: row.onScreen || row.screen || '',
|
|
362
|
+
copy: row.copy || '',
|
|
363
|
+
})
|
|
364
|
+
cursor += duration
|
|
365
|
+
})
|
|
366
|
+
return STORYBOARD_HEADER + '\n' + rows.map(storyboardRowLine).join('\n')
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
export function styleTemplate(style) {
|
|
370
|
+
if (typeof style === 'string' && style.trim().length > 0) return SPEC_MARKERS.style + '\n' + style.trim()
|
|
371
|
+
const palette = (style && style.palette) || {}
|
|
372
|
+
const fonts = (style && style.fonts) || {}
|
|
373
|
+
const lines = [SPEC_MARKERS.style]
|
|
374
|
+
for (const key of ['page', 'text', 'muted', 'accent', 'surface', 'field', 'border']) {
|
|
375
|
+
if (palette[key]) lines.push(key + ' ' + palette[key])
|
|
376
|
+
}
|
|
377
|
+
if (fonts.display) lines.push('serif "' + fonts.display + '"')
|
|
378
|
+
if (fonts.text) lines.push('sans "' + fonts.text + '"')
|
|
379
|
+
if (fonts.mono) lines.push('mono "' + fonts.mono + '"')
|
|
380
|
+
if (style && Array.isArray(style.materials) && style.materials.length) lines.push('materials: ' + style.materials.join(', '))
|
|
381
|
+
if (lines.length === 1) {
|
|
382
|
+
lines.push('page #F7F3EA', 'text #2B2B2B', 'muted #8A8578', 'accent #C0392B', 'border #D9D3C3')
|
|
383
|
+
}
|
|
384
|
+
return lines.join('\n')
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
const SPEC_WIDTH = { storyboard: 700, style: 440, narration: 440 }
|
|
388
|
+
|
|
389
|
+
/**
|
|
390
|
+
* 生成规格区的 diagram_apply ops:优先放视频左侧,空间不足时择优放到右侧。
|
|
391
|
+
*
|
|
392
|
+
* 只在缺的时候建:已经有【分镜表】就不再建第二张(那正是要避免的事)。
|
|
393
|
+
*/
|
|
394
|
+
export function scaffoldOps(doc, options = {}) {
|
|
395
|
+
const found = findSpecNodes(doc)
|
|
396
|
+
const video = options.videoNode || null
|
|
397
|
+
const pending = []
|
|
398
|
+
const push = (marker, label, width, data) => {
|
|
399
|
+
// Estimate wrapped monospace lines as well as explicit newlines, including wide CJK characters.
|
|
400
|
+
const capacity = Math.max(1, Math.floor((width - 24) / 7.2))
|
|
401
|
+
const lines = label.split('\n').reduce((sum, line) => {
|
|
402
|
+
const cells = Array.from(line).reduce((n, char) => n + (char.codePointAt(0) >= 0x2e80 ? 2 : 1), 0)
|
|
403
|
+
return sum + Math.max(1, Math.ceil(cells / capacity))
|
|
404
|
+
}, 0)
|
|
405
|
+
pending.push({ marker, label, width, data, height: Math.max(80, Math.min(2000, 28 + lines * 18)) })
|
|
406
|
+
}
|
|
407
|
+
if (found.storyboard === null) push(SPEC_MARKERS.storyboard, SPEC_MARKERS.storyboard + '\n' + storyboardTable(options.storyboard || [], options), SPEC_WIDTH.storyboard,
|
|
408
|
+
typeof options.storyboard === 'string' ? undefined : { drawaiTableEscaped: '1' })
|
|
409
|
+
if (found.style === null) push(SPEC_MARKERS.style, options.style === undefined ? styleTemplate(options.styleKit || null) : styleTemplate(options.style), SPEC_WIDTH.style)
|
|
410
|
+
if (found.narration === null && options.narration !== undefined) {
|
|
411
|
+
push(SPEC_MARKERS.narration, SPEC_MARKERS.narration + '\n' + String(options.narration).trim(), SPEC_WIDTH.narration)
|
|
412
|
+
}
|
|
413
|
+
const validVideo = video && Number.isFinite(Number(video.x)) && Number.isFinite(Number(video.y))
|
|
414
|
+
const baseY = validVideo ? Math.max(40, Number(video.y) - 380) : 170
|
|
415
|
+
const width = Math.max(0, ...pending.map(item => item.width))
|
|
416
|
+
const height = pending.reduce((sum, item) => sum + item.height, 0) + Math.max(0, pending.length - 1) * 56
|
|
417
|
+
const candidates = validVideo
|
|
418
|
+
? [Number(video.x) - width - 140, Number(video.x) + Math.max(0, Number(video.w) || 0) + 140].filter(x => x >= 40)
|
|
419
|
+
: [60]
|
|
420
|
+
const obstacles = (Array.isArray(doc?.nodes) ? doc.nodes : []).filter(node =>
|
|
421
|
+
Number.isFinite(Number(node.x)) && Number.isFinite(Number(node.y)) && Number(node.w) > 0 && Number(node.h) > 0)
|
|
422
|
+
const placement = candidates.map((x, index) => {
|
|
423
|
+
let y = baseY
|
|
424
|
+
// Keep the entire column clear of existing nodes; shift past any blocking rectangles.
|
|
425
|
+
for (let attempt = 0; attempt <= obstacles.length; attempt++) {
|
|
426
|
+
const hits = obstacles.filter(node => x < Number(node.x) + Number(node.w) + 50 && x + width + 50 > Number(node.x)
|
|
427
|
+
&& y < Number(node.y) + Number(node.h) + 50 && y + height + 50 > Number(node.y))
|
|
428
|
+
if (!hits.length) break
|
|
429
|
+
y = Math.max(...hits.map(node => Number(node.y) + Number(node.h) + 50))
|
|
430
|
+
}
|
|
431
|
+
return { x, y, score: y - baseY + index * 10 }
|
|
432
|
+
}).sort((a, b) => a.score - b.score)[0] || { x: 60, y: baseY }
|
|
433
|
+
let y = placement.y
|
|
434
|
+
const ops = pending.map(item => {
|
|
435
|
+
const op = { op: 'addNode', shape: 'text', label: item.label, x: placement.x, y, w: item.width, h: item.height,
|
|
436
|
+
style: 'rounded=0;whiteSpace=wrap;html=1;fontSize=12;align=left;verticalAlign=top;fontFamily=monospace;fillColor=none;strokeColor=#c8c2b4;' }
|
|
437
|
+
if (item.data) op.data = item.data
|
|
438
|
+
y += item.height + 56
|
|
439
|
+
return op
|
|
440
|
+
})
|
|
441
|
+
return { ops, created: pending.map(item => item.marker), already: [found.storyboard ? SPEC_MARKERS.storyboard : '', found.style ? SPEC_MARKERS.style : '', found.narration ? SPEC_MARKERS.narration : ''].filter(Boolean) }
|
|
442
|
+
}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
// 页面截屏:把一段 HTML 模板(或一个网址)在无头浏览器里渲染成 PNG,再进工程 assets/。
|
|
2
|
+
//
|
|
3
|
+
// 为什么要它:写产品界面镜头时,最费时间的不是动画,是"图从哪来"。
|
|
4
|
+
// 上游 skill 用一个 Playwright 脚本截模板;这里不引入 Playwright,
|
|
5
|
+
// 直接复用 Remotion 已经在用的 Chrome/Edge 二进制做一次性截屏。
|
|
6
|
+
|
|
7
|
+
import { existsSync, mkdirSync, statSync, writeFileSync } from 'node:fs'
|
|
8
|
+
import { join } from 'node:path'
|
|
9
|
+
import { execFile } from 'node:child_process'
|
|
10
|
+
import { promisify } from 'node:util'
|
|
11
|
+
import { pathToFileURL } from 'node:url'
|
|
12
|
+
import { captureAsset } from './assets.js'
|
|
13
|
+
|
|
14
|
+
const exec = promisify(execFile)
|
|
15
|
+
|
|
16
|
+
const POSIX = [
|
|
17
|
+
'/usr/bin/google-chrome',
|
|
18
|
+
'/usr/bin/chromium',
|
|
19
|
+
'/usr/bin/chromium-browser',
|
|
20
|
+
'/Applications/Google Chrome.app/Contents/MacOS/Google Chrome',
|
|
21
|
+
]
|
|
22
|
+
|
|
23
|
+
const WINDOWS = [
|
|
24
|
+
'C:/Program Files/Google/Chrome/Application/chrome.exe',
|
|
25
|
+
'C:/Program Files (x86)/Google/Chrome/Application/chrome.exe',
|
|
26
|
+
'C:/Program Files (x86)/Microsoft/Edge/Application/msedge.exe',
|
|
27
|
+
'C:/Program Files/Microsoft/Edge/Application/msedge.exe',
|
|
28
|
+
]
|
|
29
|
+
|
|
30
|
+
export function browserExecutable() {
|
|
31
|
+
const explicit = process.env.DRAWAI_CHROME_PATH
|
|
32
|
+
const list = [explicit, ...(process.platform === 'win32' ? WINDOWS : POSIX)].filter(Boolean)
|
|
33
|
+
for (const candidate of list) if (existsSync(candidate)) return candidate
|
|
34
|
+
throw new Error('找不到 Chrome / Edge:设 DRAWAI_CHROME_PATH 指向可执行文件后再试')
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function clamp(value, min, max) {
|
|
38
|
+
const number = Number(value)
|
|
39
|
+
if (!Number.isFinite(number)) return min
|
|
40
|
+
return Math.max(min, Math.min(max, Math.round(number)))
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function tail(error) {
|
|
44
|
+
const text = String(error?.stderr || error?.message || error)
|
|
45
|
+
const lines = text.split(/\r?\n/).filter((line) => line.trim())
|
|
46
|
+
return lines.slice(-2).join(' ') || '未知错误'
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// 返回 assets/ 里的条目(与 motion_assets capture 同一种形状)。
|
|
50
|
+
export async function capturePage(dir, options = {}) {
|
|
51
|
+
const width = clamp(options.width || 1440, 320, 2560)
|
|
52
|
+
const height = clamp(options.height || 900, 240, 2560)
|
|
53
|
+
const scale = clamp(options.scale || 2, 1, 3)
|
|
54
|
+
const wait = clamp(options.wait ?? 1200, 0, 20000)
|
|
55
|
+
const name = String(options.name || 'page-' + Date.now()).replace(/[\\/:*?"<>|]/g, '-')
|
|
56
|
+
const work = join(dir, '.out', 'capture')
|
|
57
|
+
mkdirSync(work, { recursive: true })
|
|
58
|
+
|
|
59
|
+
let target
|
|
60
|
+
if (options.url) {
|
|
61
|
+
target = String(options.url)
|
|
62
|
+
if (!/^https?:|^file:/i.test(target)) throw new Error('url 必须是 http(s) 或 file: 开头')
|
|
63
|
+
} else if (options.html) {
|
|
64
|
+
const file = join(work, name + '.html')
|
|
65
|
+
writeFileSync(file, String(options.html), 'utf8')
|
|
66
|
+
target = pathToFileURL(file).href
|
|
67
|
+
} else {
|
|
68
|
+
throw new Error('page 需要 url 或 html(直接给 HTML 源文本更常见)')
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const png = join(work, name + '.png')
|
|
72
|
+
const base = ['--disable-gpu', '--hide-scrollbars', '--no-first-run', '--no-default-browser-check',
|
|
73
|
+
'--force-device-scale-factor=' + scale, '--window-size=' + width + ',' + height,
|
|
74
|
+
'--virtual-time-budget=' + wait, '--screenshot=' + png]
|
|
75
|
+
const attempts = [['--headless=new', ...base, target], ['--headless', ...base, target]]
|
|
76
|
+
let failure
|
|
77
|
+
for (const args of attempts) {
|
|
78
|
+
try {
|
|
79
|
+
await exec(browserExecutable(), args, { windowsHide: true, timeout: 120000, maxBuffer: 8 * 1024 * 1024 })
|
|
80
|
+
failure = undefined
|
|
81
|
+
break
|
|
82
|
+
} catch (error) {
|
|
83
|
+
failure = error
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
if (failure) throw new Error('截屏失败:' + tail(failure))
|
|
87
|
+
if (!existsSync(png) || statSync(png).size < 1024) throw new Error('截屏没有产生图片(页面可能是空白或加载失败)')
|
|
88
|
+
|
|
89
|
+
const asset = await captureAsset(dir, png, {
|
|
90
|
+
name,
|
|
91
|
+
caption: options.caption || (options.url ? '页面截图 ' + target : 'HTML 模板截图'),
|
|
92
|
+
source: options.url ? 'page:' + target : 'page:html',
|
|
93
|
+
})
|
|
94
|
+
return { ...asset, viewport: { width, height, scale }, url: target }
|
|
95
|
+
}
|