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
package/README.md
CHANGED
|
@@ -8,15 +8,24 @@
|
|
|
8
8
|
|
|
9
9
|
## 功能一览
|
|
10
10
|
|
|
11
|
-
`dsh-drawai` 把可编辑画布放进 DeepSeek Harness 右侧栏,并给模型两个改图工具;人和 AI
|
|
11
|
+
`dsh-drawai` 把可编辑画布放进 DeepSeek Harness 右侧栏,并给模型两个改图工具;人和 AI 读写同一份画布。新建默认使用 `.drawai` 原生 JSON,旧 `.drawio` 在界面打开时自动迁移并保留原稿。
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
原生格式、媒体目录、自动迁移和兼容说明见[原生画布文档](docs/native-canvas.md)。
|
|
14
|
+
|
|
15
|
+
1. **右侧栏可编辑画布** —— 打开 / 新建工作区 `.drawai` 或 `.drawio`:画节点、连边、就地改字、分层、排序、对齐、导出;双击即可改标签。
|
|
14
16
|
2. **自然语言改图** —— 对话里说需求,模型用 `diagram_read` / `diagram_apply` 读现状、结构化改图、自动布局并原子写回。
|
|
15
17
|
3. **选区感知与回退** —— 你在画布上选中什么,模型就知道什么;模型改动最多可退 8 步。
|
|
16
18
|
4. **SVG 图形节点** —— 图标 / logo 可直接当节点内容,拼流程图与示意图。
|
|
17
19
|
5. **位图矢量化(`traceImage`)** —— 照着工作区图片逐像素描成 SVG 节点,不是凭印象手写近似图。
|
|
18
|
-
6.
|
|
20
|
+
6. **原生文档与 drawio 兼容** —— `.drawai` 保存多页、节点扩展和叠放顺序;旧画布迁移保留完整 XML 与原媒体,支持另存为 `.drawio`。
|
|
19
21
|
7. **视频复刻** —— 选中已导入视频,点击悬浮工具条「绘制复刻」自主设置画质、帧率、完整视频或起止范围,自动抽帧并绘制成 SVG 视频;显示预计帧数、实际进度与帧预览,支持取消,原视频保留。支持 SVG 帧序列包,以及保留原帧率、白底和片段音频的 MP4 / WebM 离线导出;也可导出白底、循环播放的无声 GIF。渲染、编码和保存由宿主后台执行,切换标签或刷新网页后仍继续,完成文件自动保存到画布旁(编码需 FFmpeg)。详见[视频节点说明](docs/video-nodes.md)。
|
|
22
|
+
8. **文生图** —— 0.4.0 暂未开放,已有图片节点仍可显示和编辑。
|
|
23
|
+
|
|
24
|
+
9. **节点绘制视频** —— 选中视频节点,先用 `shotcraft_search` 挑几张镜头卡连到它(连线顺序=镜头顺序,卡片时长=段长;**卡必须连**,断连的卡会被 `motion_project create` 直接拒绝),再说一句要做什么:`motion_project` 按卡片排好时间轴,并把**分镜表**(每镜做什么/屏幕上出现什么/配什么字)写进工程;先只写 `scene-0`、`motion_preview` 看一张**定调帧**把风格定住,再铺开写其余场景。AI 在对话里直接读写 `scene-*.tsx`,`motion_render` 在后台渲染,完成后 `motion_render apply` 把成片落成画布上能播的 clip pack。**只有对话里这一个 agent**:没有第二套后台 agent、不需要轮询、不用等任务面板。详见[动效视频说明](docs/motion-video.md)。
|
|
25
|
+
|
|
26
|
+
10. **音频、音效、旁白与踩点** —— 把 BGM、旁白挂进时间轴(`motion_audio add`),渲染时由画面侧一起播放,淡入淡出与「旁白期间压低 BGM」都在音量曲线上算;旁白用 `motion_voice` 合成(脚本一句一行,台词决定镜头多长,字幕自动进 `timeline.subtitles`);音效可用 `motion_sfx` 从 Freesound 搜(**不按许可过滤**,每条结果带许可标签,用哪条由人决定),下载时保存来源和许可信息至音频素材索引,需署名时根据索引自行整理。`motion_audio analyze` 给出 BPM 与等距节拍网格,`motion_audio apply` 把切镜吸到拍子上,`motion_audio_verify` 再从成片音轨回测对拍误差。详见[声音制作说明](docs/motion-audio.md)。
|
|
27
|
+
|
|
28
|
+
11. **素材捕获与看帧验收** —— 图片、音频、视频可捕获进工程 `assets/`(`motion_assets`,附尺寸、色板、峰值),**产品界面还能用 `motion_assets page` 让无头 Chrome 把一段 HTML 模板或网址截成 2 倍图**(截到登录页/错误页/空白页会直接报错,不会静默存一张假界面),场景里用 `staticFile()` 引用;`motion_preview` 一次渲染多帧拼成一张图直接回给对话,不用等整片渲染就能看版式。详见[动效视频说明](docs/motion-video.md)。
|
|
20
29
|
|
|
21
30
|

|
|
22
31
|
|
|
@@ -44,8 +53,9 @@ dsh plugin --profile web add github:fourzkw/dsh-drawai
|
|
|
44
53
|
|
|
45
54
|
| 装好后的变化 | 出现在哪里 |
|
|
46
55
|
|---|---|
|
|
47
|
-
| 「DrawAI 画布」面板,负责打开 `**/*.drawio` | 右侧栏 |
|
|
48
|
-
| `diagram_read` / `diagram_apply` | 模型工具集 |
|
|
56
|
+
| 「DrawAI 画布」面板,负责打开 `**/*.drawai` 和 `**/*.drawio` | 右侧栏 |
|
|
57
|
+
| `diagram_read` / `diagram_apply` / `shotcraft_search` | 模型工具集 |
|
|
58
|
+
| `motion_project` / `motion_preview` / `motion_render` / `motion_assets` / `motion_audio` | 视频工程工具 |
|
|
49
59
|
| `drawai-canvas` 技能 | 会话技能目录(模型按需取全文) |
|
|
50
60
|
|
|
51
61
|
- 两种装法都不需要 `allowBuilds` 构建授权:npm 包是预构建的;GitHub 源码包里 `lib/` 也已提交、没有 `prepare` / `postinstall` 脚本。
|
|
@@ -53,13 +63,22 @@ dsh plugin --profile web add github:fourzkw/dsh-drawai
|
|
|
53
63
|
|
|
54
64
|
## 使用 / Usage
|
|
55
65
|
|
|
56
|
-
|
|
66
|
+
人和模型共享同一画布文档;对话可直接编排节点、建视频工程、改场景、看预览和出片。迁移后以 `.drawai` 为主,原 `.drawio` 作为保留原稿;外部编辑原稿会提示版本冲突。
|
|
57
67
|
|
|
58
68
|
| 入口 | 你能得到什么 |
|
|
59
69
|
|---|---|
|
|
60
70
|
| **右侧栏画布** | 打开或新建画布、画节点与连线、就地改字、图层、分区、顺序、多选对齐、查找、微移、多页切换、复制粘贴、导出 SVG / PNG / 透明 PNG(4×,含导出选中) |
|
|
61
71
|
| **`diagram_apply`** | 让模型按 ops 改图:增删改、重接边、复制、排序、分层、导出;支持 `ids` 批量、`as` 别名、`expectRevision` |
|
|
62
72
|
| **`diagram_read`** | 让模型看见图的结构与样式、图层与父级、页面尺寸、你此刻的选区、文件指纹 `revision` |
|
|
73
|
+
| **`motion_project`** | 建/打开视频工程:按挂上来的镜头卡排时间轴,写 `timeline.json` 与 `scene-*.tsx` 桩 |
|
|
74
|
+
| **`motion_preview`** | 渲染指定几帧并拼成一张图直接回图(首次打包 10–30 秒,之后复用缓存) |
|
|
75
|
+
| **`motion_render`** | `start` 立刻返回 jobId、后台渲染;`status`/`list`/`cancel`;`apply` 把成片回填成画布 clip pack |
|
|
76
|
+
| **`motion_assets`** | `list` 看工程素材,`library` 搜插件纹理和乐器采样,`use` 按需导入;另支持 `capture`、`crop`、`palette`、`page` 等 |
|
|
77
|
+
| **`motion_audio`** | 音轨增删与对齐:`list`/`add`/`remove`/`analyze`(节拍网格)/`apply`(钉帧) |
|
|
78
|
+
| **`motion_sfx`** | 音效采购:`search`(Freesound,不按许可过滤,结果带许可)/`get`(下载 + 详情页许可复核 + 署名清单)/`list` |
|
|
79
|
+
| **`motion_audio_verify`** | 从成片音轨回测每拍偏差,超过 2 帧判不合格 |
|
|
80
|
+
| **`motion_voice`** | 旁白:`plan`(脚本→估时长→重排镜头→写字幕,不合成)/`say`(合成后再按真实时长重排)/`align`(只按已有音频重排)/`list`。脚本 `voice.script.md` 一节一句,台词决定镜头多长 |
|
|
81
|
+
| **`motion_task`** | 旧任务记录:`list`/`status`/`cancel`/`focus`/`remove`(新流程不用它开工) |
|
|
63
82
|
| **选中与回退** | 你选中什么模型就知道什么;模型的每一轮改动都能在界面上退回去 |
|
|
64
83
|
|
|
65
84
|
## 🗂️ 画布面板 / The canvas panel
|
|
@@ -72,7 +91,8 @@ dsh plugin --profile web add github:fourzkw/dsh-drawai
|
|
|
72
91
|
| **SVG 图形节点** | 节点内容直接是一段 **SVG 标记**(图标 / logo / 示意图这种形状词汇表里没有的东西):右键「SVG 内容…」贴进去即可,或让模型用 `addNode {shape:"svg", svg:"<svg viewBox…>…</svg>"}`。落盘是 drawio **原生**的图片形状(`shape=image` + `image=data:image/svg+xml,<百分号编码>`),所以 drawio 桌面版打开同样画得出来;标记只喂给 `<image>`(独立文档,脚本不执行、也不碰页面 DOM) |
|
|
73
92
|
| **内嵌位图** | 从剪贴板粘贴或拖入 PNG / JPEG / GIF / WebP,画布会**真的画出来**(同为 `shape=image` + data URI)。`diagram_read` 把 base64 省略成 `«image N 字符»`,不撑爆上下文 |
|
|
74
93
|
| **位图矢量化 traceImage** | 让模型照着工作区里一张图**逐像素**描成 SVG 内容节点(读像素 → 按必填的 `removeBg` 决定是否泛洪抠底 → k-means 调色板 → 3× 超采样归类 → 按颜色取像素级闭合轮廓 → 每色一条 nonzero path)。形状词汇表表达不了的照片 / 插画走这条,而不是让模型手写一段近似 SVG 冒充复刻 |
|
|
75
|
-
| **文本** |
|
|
94
|
+
| **文本** | 双击节点就地改字。独立文字和普通图形标签可对选中文字设置粗体、斜体、下划线、删除线、字色和字号;图片和视频节点不显示文字 |
|
|
95
|
+
| **表格** | 元素条可新建表格;Markdown 表格与已有分镜表显示为网格,双击或选中后可逐格编辑。分镜仍以 Markdown 保存在一个节点中供视频工程读取 |
|
|
76
96
|
| **连线** | 四选一画法(直线 / 直角折线 / 圆角折线 / 曲线)× 线型(含虚线间距)× 箭头(单向 / 双向 / 无 / 反向)× 颜色 × 引出段长度;自环;悬空端(从节点脱开后成为自由端点,还能再拖回去) |
|
|
77
97
|
| **连线上的文字** | 双击改、按住拖(半格 + 贴线吸附)、右键「标签居中」;**线在文字的位置真的断开**(不盖白底) |
|
|
78
98
|
| **选中与移动** | 框选 / Shift 加选 / `Ctrl+A`;整体拖动(折点与自由端点跟着同一个位移走);对齐辅助线;多选对齐与分布 |
|
|
@@ -82,7 +102,7 @@ dsh plugin --profile web add github:fourzkw/dsh-drawai
|
|
|
82
102
|
| **导出** | SVG / PNG(4×) / 透明 PNG(4×);「看一眼画布效果」走 DSH 附件服务,**不在工作区落文件** |
|
|
83
103
|
| **回退** | 「编辑 → 撤销 AI 改动」最多可以连着退 8 步 |
|
|
84
104
|
|
|
85
|
-
## 🤖
|
|
105
|
+
## 🤖 模型工具 / Model tools
|
|
86
106
|
|
|
87
107
|
### `diagram_apply` —— 模型怎么改图
|
|
88
108
|
|
|
@@ -195,6 +215,10 @@ dsh plugin --profile web add github:fourzkw/dsh-drawai
|
|
|
195
215
|
画布是自己实现的:纯 SVG + DOM,没有 iframe,也不依赖任何外部编辑器资源。换来的是每个单元都有
|
|
196
216
|
id / layer / parent、选区能上报给模型、写回是逐单元的、画布本身能被模型驱动;代价是只覆盖这个格式的一个子集。
|
|
197
217
|
|
|
218
|
+
## 🎨 文生图(暂未开放)
|
|
219
|
+
|
|
220
|
+
0.4.0 暂时关闭文生图的新增、生成和服务设置入口;旧画布中的图片和节点数据继续保留。
|
|
221
|
+
|
|
198
222
|
## ⚙️ 配置与默认值 / Configuration
|
|
199
223
|
|
|
200
224
|
**目前还没有设置页**:下面这些默认值都写在代码里;能通过 AI 侧改的,在右列给出等价做法。
|
|
@@ -209,6 +233,7 @@ id / layer / parent、选区能上报给模型、写回是逐单元的、画布
|
|
|
209
233
|
| 主题跟随 DSH 的明暗 | —— |
|
|
210
234
|
| 显示层、当前层 | 「图层」菜单(会写进文件) |
|
|
211
235
|
|
|
236
|
+
|
|
212
237
|
**右侧常驻元素条**:按类型(图形 / 文字 / 分区 / SVG / 图片)放置,点画布落下**一次**即卸工具;
|
|
213
238
|
「图形」的矩形、圆角、菱形等是同族变体,只在侧面板展开,不全部铺在主条上。选中图形后可换同族形状;悬停画布节点不会触发右侧工具栏浮窗。
|
|
214
239
|
|
|
@@ -234,6 +259,10 @@ id / layer / parent、选区能上报给模型、写回是逐单元的、画布
|
|
|
234
259
|
|
|
235
260
|
**就地文字编辑**:双击节点或连线标签开始编辑,`Enter` / `Shift+Enter` 换行,点击外部或 `Ctrl+Enter`(Mac 为 `Command+Enter`)完成,`Esc` 取消。输入法确认文字不会提前完成编辑。
|
|
236
261
|
|
|
262
|
+
独立文字和普通图形标签支持部分 draw.io 富文本格式,并可在 `.drawai` 保存及 `.drawio` 往返中保留。导入的复杂 HTML 经安全过滤后显示,完整 HTML 表格和连线富文本等仍不完整;混合字号的换行与对齐可能和 draw.io 不完全一致。表格节点的单元格编辑会以纯文字保存,若原有局部样式会在编辑器中提示。
|
|
263
|
+
|
|
264
|
+
选中文字节点后调整「字色」或「字号」,会统一整段文字的对应属性,保留粗体、斜体等其他格式;双击进入编辑并选中部分文字后设置格式,仅影响选中的文字。工具栏与弹出面板会尽量留在窗口内。
|
|
265
|
+
|
|
237
266
|
编辑框与显示标签共用文字区域、字体、行高和缩放:普通节点居中,分区标题左上对齐;换行在浏览器中按实际字体宽度计算,编辑中的节点撑高会先预览再提交。连线标签也支持多行显示,并按实际字号设置编辑框与连线避让区域。
|
|
238
267
|
|
|
239
268
|
**连线工具与修改入口**:悬停右侧连线图标选择直线、直角折线、圆角折线或曲线,然后在空白处拖拽确定起终点;直接点击连线图标使用上次选择的类型。连线工具用于新建;修改已有连线请选中后使用浮动工具栏的「线型」,也可使用右键菜单。
|
|
@@ -244,7 +273,8 @@ id / layer / parent、选区能上报给模型、写回是逐单元的、画布
|
|
|
244
273
|
- **生效方式**:宿主侧只在 `dsh web` 启动时加载,换 `lib/index.js` 必须**重启**;客户端是独立 bundle,刷新页面即可。
|
|
245
274
|
- **界面语言**:目前只有简体中文,还没有 i18n。
|
|
246
275
|
- **大图性能**:几百个单元时的重渲染与路由开销还没测过。
|
|
247
|
-
-
|
|
276
|
+
- **数据与网络**:画布文件和视频工程保存在工作区;音效搜索会访问 Freesound,网页素材收集会访问指定网站及品牌素材服务,Edge TTS 旁白和首次获取 Chromium 也可能联网。API key 存在浏览器 localStorage,宿主进程不保存;没有安装期脚本。
|
|
277
|
+
- **视频素材**:动效用的 46 个字体与 102 个乐器采样 / 9 张纹理**不在 npm 包里**(约 200MB),第一次用到动效功能时会从 GitHub Release 下载一次(约 145MB),逐文件校验后缓存在 `~/.dsh/drawai-assets/v1/`,之后离线可用。下载失败或素材缺失时工具会返回可照做的中文指引;画布与图片功能不受影响。缓存位置与镜像地址可用 `DRAWAI_ASSETS_DIR` / `DRAWAI_ASSETS_URL` 覆盖;素材来源与许可集中记录在 [统一说明](LICENSE)。通过 `motion_assets library` 检索,再用 `motion_assets use` 导入当前工程,不依赖其他工程或固定磁盘路径;文件与上游哈希不一致时会提示并使用本地文件。导入图片和音频时需要可用的 FFmpeg。
|
|
248
278
|
|
|
249
279
|
## 已知限制 / Limitations
|
|
250
280
|
|
|
@@ -252,7 +282,10 @@ id / layer / parent、选区能上报给模型、写回是逐单元的、画布
|
|
|
252
282
|
- 独立 `edgeLabel` 单元只读;`entityRelation` 等其它 edgeStyle、箭头字形、旋转 / 翻转 / 透明、stencil 形状只保留、不渲染;`.drawio.svg` / `.png` / `.html` 内嵌载体不支持。
|
|
253
283
|
- **图片**:内嵌 SVG(`data:image/svg+xml,…`)与内嵌位图(PNG/JPEG/GIF/WebP data URI)都会画出来;**外链 / 相对路径**图片仍按矩形占位,单元原样保留(工作区磁盘上的图片文件画布直接读不到——要复刻请用 `traceImage`)。
|
|
254
284
|
- 导出支持 SVG 与 PNG(透明背景、倍数或指定尺寸):没有 PDF。
|
|
285
|
+
- **文生图**:0.4.0 暂未开放。
|
|
255
286
|
- 交互细节:对齐辅助线只跟节点比(不与折点、端点对齐);单条选中的连线不能复制。
|
|
287
|
+
- **视频**:动效视频节点是「画布上的一个工程目录」,成片靠 `motion_render` 后台渲染(首帧要打包,第一次通常 20–40 秒),渲染完成后用 `motion_render apply` 回填成 clip pack 才能在卡片里播放。节点不支持 `video_player` 式的自由播放控件(逐帧 / 跳转),要精确对照请用 `motion_preview` 或 `motion_render` 的 `frames` 参数看静帧。单帧渲染比整片省时,但每次仍要过一次打包(源码或素材变了才会重建缓存)。
|
|
288
|
+
- **声音**:`motion_audio` 只做「分析节拍 + 挂到轴 + 钉拍」,不生成音乐;旁白靠 `motion_voice` 合成(edge-tts,需要本机 Python 3 + `edge_tts` 包;拿不到就退回 Windows SAPI,音色是老式播音腔)。整片渲完会自动过一次响度归一 + 限幅(`loudnorm` + `alimiter`,默认 −16 LUFS / 真峰值 −1.5 dBFS),目标可以在 `timeline.audioMix` 里改;画布上的音频节点只是素材库里的文件,不参与渲染。
|
|
256
289
|
|
|
257
290
|
完整的取舍与实现细节在 [docs/design.md](docs/design.md)。
|
|
258
291
|
|
|
@@ -262,7 +295,7 @@ id / layer / parent、选区能上报给模型、写回是逐单元的、画布
|
|
|
262
295
|
npm run watch # 常驻:监听 src/,变化即重建 lib/
|
|
263
296
|
npm run build # 一次性构建
|
|
264
297
|
npm run check # 安装前烟测(含 lib 与 src 是否同步)
|
|
265
|
-
npm test #
|
|
298
|
+
npm test # 多份自测(mxfile 编解码 / 路由预览 / 宿主行为 / 渲染 / 组件 / 矢量化 / 视频 / 文生图 provider 与宿主路由)
|
|
266
299
|
```
|
|
267
300
|
|
|
268
301
|
> ⚠️ **绝不改 `lib/` 下的文件** —— 它们是构建产物,会被覆盖。改 `src/`。
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
/* 由 tools/build.mjs 生成 —— 请勿直接编辑;改 src/ 下的源文件。 */
|
|
2
|
+
import { execFile } from 'node:child_process'
|
|
3
|
+
import { promisify } from 'node:util'
|
|
4
|
+
import { randomUUID, createHash } from 'node:crypto'
|
|
5
|
+
import { mkdirSync, readFileSync, writeFileSync, copyFileSync, statSync, realpathSync, existsSync, renameSync, unlinkSync } from 'node:fs'
|
|
6
|
+
import { join, dirname, basename, relative, isAbsolute } from 'node:path'
|
|
7
|
+
import { searchSfx, getSfx } from './motion/sfx.js'
|
|
8
|
+
import { ffmpegBinary } from './video-export.js'
|
|
9
|
+
import { clipDirFor, ensureClipPack, mediaRootFor } from './media.js'
|
|
10
|
+
import { audioNodeData, audioNodeSettings, validateAudioTimeline } from './motion-core.js'
|
|
11
|
+
const exec = promisify(execFile)
|
|
12
|
+
const MAX_BYTES = 32 * 1024 * 1024
|
|
13
|
+
const sha = bytes => createHash('sha256').update(bytes).digest('hex')
|
|
14
|
+
const run = (args,signal,timeout=120000) => exec(ffmpegBinary(),args,{windowsHide:true,timeout,maxBuffer:2*1024*1024,signal})
|
|
15
|
+
|
|
16
|
+
export function audioScopeDirectory(drawioAbs,directory,create=false) {
|
|
17
|
+
const root=realpathSync(dirname(drawioAbs))
|
|
18
|
+
let ancestor=directory
|
|
19
|
+
while(!existsSync(ancestor)){const parent=dirname(ancestor);if(parent===ancestor)throw new Error('音频目录无效');ancestor=parent}
|
|
20
|
+
const inside=value=>{const rel=relative(root,realpathSync(value));if(isAbsolute(rel)||rel==='..'||rel.startsWith('..\\')||rel.startsWith('../'))throw new Error('音频真实路径超出画布目录')}
|
|
21
|
+
inside(ancestor)
|
|
22
|
+
if(create)mkdirSync(directory,{recursive:true})
|
|
23
|
+
if(existsSync(directory))inside(directory)
|
|
24
|
+
return directory
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function frozenAudioFile(drawioAbs,clip) {
|
|
28
|
+
const directory=realpathSync(audioScopeDirectory(drawioAbs,clipDirFor(drawioAbs,clip))),file=realpathSync(join(directory,'audio.wav'))
|
|
29
|
+
const rel=relative(directory,file)
|
|
30
|
+
if(isAbsolute(rel)||rel.startsWith('..'))throw new Error('音频文件超出媒体包')
|
|
31
|
+
const manifest=JSON.parse(readFileSync(audioScopeDirectory(drawioAbs,join(directory,'audio.json')),'utf8'))
|
|
32
|
+
if(sha(readFileSync(file))!==manifest.sha256)throw new Error('音频文件已改变,请重新导入')
|
|
33
|
+
return {file,manifest}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export async function analyzeAudio(file,signal) {
|
|
37
|
+
const {stderr}=await run(['-hide_banner','-i',file,'-map','0:a:0','-af','volumedetect','-f','null','-'],signal)
|
|
38
|
+
const duration=/Duration:\s*(\d+):(\d+):([\d.]+)/.exec(stderr)
|
|
39
|
+
if(!duration)throw new Error('无法读取音频时长')
|
|
40
|
+
const seconds=Number(duration[1])*3600+Number(duration[2])*60+Number(duration[3])
|
|
41
|
+
const peak=/max_volume:\s*(-?[\d.]+|-inf) dB/.exec(stderr)?.[1]
|
|
42
|
+
return {duration:seconds,samplePeakDb:peak==='-inf'?null:Number(peak),hasAudio:true,measurement:'sample peak; not true peak or LUFS',log:stderr}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export async function importAudioFile(drawioAbs,source,{name=basename(source),role='sfx',provenance={provider:'upload'},signal}={}) {
|
|
46
|
+
audioNodeData(role)
|
|
47
|
+
if(statSync(source).size>MAX_BYTES)throw new Error('音频源文件不能超过32MB')
|
|
48
|
+
const before=await analyzeAudio(source,signal)
|
|
49
|
+
if(before.duration<=0||before.duration>600)throw new Error('音频时长须在0–600秒内')
|
|
50
|
+
audioScopeDirectory(drawioAbs,mediaRootFor(drawioAbs),true)
|
|
51
|
+
const clip='audio-'+randomUUID(),{dir}=ensureClipPack(drawioAbs,clip,{kind:'raster'})
|
|
52
|
+
await run(['-hide_banner','-loglevel','error','-y','-i',source,'-map','0:a:0','-vn','-ac','2','-ar','48000','-c:a','pcm_s16le',join(dir,'audio.wav')],signal)
|
|
53
|
+
await run(['-hide_banner','-loglevel','error','-y','-i',join(dir,'audio.wav'),'-c:a','aac','-b:a','128k','-movflags','+faststart',join(dir,'preview.m4a')],signal)
|
|
54
|
+
const bytes=readFileSync(join(dir,'audio.wav'))
|
|
55
|
+
if(bytes.length>128*1024*1024)throw new Error('标准化音频过大')
|
|
56
|
+
const {log,...analysis}=await analyzeAudio(join(dir,'audio.wav'),signal)
|
|
57
|
+
const manifest={version:1,name:String(name).slice(0,240),sha256:sha(bytes),sourceSha256:sha(readFileSync(source)),...analysis,provenance,
|
|
58
|
+
changes:['Decoded to stereo 48kHz PCM for mixing'],createdAt:new Date().toISOString()}
|
|
59
|
+
writeFileSync(join(dir,'audio.json'),JSON.stringify(manifest,null,2))
|
|
60
|
+
return {clip,manifest,data:{...audioNodeData(role),audioClip:clip,audioName:manifest.name,audioDuration:String(manifest.duration),audioSha256:manifest.sha256}}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export function defaultAudioPlan(input,composition,assets) {
|
|
64
|
+
const fps=composition.fps,total=composition.durationFrames
|
|
65
|
+
const cues=input.refs.filter(ref=>ref.role==='audio').map(ref=>{
|
|
66
|
+
const data=ref.nodes.find(node=>node.data?.drawaiAudio==='1')?.data||{},settings=audioNodeSettings(data)
|
|
67
|
+
const duration=Number(assets[ref.id]?.[0]?.duration||data.audioDuration)
|
|
68
|
+
if(!duration)throw new Error('音频还未上传:'+ref.id)
|
|
69
|
+
const from=Math.round(settings.at*fps),end=settings.trimEnd||duration
|
|
70
|
+
const length=Math.min(total-from,settings.loop?total:Math.max(1,Math.floor((end-settings.trimStart)*fps)))
|
|
71
|
+
return {referenceId:ref.id,offsetFrames:from,durationFrames:length,trimStart:settings.trimStart,
|
|
72
|
+
gainDb:settings.gainDb,fadeIn:Math.min(settings.fadeIn,length/fps),fadeOut:Math.min(settings.fadeOut,length/fps),loop:settings.loop,action:'节点设置:'+settings.role}
|
|
73
|
+
})
|
|
74
|
+
return validateAudioTimeline({cues},composition,input)
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export async function mixMotionAudio({input,composition,assets,publicDir,packDir,signal,plateSource}) {
|
|
78
|
+
const plan=validateAudioTimeline(composition.audio,composition,input),fps=composition.fps,seconds=composition.durationFrames/fps
|
|
79
|
+
const source=join(packDir,'source.mp4'),picture=join(packDir,'picture.mp4')
|
|
80
|
+
const logs=[]
|
|
81
|
+
if((plateSource||plan.cues.length)&&plateSource!==picture){
|
|
82
|
+
const result=await run(['-hide_banner','-y','-i',plateSource||source,'-map','0:v:0','-c:v','copy','-an',picture],signal)
|
|
83
|
+
logs.push(result.stderr)
|
|
84
|
+
}
|
|
85
|
+
if(!plan.cues.length){
|
|
86
|
+
if(plateSource)copyFileSync(picture,source)
|
|
87
|
+
mkdirSync(join(packDir,'project'),{recursive:true})
|
|
88
|
+
writeFileSync(join(packDir,'project','audio-plan.json'),JSON.stringify(plan,null,2))
|
|
89
|
+
return {status:'silent',listening:'not-performed',variants:{picture:plateSource?'picture.mp4':'source.mp4',full:'source.mp4'}}
|
|
90
|
+
}
|
|
91
|
+
const manifest=[]
|
|
92
|
+
const validateAsset=cue=>{
|
|
93
|
+
const asset=assets[cue.referenceId]?.[0]
|
|
94
|
+
if(asset?.kind!=='audio'||!/^asset-\d+\.wav$/.test(asset.src))throw new Error('音频快照不存在:'+cue.referenceId)
|
|
95
|
+
const root=realpathSync(publicDir),file=realpathSync(join(root,asset.src)),rel=relative(root,file)
|
|
96
|
+
if(isAbsolute(rel)||rel.startsWith('..'))throw new Error('音频快照超出工程')
|
|
97
|
+
if(sha(readFileSync(file))!==asset.sha256)throw new Error('音频快照哈希不一致')
|
|
98
|
+
return {asset,file}
|
|
99
|
+
}
|
|
100
|
+
async function mix(cues,filename){
|
|
101
|
+
if(!cues.length){copyFileSync(picture,join(packDir,filename));return}
|
|
102
|
+
const args=['-hide_banner','-y','-i',picture],filters=[],voices=cues.filter(cue=>cue.role==='voice')
|
|
103
|
+
cues.forEach((cue,index)=>{
|
|
104
|
+
const {asset,file}=validateAsset(cue),length=cue.durationFrames/fps,end=Number(asset.trimEnd||asset.duration)
|
|
105
|
+
if(cue.trimStart>=end||end>asset.duration+.011)throw new Error('音频裁切超出素材:'+cue.referenceId)
|
|
106
|
+
args.push('-i',file)
|
|
107
|
+
const chain=[`atrim=start=${cue.trimStart}:end=${end}`,'asetpts=PTS-STARTPTS']
|
|
108
|
+
if(cue.loop)chain.push(`aloop=loop=-1:size=${Math.max(1,Math.round((end-cue.trimStart)*48000))}`)
|
|
109
|
+
chain.push(`atrim=duration=${length}`,`volume=${Math.pow(10,cue.gainDb/20)}`)
|
|
110
|
+
if(cue.fadeIn)chain.push(`afade=t=in:st=0:d=${cue.fadeIn}`)
|
|
111
|
+
if(cue.fadeOut)chain.push(`afade=t=out:st=${Math.max(0,length-cue.fadeOut)}:d=${cue.fadeOut}`)
|
|
112
|
+
if(cue.role==='bgm'&&voices.length){
|
|
113
|
+
const windows=voices.map(voice=>`between(t,${(voice.from-cue.from)/fps},${(voice.from+voice.durationFrames-cue.from)/fps})`).join('+')
|
|
114
|
+
chain.push(`volume='if(gt(${windows},0),0.28,1)':eval=frame`)
|
|
115
|
+
}
|
|
116
|
+
chain.push(`apad=pad_dur=${length}`,`atrim=duration=${length}`,`adelay=${Math.round(cue.from/fps*48000)}S:all=1`)
|
|
117
|
+
filters.push(`[${index+1}:a]${chain.join(',')}[a${index}]`)
|
|
118
|
+
manifest.push({referenceId:cue.referenceId,sha256:asset.sha256,provenance:asset.provenance})
|
|
119
|
+
})
|
|
120
|
+
filters.push(cues.map((_,index)=>`[a${index}]`).join('')+`amix=inputs=${cues.length}:normalize=0,alimiter=limit=0.95:level=0:latency=1,apad,atrim=duration=${seconds}[mix]`)
|
|
121
|
+
const script=join(packDir,'project','mix-'+filename+'.txt');mkdirSync(dirname(script),{recursive:true});writeFileSync(script,filters.join(';\n'))
|
|
122
|
+
const temporary=join(packDir,filename+'.mixing.mp4')
|
|
123
|
+
try {
|
|
124
|
+
const result=await run([...args,'-filter_complex_script',script,'-map','0:v:0','-map','[mix]','-c:v','copy','-c:a','aac','-ar','48000','-b:a','192k','-t',String(seconds),'-movflags','+faststart',temporary],signal,180000)
|
|
125
|
+
logs.push(result.stderr);renameSync(temporary,join(packDir,filename))
|
|
126
|
+
} finally { if(existsSync(script))unlinkSync(script); if(existsSync(temporary))unlinkSync(temporary) }
|
|
127
|
+
}
|
|
128
|
+
await mix(plan.cues,'source.mp4')
|
|
129
|
+
if(plan.cues.some(cue=>cue.role==='bgm'))await mix(plan.cues.filter(cue=>cue.role!=='bgm'),'no-bgm.mp4')
|
|
130
|
+
const {log,...analysis}=await analyzeAudio(source,signal);logs.push(log)
|
|
131
|
+
writeFileSync(join(packDir,'project','audio-plan.json'),JSON.stringify(plan,null,2))
|
|
132
|
+
writeFileSync(join(packDir,'project','audio-manifest.json'),JSON.stringify([...new Map(manifest.map(item=>[item.referenceId,item])).values()],null,2))
|
|
133
|
+
return {status:'mixed',...analysis,listening:'not-performed',warnings:analysis.samplePeakDb===null||analysis.samplePeakDb<=-90?['成片音轨几乎静音,请试听检查']:[],
|
|
134
|
+
variants:{full:'source.mp4',picture:'picture.mp4',...(plan.cues.some(cue=>cue.role==='bgm')?{noBgm:'no-bgm.mp4'}:{})}}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// 两个音效入口共用 JS 下载器,避免 Python 分支的许可拦截与重复清单。
|
|
138
|
+
export async function sourceFreesound(action,args,directory,signal) {
|
|
139
|
+
signal?.throwIfAborted()
|
|
140
|
+
if(action==='search') {
|
|
141
|
+
const found=await searchSfx({...args,signal})
|
|
142
|
+
return {provider:'freesound',query:found.query,results:found.results.map(row=>({...row,name:row.title,duration:row.seconds,license_label:row.licenseLabel}))}
|
|
143
|
+
}
|
|
144
|
+
const result=await getSfx(directory,[args.id],{persist:false,signal})
|
|
145
|
+
signal?.throwIfAborted()
|
|
146
|
+
return {...result,results:result.results.map(row=>row.ok?{...row,asset:{...row.asset,file:'assets/'+row.asset.file}}:row)}
|
|
147
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/* 由 tools/build.mjs 生成 —— 请勿直接编辑;改 src/ 下的源文件。 */
|
|
2
|
+
import { randomUUID } from 'node:crypto'
|
|
3
|
+
import { mkdirSync, writeFileSync, appendFileSync, readFileSync, statSync, existsSync, rmSync } from 'node:fs'
|
|
4
|
+
import { join, dirname } from 'node:path'
|
|
5
|
+
import { importAudioFile, frozenAudioFile, sourceFreesound, audioScopeDirectory } from './audio-media.js'
|
|
6
|
+
import { audioNodeData } from './motion-core.js'
|
|
7
|
+
import { clipDirFor } from './media.js'
|
|
8
|
+
|
|
9
|
+
export function createAudioService() {
|
|
10
|
+
const uploads=new Map()
|
|
11
|
+
const cache=scope=>audioScopeDirectory(scope.drawioAbs,join(dirname(scope.drawioAbs),'.drawai-cache','audio'),true)
|
|
12
|
+
const owner=(scope,token)=>{
|
|
13
|
+
const job=uploads.get(token)
|
|
14
|
+
if(!job||job.sessionId!==scope.sessionId||job.drawioAbs!==scope.drawioAbs)throw new Error('音频上传令牌不属于当前画布或会话')
|
|
15
|
+
return job
|
|
16
|
+
}
|
|
17
|
+
return {
|
|
18
|
+
async search(scope,args,signal){return sourceFreesound('search',args,join(cache(scope),'search'),signal)},
|
|
19
|
+
async get(scope,args,signal){
|
|
20
|
+
const directory=join(cache(scope),'freesound-'+randomUUID())
|
|
21
|
+
try{
|
|
22
|
+
const result=await sourceFreesound('get',args,directory,signal)
|
|
23
|
+
const row=result.results?.find(item=>item.ok)?.asset
|
|
24
|
+
if(!row||!/^assets\/sfx\/fs-\d+\.mp3$/.test(row.file))throw new Error('未下载到可用音效')
|
|
25
|
+
return await importAudioFile(scope.drawioAbs,join(directory,row.file),{name:row.name||row.title||row.file,role:'sfx',provenance:row,signal})
|
|
26
|
+
}finally{rmSync(audioScopeDirectory(scope.drawioAbs,directory),{recursive:true,force:true})}
|
|
27
|
+
},
|
|
28
|
+
start(scope,args){
|
|
29
|
+
for(const [token,job]of uploads)if(!job.finishing&&Date.now()-job.time>3600000){rmSync(audioScopeDirectory(job.drawioAbs,job.directory),{recursive:true,force:true});uploads.delete(token)}
|
|
30
|
+
audioNodeData(args.role||'sfx')
|
|
31
|
+
if(uploads.size>=16)throw new Error('音频上传任务已满')
|
|
32
|
+
if(!Number.isInteger(args.size)||args.size<1||args.size>32*1024*1024||!['mp3','wav','m4a','aac','ogg','flac','webm'].includes(args.extension))throw new Error('请选择32MB以内的音频文件')
|
|
33
|
+
const token=randomUUID(),directory=join(cache(scope),'upload-'+token),file=join(directory,'source.'+args.extension)
|
|
34
|
+
mkdirSync(directory,{recursive:true});writeFileSync(file,'')
|
|
35
|
+
uploads.set(token,{...scope,directory,file,size:args.size,received:0,name:String(args.name||'音频').slice(0,240),role:args.role||'sfx',time:Date.now()})
|
|
36
|
+
return {token}
|
|
37
|
+
},
|
|
38
|
+
chunk(scope,args){
|
|
39
|
+
const job=owner(scope,args.token)
|
|
40
|
+
if(job.finished||job.finishing||args.offset!==job.received||typeof args.base64!=='string'||args.base64.length>1400000||/[^A-Za-z0-9+/=]/.test(args.base64))throw new Error('音频分块顺序或格式无效')
|
|
41
|
+
const bytes=Buffer.from(args.base64,'base64')
|
|
42
|
+
if(bytes.toString('base64')!==args.base64)throw new Error('音频分块编码无效')
|
|
43
|
+
if(bytes.length<1||bytes.length>1024*1024||job.received+bytes.length>job.size)throw new Error('音频分块超出范围')
|
|
44
|
+
audioScopeDirectory(scope.drawioAbs,job.file)
|
|
45
|
+
appendFileSync(job.file,bytes);job.received+=bytes.length;job.time=Date.now()
|
|
46
|
+
return {received:job.received}
|
|
47
|
+
},
|
|
48
|
+
async finish(scope,args,signal){
|
|
49
|
+
const job=owner(scope,args.token)
|
|
50
|
+
if(job.finished)return job.result
|
|
51
|
+
if(job.received!==job.size)throw new Error('音频尚未上传完整')
|
|
52
|
+
if(job.finishing)throw new Error('音频正在处理')
|
|
53
|
+
job.finishing=true
|
|
54
|
+
try{job.result=await importAudioFile(scope.drawioAbs,job.file,{name:job.name,role:job.role,signal});job.finished=true;return job.result}
|
|
55
|
+
finally{job.finishing=false}
|
|
56
|
+
},
|
|
57
|
+
release(scope,args){const job=owner(scope,args.token);if(job.finishing)throw new Error('音频仍在处理');rmSync(audioScopeDirectory(scope.drawioAbs,job.directory),{recursive:true,force:true});uploads.delete(args.token);return {}},
|
|
58
|
+
read(scope,clip){
|
|
59
|
+
const {manifest}=frozenAudioFile(scope.drawioAbs,clip),file=join(clipDirFor(scope.drawioAbs,clip),'preview.m4a')
|
|
60
|
+
audioScopeDirectory(scope.drawioAbs,file)
|
|
61
|
+
if(statSync(file).size>16*1024*1024)throw new Error('音频预览过大')
|
|
62
|
+
return {manifest,mime:'audio/mp4',base64:readFileSync(file).toString('base64')}
|
|
63
|
+
},
|
|
64
|
+
leases(scope){return [...uploads.values()].filter(job=>job.drawioAbs===scope.drawioAbs&&job.result).map(job=>job.result.clip)},
|
|
65
|
+
}
|
|
66
|
+
}
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
/* 由 tools/build.mjs 生成 —— 请勿直接编辑;改 src/ 下的源文件。 */
|
|
2
|
+
/** Native JSON document codec. XML is retained only as a compatibility capsule. */
|
|
3
|
+
import { randomUUID } from 'node:crypto'
|
|
4
|
+
import { existsSync, lstatSync, readdirSync, mkdirSync, copyFileSync, readFileSync, writeFileSync, renameSync, constants } from 'node:fs'
|
|
5
|
+
import { join, dirname } from 'node:path'
|
|
6
|
+
import { mediaRootFor } from './media.js'
|
|
7
|
+
import { parseMxfile, applyDocToMxfile, buildMxfile, contentHash } from './mxfile.js'
|
|
8
|
+
import { cloneDocument, normalizeDocument } from './document-core.js'
|
|
9
|
+
|
|
10
|
+
export const CANVAS_SCHEMA_VERSION = 1
|
|
11
|
+
export const isCanvasPath = (path) => typeof path === 'string' && /\.(drawai|drawio)$/i.test(path)
|
|
12
|
+
export const isNativeCanvasPath = (path) => typeof path === 'string' && /\.drawai$/i.test(path)
|
|
13
|
+
export const nativeCanvasPath = (path) => path.replace(/\.drawio$/i, '.drawai')
|
|
14
|
+
const nativeText = (text) => String(text).trimStart().startsWith('{')
|
|
15
|
+
|
|
16
|
+
function decode(text) {
|
|
17
|
+
const project = JSON.parse(text)
|
|
18
|
+
if (project.format !== 'dsh-drawai' || project.schemaVersion !== CANVAS_SCHEMA_VERSION) throw new Error('不支持的 DrawAI 文档版本')
|
|
19
|
+
if (typeof project.id !== 'string' || !Array.isArray(project.pages) || !project.pages.length) throw new Error('DrawAI 文档缺少项目或页面')
|
|
20
|
+
const ids = new Set()
|
|
21
|
+
for (const page of project.pages) {
|
|
22
|
+
if (!page || typeof page.id !== 'string' || ids.has(page.id) || !Array.isArray(page.doc?.nodes) || !Array.isArray(page.doc?.edges)) throw new Error('DrawAI 页面结构或身份无效')
|
|
23
|
+
ids.add(page.id)
|
|
24
|
+
}
|
|
25
|
+
return project
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function encode(project) { return JSON.stringify(project, null, 2) + '\n' }
|
|
29
|
+
function stored(doc) { const copy = cloneDocument(doc); delete copy.revision; return copy }
|
|
30
|
+
|
|
31
|
+
export function migrateMxfile(text, sourcePath = '') {
|
|
32
|
+
const first = parseMxfile(text)
|
|
33
|
+
const project = { format: 'dsh-drawai', schemaVersion: CANVAS_SCHEMA_VERSION, id: randomUUID(), pages: [],
|
|
34
|
+
compat: { drawio: text }, migration: { sourcePath, sourceHash: contentHash(text) } }
|
|
35
|
+
for (let i = 0; i < first.pageCount; i++) {
|
|
36
|
+
const page = parseMxfile(text, { page: i })
|
|
37
|
+
project.pages.push({ id: page.pageId || 'page-' + i, name: page.pageNames[i], doc: stored(page.doc), notes: page.notes })
|
|
38
|
+
}
|
|
39
|
+
const result = encode(project)
|
|
40
|
+
decode(result)
|
|
41
|
+
return result
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export function parseCanvas(text, options = {}) {
|
|
45
|
+
if (!nativeText(text)) return parseMxfile(text, options)
|
|
46
|
+
const project = decode(text)
|
|
47
|
+
const index = Math.max(0, Math.min(project.pages.length - 1, Math.floor(Number(options.page) || 0)))
|
|
48
|
+
const page = project.pages[index]
|
|
49
|
+
const doc = normalizeDocument(page.doc)
|
|
50
|
+
doc.revision = contentHash(text)
|
|
51
|
+
doc.meta = { ...doc.meta, pageIndex: index, pageCount: project.pages.length, pageNames: project.pages.map((item) => item.name) }
|
|
52
|
+
return { doc, notes: page.notes || [], pageCount: project.pages.length, pageIndex: index,
|
|
53
|
+
pageNames: project.pages.map((item) => item.name), pageId: page.id, pageIds: project.pages.map((item) => item.id) }
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export function applyCanvas(text, doc, options = {}) {
|
|
57
|
+
if (!nativeText(text)) return applyDocToMxfile(text, doc, options)
|
|
58
|
+
const project = decode(text)
|
|
59
|
+
const index = options.page === undefined ? 0 : Number(options.page)
|
|
60
|
+
if (!Number.isInteger(index) || !project.pages[index]) throw new Error('页面不存在')
|
|
61
|
+
project.pages[index].doc = stored(normalizeDocument(doc))
|
|
62
|
+
return { text: encode(project), dropped: [] }
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export function buildCanvas(doc, options = {}) {
|
|
66
|
+
if (options.format !== 'drawai') return buildMxfile(doc, options)
|
|
67
|
+
return { text: encode({ format: 'dsh-drawai', schemaVersion: CANVAS_SCHEMA_VERSION, id: randomUUID(),
|
|
68
|
+
pages: [{ id: randomUUID(), name: options.name || '第 1 页', doc: stored(normalizeDocument(doc)) }] }), dropped: [] }
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export function exportCanvasToMxfile(text) {
|
|
72
|
+
if (!nativeText(text)) return text
|
|
73
|
+
const project = decode(text)
|
|
74
|
+
if (!project.compat?.drawio) {
|
|
75
|
+
if (project.pages.length !== 1) throw new Error('缺少多页 drawio 兼容模板')
|
|
76
|
+
return buildMxfile(project.pages[0].doc, { name: project.pages[0].name }).text
|
|
77
|
+
}
|
|
78
|
+
let xml = project.compat.drawio
|
|
79
|
+
for (let page = 0; page < project.pages.length; page++) xml = applyDocToMxfile(xml, project.pages[page].doc, { page }).text
|
|
80
|
+
return xml
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export function forkNativeCanvas(text) {
|
|
84
|
+
const project = decode(text)
|
|
85
|
+
project.id = randomUUID()
|
|
86
|
+
delete project.migration
|
|
87
|
+
return encode(project)
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/** Runs through the host filesystem service, preserving its workspace/policy checks. */
|
|
91
|
+
export async function migrateCanvasFile({ fs, source, destination, sourcePath, destinationPath, policy }) {
|
|
92
|
+
const original = await fs.readText(source)
|
|
93
|
+
if (await fs.stat(destination)) {
|
|
94
|
+
const existing = await fs.readText(destination)
|
|
95
|
+
const project = decode(existing)
|
|
96
|
+
if (project.migration?.sourcePath !== sourcePath || project.migration.sourceHash !== contentHash(original)) throw new Error('迁移目标已存在或旧画布已更改;请先处理两个版本')
|
|
97
|
+
return { text: existing, reused: true }
|
|
98
|
+
}
|
|
99
|
+
const text = migrateMxfile(original, sourcePath)
|
|
100
|
+
if (destinationPath) copyMigrationMedia(sourcePath, destinationPath, contentHash(original))
|
|
101
|
+
if (await fs.readText(source) !== original) throw new Error('迁移期间源画布已更改,请重新打开')
|
|
102
|
+
if (await fs.stat(destination)) throw new Error('迁移目标已存在')
|
|
103
|
+
await fs.writeText(destination, text, undefined, undefined, policy)
|
|
104
|
+
return { text, reused: false }
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// Never share mutable media with the retained original. Publish the complete copy
|
|
108
|
+
// by directory rename; an unrelated existing destination is never overwritten.
|
|
109
|
+
function copyMigrationMedia(source, destination, sourceHash) {
|
|
110
|
+
const from = mediaRootFor(source), to = mediaRootFor(destination)
|
|
111
|
+
if (!existsSync(from)) return
|
|
112
|
+
const marker = '.drawai-migration.json'
|
|
113
|
+
if (existsSync(to)) {
|
|
114
|
+
if (lstatSync(to).isSymbolicLink()) throw new Error('迁移媒体目录不能为符号链接')
|
|
115
|
+
const saved = JSON.parse(readFileSync(join(to, marker), 'utf8'))
|
|
116
|
+
if (saved.source !== source || saved.sourceHash !== sourceHash) throw new Error('迁移媒体目录已存在')
|
|
117
|
+
return
|
|
118
|
+
}
|
|
119
|
+
const temporary = join(dirname(to), '.drawai-migration-' + randomUUID())
|
|
120
|
+
function copyDirectory(input, output) {
|
|
121
|
+
if (lstatSync(input).isSymbolicLink()) throw new Error('迁移媒体不支持符号链接')
|
|
122
|
+
mkdirSync(output)
|
|
123
|
+
for (const entry of readdirSync(input, { withFileTypes: true })) {
|
|
124
|
+
const src = join(input, entry.name), dst = join(output, entry.name)
|
|
125
|
+
if (entry.isSymbolicLink()) throw new Error('迁移媒体不支持符号链接')
|
|
126
|
+
if (entry.isDirectory()) copyDirectory(src, dst)
|
|
127
|
+
else copyFileSync(src, dst, constants.COPYFILE_EXCL)
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
copyDirectory(from, temporary)
|
|
131
|
+
writeFileSync(join(temporary, marker), JSON.stringify({ source, sourceHash }), { flag: 'wx' })
|
|
132
|
+
if (existsSync(to)) throw new Error('迁移媒体目录已存在')
|
|
133
|
+
renameSync(temporary, to)
|
|
134
|
+
}
|