├── .eslintrc.json ├── .github ├── CODEOWNERS ├── ISSUE_TEMPLATE │ ├── bug_report.md │ ├── documentation.md │ └── feature_request.md └── workflows │ ├── docs.yml │ ├── publish.yml │ └── verify.yml ├── .gitignore ├── .husky ├── commit-msg └── pre-commit ├── .npmrc ├── .prettierignore ├── .prettierrc ├── CHANGELOG.md ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── FUNDING.yml ├── LICENSE ├── README.md ├── SECURITY.md ├── commitlint.config.js ├── lerna.json ├── package-lock.json ├── package.json ├── packages ├── 2d │ ├── CHANGELOG.md │ ├── package.json │ ├── rollup.config.mjs │ ├── rollup.editor.mjs │ ├── src │ │ ├── editor │ │ │ ├── NodeInspectorConfig.tsx │ │ │ ├── PreviewOverlayConfig.tsx │ │ │ ├── Provider.tsx │ │ │ ├── SceneGraphTabConfig.tsx │ │ │ ├── icons │ │ │ │ ├── CircleIcon.tsx │ │ │ │ ├── CodeBlockIcon.tsx │ │ │ │ ├── CurveIcon.tsx │ │ │ │ ├── GridIcon.tsx │ │ │ │ ├── IconMap.ts │ │ │ │ ├── ImgIcon.tsx │ │ │ │ ├── LayoutIcon.tsx │ │ │ │ ├── LineIcon.tsx │ │ │ │ ├── NodeIcon.tsx │ │ │ │ ├── RayIcon.tsx │ │ │ │ ├── RectIcon.tsx │ │ │ │ ├── ShapeIcon.tsx │ │ │ │ ├── TxtIcon.tsx │ │ │ │ ├── VideoIcon.tsx │ │ │ │ └── View2DIcon.tsx │ │ │ ├── index.css │ │ │ ├── index.ts │ │ │ ├── shortcuts.ts │ │ │ ├── tree │ │ │ │ ├── DetachedRoot.tsx │ │ │ │ ├── NodeElement.tsx │ │ │ │ ├── TreeElement.tsx │ │ │ │ ├── TreeRoot.tsx │ │ │ │ ├── ViewRoot.tsx │ │ │ │ ├── index.module.scss │ │ │ │ ├── index.ts │ │ │ │ └── navigation.ts │ │ │ ├── tsconfig.build.json │ │ │ ├── tsconfig.json │ │ │ ├── tsdoc.json │ │ │ ├── utils │ │ │ │ ├── SignalSet.ts │ │ │ │ └── index.ts │ │ │ └── vite-env.d.ts │ │ ├── lib │ │ │ ├── code │ │ │ │ ├── CodeCursor.ts │ │ │ │ ├── CodeDiffer.ts │ │ │ │ ├── CodeFragment.ts │ │ │ │ ├── CodeHighlighter.ts │ │ │ │ ├── CodeMetrics.ts │ │ │ │ ├── CodeRange.test.ts │ │ │ │ ├── CodeRange.ts │ │ │ │ ├── CodeScope.ts │ │ │ │ ├── CodeSelection.ts │ │ │ │ ├── CodeSignal.ts │ │ │ │ ├── CodeTokenizer.ts │ │ │ │ ├── DefaultHighlightStyle.ts │ │ │ │ ├── LezerHighlighter.ts │ │ │ │ ├── diff.test.ts │ │ │ │ ├── diff.ts │ │ │ │ ├── extractRange.ts │ │ │ │ └── index.ts │ │ │ ├── components │ │ │ │ ├── Bezier.ts │ │ │ │ ├── Camera.ts │ │ │ │ ├── Circle.ts │ │ │ │ ├── Code.ts │ │ │ │ ├── CodeBlock.ts │ │ │ │ ├── CubicBezier.ts │ │ │ │ ├── Curve.ts │ │ │ │ ├── Grid.ts │ │ │ │ ├── Icon.ts │ │ │ │ ├── Img.ts │ │ │ │ ├── Knot.ts │ │ │ │ ├── Latex.ts │ │ │ │ ├── Layout.ts │ │ │ │ ├── Line.ts │ │ │ │ ├── Node.ts │ │ │ │ ├── Path.ts │ │ │ │ ├── Polygon.ts │ │ │ │ ├── QuadBezier.ts │ │ │ │ ├── Ray.ts │ │ │ │ ├── Rect.ts │ │ │ │ ├── SVG.ts │ │ │ │ ├── Shape.ts │ │ │ │ ├── Spline.ts │ │ │ │ ├── Txt.test.tsx │ │ │ │ ├── Txt.ts │ │ │ │ ├── TxtLeaf.ts │ │ │ │ ├── Video.ts │ │ │ │ ├── View2D.ts │ │ │ │ ├── __logs__ │ │ │ │ │ ├── image-without-source.md │ │ │ │ │ ├── line-without-points.md │ │ │ │ │ ├── reactive-playback-rate.md │ │ │ │ │ └── spline-with-insufficient-knots.md │ │ │ │ ├── __tests__ │ │ │ │ │ ├── children.test.tsx │ │ │ │ │ ├── clone.test.tsx │ │ │ │ │ ├── generatorTest.ts │ │ │ │ │ ├── mockScene2D.ts │ │ │ │ │ ├── query.test.tsx │ │ │ │ │ └── state.test.tsx │ │ │ │ ├── index.ts │ │ │ │ └── types.ts │ │ │ ├── curves │ │ │ │ ├── ArcSegment.ts │ │ │ │ ├── CircleSegment.ts │ │ │ │ ├── CubicBezierSegment.ts │ │ │ │ ├── CurveDrawingInfo.ts │ │ │ │ ├── CurvePoint.ts │ │ │ │ ├── CurveProfile.ts │ │ │ │ ├── KnotInfo.ts │ │ │ │ ├── LineSegment.ts │ │ │ │ ├── Polynomial.ts │ │ │ │ ├── Polynomial2D.ts │ │ │ │ ├── PolynomialSegment.ts │ │ │ │ ├── QuadBezierSegment.ts │ │ │ │ ├── Segment.ts │ │ │ │ ├── UniformPolynomialCurveSampler.ts │ │ │ │ ├── createCurveProfileLerp.ts │ │ │ │ ├── getBezierSplineProfile.ts │ │ │ │ ├── getCircleProfile.ts │ │ │ │ ├── getPathProfile.ts │ │ │ │ ├── getPointAtDistance.ts │ │ │ │ ├── getPolylineProfile.test.ts │ │ │ │ ├── getPolylineProfile.ts │ │ │ │ ├── getRectProfile.ts │ │ │ │ └── index.ts │ │ │ ├── decorators │ │ │ │ ├── canvasStyleSignal.ts │ │ │ │ ├── colorSignal.ts │ │ │ │ ├── compound.ts │ │ │ │ ├── computed.ts │ │ │ │ ├── defaultStyle.ts │ │ │ │ ├── filtersSignal.ts │ │ │ │ ├── index.ts │ │ │ │ ├── initializers.ts │ │ │ │ ├── nodeName.ts │ │ │ │ ├── signal.test.ts │ │ │ │ ├── signal.ts │ │ │ │ ├── spacingSignal.ts │ │ │ │ └── vector2Signal.ts │ │ │ ├── globals.d.ts │ │ │ ├── index.ts │ │ │ ├── jsx-dev-runtime.ts │ │ │ ├── jsx-runtime.ts │ │ │ ├── parse-svg-path.d.ts │ │ │ ├── partials │ │ │ │ ├── Filter.ts │ │ │ │ ├── Gradient.ts │ │ │ │ ├── Pattern.ts │ │ │ │ ├── ShaderConfig.ts │ │ │ │ ├── index.ts │ │ │ │ └── types.ts │ │ │ ├── scenes │ │ │ │ ├── Scene2D.ts │ │ │ │ ├── index.ts │ │ │ │ ├── makeScene2D.ts │ │ │ │ └── useScene2D.ts │ │ │ ├── tsconfig.build.json │ │ │ ├── tsconfig.json │ │ │ ├── tsdoc.json │ │ │ └── utils │ │ │ │ ├── CanvasUtils.ts │ │ │ │ ├── diff.test.ts │ │ │ │ ├── diff.ts │ │ │ │ ├── index.ts │ │ │ │ ├── is.ts │ │ │ │ ├── makeSignalExtensions.ts │ │ │ │ └── withDefaults.tsx │ │ ├── tsconfig.base.json │ │ ├── tsconfig.build.json │ │ └── tsconfig.json │ ├── tsconfig.project.json │ └── vitest.config.ts ├── core │ ├── CHANGELOG.md │ ├── package.json │ ├── project.d.ts │ ├── rollup.config.mjs │ ├── shaders │ │ ├── common.glsl │ │ └── fragment.glsl │ ├── src │ │ ├── app │ │ │ ├── Exporter.ts │ │ │ ├── ImageExporter.ts │ │ │ ├── Logger.ts │ │ │ ├── PlaybackManager.ts │ │ │ ├── PlaybackStatus.ts │ │ │ ├── Player.ts │ │ │ ├── Presenter.ts │ │ │ ├── Project.ts │ │ │ ├── ProjectMetadata.ts │ │ │ ├── Renderer.ts │ │ │ ├── SettingsMetadata.ts │ │ │ ├── SharedWebGLContext.ts │ │ │ ├── Stage.ts │ │ │ ├── TimeEstimator.ts │ │ │ ├── __logs__ │ │ │ │ └── include-without-preprocessor.md │ │ │ ├── bootstrap.ts │ │ │ ├── index.ts │ │ │ └── presets.ts │ │ ├── decorators │ │ │ ├── decorate.ts │ │ │ ├── deprecated.ts │ │ │ ├── index.ts │ │ │ ├── lazy.ts │ │ │ └── threadable.ts │ │ ├── events │ │ │ ├── AsyncEventDispatcher.ts │ │ │ ├── EventDispatcher.ts │ │ │ ├── EventDispatcherBase.ts │ │ │ ├── FlagDispatcher.ts │ │ │ ├── ValueDispatcher.ts │ │ │ └── index.ts │ │ ├── flow │ │ │ ├── __logs__ │ │ │ │ └── infinite-loop.md │ │ │ ├── all.ts │ │ │ ├── any.ts │ │ │ ├── chain.ts │ │ │ ├── delay.ts │ │ │ ├── every.ts │ │ │ ├── index.ts │ │ │ ├── loop.ts │ │ │ ├── loopFor.ts │ │ │ ├── loopUntil.ts │ │ │ ├── noop.ts │ │ │ ├── run.ts │ │ │ ├── scheduling.test.ts │ │ │ ├── scheduling.ts │ │ │ └── sequence.ts │ │ ├── globals.d.ts │ │ ├── index.ts │ │ ├── media │ │ │ ├── AudioData.ts │ │ │ ├── AudioManager.ts │ │ │ ├── AudioManagerPool.ts │ │ │ ├── AudioResourceManager.ts │ │ │ ├── index.ts │ │ │ └── loadImage.ts │ │ ├── meta │ │ │ ├── BoolMetaField.ts │ │ │ ├── ColorMetaField.ts │ │ │ ├── EnumMetaField.ts │ │ │ ├── ExporterMetaFile.ts │ │ │ ├── MetaField.ts │ │ │ ├── MetaFile.ts │ │ │ ├── MetaOption.ts │ │ │ ├── NumberMetaField.ts │ │ │ ├── ObjectMetaField.ts │ │ │ ├── RangeMetaField.ts │ │ │ ├── StringMetaField.ts │ │ │ ├── Vector2MetaField.ts │ │ │ └── index.ts │ │ ├── plugin │ │ │ ├── DefaultPlugin.ts │ │ │ ├── Plugin.ts │ │ │ ├── index.ts │ │ │ └── makePlugin.ts │ │ ├── scenes │ │ │ ├── GeneratorScene.ts │ │ │ ├── Inspectable.ts │ │ │ ├── LifecycleEvents.ts │ │ │ ├── Random.ts │ │ │ ├── Scene.ts │ │ │ ├── SceneMetadata.ts │ │ │ ├── SceneState.ts │ │ │ ├── Shaders.ts │ │ │ ├── Slides.ts │ │ │ ├── Sounds.ts │ │ │ ├── Threadable.ts │ │ │ ├── Variables.ts │ │ │ ├── index.ts │ │ │ └── timeEvents │ │ │ │ ├── EditableTimeEvents.ts │ │ │ │ ├── ReadOnlyTimeEvents.ts │ │ │ │ ├── SerializedTimeEvent.ts │ │ │ │ ├── TimeEvent.ts │ │ │ │ ├── TimeEvents.ts │ │ │ │ └── index.ts │ │ ├── signals │ │ │ ├── CompoundSignalContext.ts │ │ │ ├── ComputedContext.ts │ │ │ ├── DeferredEffectContext.ts │ │ │ ├── DependencyContext.ts │ │ │ ├── EffectContext.ts │ │ │ ├── SignalContext.ts │ │ │ ├── Vector2SignalContext.ts │ │ │ ├── createComputed.test.ts │ │ │ ├── createComputed.ts │ │ │ ├── createComputedAsync.test.ts │ │ │ ├── createComputedAsync.ts │ │ │ ├── createDeferredEffect.test.ts │ │ │ ├── createDeferredEffect.ts │ │ │ ├── createEffect.test.ts │ │ │ ├── createEffect.ts │ │ │ ├── createSignal.test.ts │ │ │ ├── createSignal.ts │ │ │ ├── index.ts │ │ │ ├── symbols.ts │ │ │ ├── types.ts │ │ │ └── utils.ts │ │ ├── threading │ │ │ ├── Thread.ts │ │ │ ├── ThreadGenerator.ts │ │ │ ├── __logs__ │ │ │ │ └── reused-generator.md │ │ │ ├── cancel.test.ts │ │ │ ├── cancel.ts │ │ │ ├── index.ts │ │ │ ├── join.test.ts │ │ │ ├── join.ts │ │ │ ├── names.ts │ │ │ ├── spawn.ts │ │ │ ├── threads.test.ts │ │ │ └── threads.ts │ │ ├── transitions │ │ │ ├── fadeTransition.ts │ │ │ ├── index.ts │ │ │ ├── slideTransition.ts │ │ │ ├── useTransition.ts │ │ │ ├── waitTransition.ts │ │ │ ├── zoomInTransition.ts │ │ │ └── zoomOutTransition.ts │ │ ├── tweening │ │ │ ├── index.ts │ │ │ ├── interpolationFunctions.test.ts │ │ │ ├── interpolationFunctions.ts │ │ │ ├── spring.test.ts │ │ │ ├── spring.ts │ │ │ ├── timingFunctions.ts │ │ │ ├── tween.test.ts │ │ │ └── tween.ts │ │ ├── types │ │ │ ├── BBox.test.ts │ │ │ ├── BBox.ts │ │ │ ├── Canvas.ts │ │ │ ├── Color.test.ts │ │ │ ├── Color.ts │ │ │ ├── Matrix.ts │ │ │ ├── Matrix2D.test.ts │ │ │ ├── Matrix2D.ts │ │ │ ├── Origin.ts │ │ │ ├── Spacing.test.ts │ │ │ ├── Spacing.ts │ │ │ ├── Type.ts │ │ │ ├── Vector.test.ts │ │ │ ├── Vector.ts │ │ │ └── index.ts │ │ ├── utils │ │ │ ├── DetailedError.ts │ │ │ ├── ExperimentalError.ts │ │ │ ├── Semaphore.ts │ │ │ ├── __logs__ │ │ │ │ └── experimental-features.md │ │ │ ├── beginSlide.ts │ │ │ ├── capitalize.ts │ │ │ ├── createRef.ts │ │ │ ├── createRefArray.ts │ │ │ ├── createRefMap.ts │ │ │ ├── debug.test.ts │ │ │ ├── debug.ts │ │ │ ├── deprecate.ts │ │ │ ├── errorToLog.ts │ │ │ ├── experimentalLog.ts │ │ │ ├── getContext.ts │ │ │ ├── index.ts │ │ │ ├── math.ts │ │ │ ├── proxyUtils.test.ts │ │ │ ├── proxyUtils.ts │ │ │ ├── range.test.ts │ │ │ ├── range.ts │ │ │ ├── useContext.ts │ │ │ ├── useDuration.ts │ │ │ ├── usePlayback.ts │ │ │ ├── useRandom.ts │ │ │ ├── useScene.ts │ │ │ ├── useThread.ts │ │ │ └── useTime.ts │ │ └── vite-env.d.ts │ ├── tsconfig.build.json │ ├── tsconfig.json │ ├── tsconfig.project.json │ ├── tsdoc.json │ ├── vitest.config.ts │ └── vitest.setup.ts ├── create │ ├── CHANGELOG.md │ ├── index.js │ ├── package.json │ ├── template-2d-js │ │ ├── gitignore │ │ ├── package.json │ │ ├── public │ │ │ └── .gitkeep │ │ └── src │ │ │ ├── project.js │ │ │ └── scenes │ │ │ └── example.jsx │ └── template-2d-ts │ │ ├── gitignore │ │ ├── package.json │ │ ├── public │ │ └── .gitkeep │ │ ├── src │ │ ├── motion-canvas.d.ts │ │ ├── project.ts │ │ └── scenes │ │ │ └── example.tsx │ │ └── tsconfig.json ├── docs │ ├── .gitignore │ ├── babel.config.js │ ├── blog │ │ ├── 2023-02-04-public-release.mdx │ │ ├── 2023-02-08-version-2.1.0.mdx │ │ ├── 2023-02-09-version-2.2.0.mdx │ │ ├── 2023-02-11-version-2.3.0.mdx │ │ ├── 2023-02-18-version-2.4.0.mdx │ │ ├── 2023-02-20-version-2.5.0.mdx │ │ ├── 2023-02-24-version-2.6.0.mdx │ │ ├── 2023-02-27-version-3.0.0.mdx │ │ ├── 2023-03-07-version-3.1.0.mdx │ │ ├── 2023-03-10-version-3.2.0.mdx │ │ ├── 2023-03-18-version-3.3.0.mdx │ │ ├── 2023-03-28-version-3.4.0.mdx │ │ ├── 2023-04-06-example-cubic.tsx │ │ ├── 2023-04-06-example-quadratic.tsx │ │ ├── 2023-04-06-version-3.5.0.mdx │ │ ├── 2023-05-08-cardinal.tsx │ │ ├── 2023-05-08-version-3.6.0.mdx │ │ ├── 2023-05-13-version-3.8.0.mdx │ │ ├── 2023-05-29-version-3.9.0.mdx │ │ ├── 2023-07-23-curve.tsx │ │ ├── 2023-07-23-path.tsx │ │ ├── 2023-07-23-version-3.10.0.mdx │ │ ├── 2023-10-14-skew.tsx │ │ ├── 2023-10-14-version-3.11.0.mdx │ │ ├── 2023-12-31-glob.tsx │ │ ├── 2023-12-31-line.tsx │ │ ├── 2023-12-31-spawner.tsx │ │ ├── 2023-12-31-txt.tsx │ │ ├── 2023-12-31-version-3.12.0.mdx │ │ ├── 2024-01-10-version-3.13.0-scene-graph.png │ │ ├── 2024-01-10-version-3.13.0-scene-graph.webp │ │ ├── 2024-01-10-version-3.13.0.mdx │ │ ├── 2024-02-04-shaders.tsx │ │ ├── 2024-02-04-version-3.14.0.mdx │ │ ├── 2024-03-21-code.tsx │ │ ├── 2024-03-21-defaults.tsx │ │ ├── 2024-03-21-loop.tsx │ │ ├── 2024-03-21-version-3.15.0.mdx │ │ ├── 2024-05-16-camera.tsx │ │ ├── 2024-05-16-effects.tsx │ │ ├── 2024-05-16-vectors.tsx │ │ ├── 2024-05-16-version-3.16.0.mdx │ │ ├── 2024-08-13-latex.tsx │ │ ├── 2024-08-13-version-3.17.0.mdx │ │ └── authors.yml │ ├── bundle.js │ ├── config │ │ └── codeTheme.js │ ├── docs │ │ ├── advanced │ │ │ ├── _category_.yml │ │ │ ├── code │ │ │ │ ├── filters-and-effects │ │ │ │ │ ├── destination-in-example.tsx │ │ │ │ │ ├── destination-out-example.tsx │ │ │ │ │ ├── filters-order.tsx │ │ │ │ │ ├── filters-preview.tsx │ │ │ │ │ ├── masking-visualized-source-in.tsx │ │ │ │ │ ├── source-in-example.tsx │ │ │ │ │ ├── source-out-example.tsx │ │ │ │ │ ├── xor-destination-in-example.tsx │ │ │ │ │ └── xor-example.tsx │ │ │ │ └── tsconfig.json │ │ │ ├── custom-font.mdx │ │ │ ├── experimental.mdx │ │ │ ├── filters-and-effects.mdx │ │ │ ├── plugins │ │ │ │ ├── editor.mdx │ │ │ │ ├── index.mdx │ │ │ │ ├── node.mdx │ │ │ │ └── runtime.mdx │ │ │ ├── project-variables.mdx │ │ │ ├── random.mdx │ │ │ ├── shaders.mdx │ │ │ └── spawners.mdx │ │ ├── components │ │ │ ├── _category_.yml │ │ │ ├── bezier.mdx │ │ │ ├── code-block.mdx │ │ │ ├── code │ │ │ │ ├── grammars.json │ │ │ │ ├── grammars.mdx │ │ │ │ └── index.mdx │ │ │ ├── custom-components.mdx │ │ │ ├── latex.mdx │ │ │ ├── path.mdx │ │ │ └── spline.mdx │ │ ├── getting-started │ │ │ ├── _category_.yml │ │ │ ├── camera.mdx │ │ │ ├── configuration.mdx │ │ │ ├── effects.mdx │ │ │ ├── flow.mdx │ │ │ ├── hierarchy.mdx │ │ │ ├── layouts.mdx │ │ │ ├── logging.mdx │ │ │ ├── media.mdx │ │ │ ├── positioning.mdx │ │ │ ├── presentation.mdx │ │ │ ├── quickstart.mdx │ │ │ ├── references.mdx │ │ │ ├── rendering │ │ │ │ ├── image-sequence.mdx │ │ │ │ ├── index.mdx │ │ │ │ └── video.mdx │ │ │ ├── signals.mdx │ │ │ ├── time-events.mdx │ │ │ ├── transitions.mdx │ │ │ └── tweening.mdx │ │ ├── intro.md │ │ └── migration │ │ │ ├── 3.0.0.mdx │ │ │ ├── _category_.yml │ │ │ └── updating.mdx │ ├── docusaurus.config.js │ ├── editor.js │ ├── package.json │ ├── release-blog-generator.js │ ├── sidebars.js │ ├── src │ │ ├── Icon │ │ │ ├── Code │ │ │ │ └── index.tsx │ │ │ ├── Discord │ │ │ │ └── index.tsx │ │ │ ├── ExternalLink │ │ │ │ └── index.tsx │ │ │ ├── Filters │ │ │ │ └── index.tsx │ │ │ ├── GitHub │ │ │ │ └── index.tsx │ │ │ ├── Image │ │ │ │ └── index.tsx │ │ │ ├── Pause │ │ │ │ └── index.tsx │ │ │ ├── PhotoCamera │ │ │ │ └── index.tsx │ │ │ ├── PlayArrow │ │ │ │ └── index.tsx │ │ │ ├── Science │ │ │ │ └── index.tsx │ │ │ ├── SkipNext │ │ │ │ └── index.tsx │ │ │ ├── SkipPrevious │ │ │ │ └── index.tsx │ │ │ ├── Split │ │ │ │ └── index.tsx │ │ │ ├── Text │ │ │ │ └── index.tsx │ │ │ └── VideoSettings │ │ │ │ └── index.tsx │ │ ├── components │ │ │ ├── AnimationPlayer │ │ │ │ ├── AnimationLink.tsx │ │ │ │ ├── index.tsx │ │ │ │ └── styles.module.css │ │ │ ├── Api │ │ │ │ ├── ApiItem.tsx │ │ │ │ ├── ApiPage.tsx │ │ │ │ ├── ApiSnippet.tsx │ │ │ │ ├── Code │ │ │ │ │ ├── CodeBlock.tsx │ │ │ │ │ ├── Container.tsx │ │ │ │ │ ├── Line.tsx │ │ │ │ │ ├── Token.tsx │ │ │ │ │ ├── TokenList.tsx │ │ │ │ │ └── styles.module.css │ │ │ │ ├── Comment │ │ │ │ │ ├── Summary.tsx │ │ │ │ │ ├── index.tsx │ │ │ │ │ └── styles.module.css │ │ │ │ ├── Filters │ │ │ │ │ ├── Controls.tsx │ │ │ │ │ ├── index.module.css │ │ │ │ │ └── index.tsx │ │ │ │ ├── Group │ │ │ │ │ ├── Category.tsx │ │ │ │ │ └── index.tsx │ │ │ │ ├── Item │ │ │ │ │ ├── ClassItem.tsx │ │ │ │ │ ├── FunctionItem.tsx │ │ │ │ │ ├── ModuleItem.tsx │ │ │ │ │ ├── ProjectItem.tsx │ │ │ │ │ ├── PropertyItem.tsx │ │ │ │ │ ├── TypeAliasItem.tsx │ │ │ │ │ ├── index.tsx │ │ │ │ │ └── styles.module.css │ │ │ │ ├── Parameters │ │ │ │ │ └── index.tsx │ │ │ │ ├── Preview │ │ │ │ │ ├── ClassPreview.tsx │ │ │ │ │ ├── FlagsPreview.tsx │ │ │ │ │ ├── FunctionPreview.tsx │ │ │ │ │ ├── ParameterPreview.tsx │ │ │ │ │ ├── PropertyPreview.tsx │ │ │ │ │ ├── SignaturePreview.tsx │ │ │ │ │ ├── TypeAliasPreview.tsx │ │ │ │ │ ├── TypeLiteralPreview.tsx │ │ │ │ │ ├── TypeParameterPreview.tsx │ │ │ │ │ └── index.tsx │ │ │ │ ├── ReflectionKind.ts │ │ │ │ ├── Signatures │ │ │ │ │ └── index.tsx │ │ │ │ ├── Type │ │ │ │ │ ├── ArrayType.tsx │ │ │ │ │ ├── ConditionalType.tsx │ │ │ │ │ ├── IndexedAccessType.tsx │ │ │ │ │ ├── InferredType.tsx │ │ │ │ │ ├── IntersectionType.tsx │ │ │ │ │ ├── IntrinsicType.tsx │ │ │ │ │ ├── LiteralType.tsx │ │ │ │ │ ├── MappedType.tsx │ │ │ │ │ ├── NamedTupleMemberType.tsx │ │ │ │ │ ├── PredicateType.tsx │ │ │ │ │ ├── QueryType.tsx │ │ │ │ │ ├── ReferenceType.tsx │ │ │ │ │ ├── ReflectionType.tsx │ │ │ │ │ ├── TemplateLiteralType.tsx │ │ │ │ │ ├── TupleType.tsx │ │ │ │ │ ├── TypeOperatorType.tsx │ │ │ │ │ ├── UnionType.tsx │ │ │ │ │ └── index.tsx │ │ │ │ └── TypeParameters │ │ │ │ │ └── index.tsx │ │ │ ├── DocPage │ │ │ │ └── index.tsx │ │ │ ├── Dropdown │ │ │ │ └── index.tsx │ │ │ ├── ExperimentalWarning │ │ │ │ └── index.tsx │ │ │ ├── Fiddle │ │ │ │ ├── FiddleCodeBlock.tsx │ │ │ │ ├── SharedPlayer.ts │ │ │ │ ├── autocomplete.ts │ │ │ │ ├── errorHighlighting.ts │ │ │ │ ├── folding.ts │ │ │ │ ├── index.tsx │ │ │ │ ├── parseFiddle.ts │ │ │ │ ├── styles.module.css │ │ │ │ ├── themes.ts │ │ │ │ └── transformer.ts │ │ │ ├── Homepage │ │ │ │ ├── Features.tsx │ │ │ │ ├── Header.tsx │ │ │ │ └── styles.module.css │ │ │ ├── NavbarLink │ │ │ │ ├── Discord.tsx │ │ │ │ ├── GitHub.tsx │ │ │ │ ├── Link.tsx │ │ │ │ └── styles.module.css │ │ │ ├── Release │ │ │ │ ├── Contributor.tsx │ │ │ │ ├── Issue.tsx │ │ │ │ ├── IssueGroup.tsx │ │ │ │ ├── PullRequest.tsx │ │ │ │ └── styles.module.css │ │ │ ├── Tooltip │ │ │ │ ├── index.tsx │ │ │ │ └── styles.module.css │ │ │ ├── UI │ │ │ │ ├── index.tsx │ │ │ │ └── styles.module.css │ │ │ └── YouTubeVideo │ │ │ │ ├── index.tsx │ │ │ │ └── styles.module.css │ │ ├── contexts │ │ │ ├── api.tsx │ │ │ ├── codeTheme.tsx │ │ │ └── filters.tsx │ │ ├── css │ │ │ └── custom.css │ │ ├── pages │ │ │ └── index.tsx │ │ ├── theme │ │ │ ├── Admonition │ │ │ │ ├── index.tsx │ │ │ │ └── styles.module.css │ │ │ ├── Icon │ │ │ │ ├── Arrow │ │ │ │ │ └── index.tsx │ │ │ │ ├── Close │ │ │ │ │ └── index.tsx │ │ │ │ ├── Danger │ │ │ │ │ └── index.tsx │ │ │ │ ├── DarkMode │ │ │ │ │ └── index.tsx │ │ │ │ ├── Edit │ │ │ │ │ ├── index.tsx │ │ │ │ │ └── styles.module.css │ │ │ │ ├── ExternalLink │ │ │ │ │ ├── index.tsx │ │ │ │ │ └── styles.module.css │ │ │ │ ├── Home │ │ │ │ │ └── index.tsx │ │ │ │ ├── Info │ │ │ │ │ └── index.tsx │ │ │ │ ├── Language │ │ │ │ │ └── index.tsx │ │ │ │ ├── LightBulb │ │ │ │ │ └── index.tsx │ │ │ │ ├── LightMode │ │ │ │ │ └── index.tsx │ │ │ │ ├── Menu │ │ │ │ │ └── index.tsx │ │ │ │ └── Warning │ │ │ │ │ └── index.tsx │ │ │ ├── MDXComponents.js │ │ │ ├── MDXComponents │ │ │ │ ├── Details.module.css │ │ │ │ └── Details.tsx │ │ │ └── Navbar │ │ │ │ └── Content │ │ │ │ ├── index.tsx │ │ │ │ └── styles.module.css │ │ └── utils │ │ │ ├── useStorage.ts │ │ │ └── useSubscribable.ts │ ├── static │ │ ├── .nojekyll │ │ ├── CNAME │ │ ├── google2548701e78413b2d.html │ │ └── img │ │ │ ├── banner.png │ │ │ ├── discord-icon-dark.svg │ │ │ ├── discord-icon.svg │ │ │ ├── favicon.svg │ │ │ ├── github-icon-dark.svg │ │ │ ├── github-icon.svg │ │ │ ├── logo.svg │ │ │ ├── logo_dark.svg │ │ │ ├── logpayload-message.png │ │ │ ├── media │ │ │ ├── editor-audio-track-delayed.png │ │ │ ├── editor-audio-track.png │ │ │ └── spline.svg │ │ │ ├── rendering_icon.svg │ │ │ └── ui.svg │ ├── tsconfig.json │ ├── tsdoc.json │ ├── typedoc-standalone.js │ └── typedoc.js ├── e2e │ ├── .gitignore │ ├── package.json │ ├── src │ │ ├── __image_snapshots__ │ │ │ ├── circle.png │ │ │ └── rect.png │ │ ├── app.ts │ │ ├── global.d.ts │ │ └── rendering.test.ts │ ├── tests │ │ ├── motion-canvas.d.ts │ │ ├── project.meta │ │ ├── project.ts │ │ └── scenes │ │ │ ├── circle.meta │ │ │ ├── circle.tsx │ │ │ ├── rect.meta │ │ │ └── rect.tsx │ ├── tsconfig.json │ └── vite.config.ts ├── examples │ ├── assets │ │ ├── example.mp4 │ │ └── logo.svg │ ├── package.json │ ├── src │ │ ├── code-block.meta │ │ ├── code-block.ts │ │ ├── code.meta │ │ ├── code.ts │ │ ├── components.meta │ │ ├── components.ts │ │ ├── components │ │ │ └── Switch.tsx │ │ ├── layout-group.meta │ │ ├── layout-group.ts │ │ ├── layout.meta │ │ ├── layout.ts │ │ ├── logging.meta │ │ ├── logging.ts │ │ ├── media-image.meta │ │ ├── media-image.ts │ │ ├── media-video.meta │ │ ├── media-video.ts │ │ ├── motion-canvas.d.ts │ │ ├── node-signal.meta │ │ ├── node-signal.ts │ │ ├── positioning.meta │ │ ├── positioning.ts │ │ ├── presentation.meta │ │ ├── presentation.ts │ │ ├── quickstart.meta │ │ ├── quickstart.ts │ │ ├── random.meta │ │ ├── random.ts │ │ ├── scenes │ │ │ ├── code-block.meta │ │ │ ├── code-block.tsx │ │ │ ├── code.meta │ │ │ ├── code.tsx │ │ │ ├── components.meta │ │ │ ├── components.tsx │ │ │ ├── layout-group.meta │ │ │ ├── layout-group.tsx │ │ │ ├── layout.meta │ │ │ ├── layout.tsx │ │ │ ├── logging.meta │ │ │ ├── logging.tsx │ │ │ ├── media-image.meta │ │ │ ├── media-image.tsx │ │ │ ├── media-video.meta │ │ │ ├── media-video.tsx │ │ │ ├── node-signal.meta │ │ │ ├── node-signal.tsx │ │ │ ├── positioning.meta │ │ │ ├── positioning.tsx │ │ │ ├── presentation.meta │ │ │ ├── presentation.tsx │ │ │ ├── quickstart.meta │ │ │ ├── quickstart.tsx │ │ │ ├── random.meta │ │ │ ├── random.tsx │ │ │ ├── tex.meta │ │ │ ├── tex.tsx │ │ │ ├── transitions-first.meta │ │ │ ├── transitions-first.tsx │ │ │ ├── transitions-second.meta │ │ │ ├── transitions-second.tsx │ │ │ ├── tweening-color.meta │ │ │ ├── tweening-color.tsx │ │ │ ├── tweening-cubic.meta │ │ │ ├── tweening-cubic.tsx │ │ │ ├── tweening-linear.meta │ │ │ ├── tweening-linear.tsx │ │ │ ├── tweening-save-restore.meta │ │ │ ├── tweening-save-restore.tsx │ │ │ ├── tweening-spring.meta │ │ │ ├── tweening-spring.tsx │ │ │ ├── tweening-vector.meta │ │ │ ├── tweening-vector.tsx │ │ │ ├── tweening-visualiser.meta │ │ │ └── tweening-visualiser.tsx │ │ ├── tex.meta │ │ ├── tex.ts │ │ ├── transitions.meta │ │ ├── transitions.ts │ │ ├── tweening-color.meta │ │ ├── tweening-color.ts │ │ ├── tweening-cubic.meta │ │ ├── tweening-cubic.ts │ │ ├── tweening-linear.meta │ │ ├── tweening-linear.ts │ │ ├── tweening-save-restore.meta │ │ ├── tweening-save-restore.ts │ │ ├── tweening-spring.meta │ │ ├── tweening-spring.ts │ │ ├── tweening-vector.meta │ │ ├── tweening-vector.ts │ │ ├── tweening-visualiser.meta │ │ └── tweening-visualiser.ts │ ├── tsconfig.json │ └── vite.config.ts ├── ffmpeg │ ├── CHANGELOG.md │ ├── client │ │ ├── FFmpegExporterClient.ts │ │ ├── global.d.ts │ │ ├── index.ts │ │ └── tsconfig.json │ ├── package.json │ ├── server │ │ ├── FFmpegBridge.ts │ │ ├── FFmpegExporterServer.ts │ │ ├── ImageStream.ts │ │ ├── global.d.ts │ │ ├── index.ts │ │ └── tsconfig.json │ └── tsdoc.json ├── internal │ ├── common │ │ └── marked.js │ ├── index.d.ts │ ├── package.json │ ├── rollup │ │ └── typescript.mjs │ ├── transformers │ │ └── markdown-literals.js │ └── vite │ │ └── markdown-literals.js ├── player │ ├── CHANGELOG.md │ ├── index.html │ ├── package.json │ ├── src │ │ ├── global.d.ts │ │ ├── main.ts │ │ ├── styles.scss │ │ └── template.html │ ├── tsconfig.json │ ├── tsdoc.json │ ├── types │ │ └── main.d.ts │ └── vite.config.ts ├── template │ ├── package.json │ ├── src │ │ ├── motion-canvas.d.ts │ │ ├── project.meta │ │ ├── project.ts │ │ └── scenes │ │ │ ├── example.meta │ │ │ └── example.tsx │ ├── tsconfig.json │ ├── tsdoc.json │ └── vite.config.ts ├── ui │ ├── CHANGELOG.md │ ├── editor.html │ ├── index.html │ ├── package.json │ ├── project.html │ ├── src │ │ ├── Editor.module.scss │ │ ├── Editor.tsx │ │ ├── Index.module.scss │ │ ├── ProjectSelection.tsx │ │ ├── components │ │ │ ├── animations │ │ │ │ ├── borderHighlight.ts │ │ │ │ ├── emphasize.ts │ │ │ │ ├── highlight.ts │ │ │ │ ├── index.ts │ │ │ │ └── shake.ts │ │ │ ├── console │ │ │ │ ├── Console.module.scss │ │ │ │ ├── Console.tsx │ │ │ │ ├── Log.tsx │ │ │ │ ├── SourceCodeFrame.tsx │ │ │ │ ├── StackTrace.tsx │ │ │ │ └── index.ts │ │ │ ├── controls │ │ │ │ ├── Button.tsx │ │ │ │ ├── ButtonCheckbox.tsx │ │ │ │ ├── ButtonSelect.tsx │ │ │ │ ├── Checkbox.tsx │ │ │ │ ├── ColorInput.tsx │ │ │ │ ├── ColorPicker.tsx │ │ │ │ ├── ColorPreview.tsx │ │ │ │ ├── Controls.module.scss │ │ │ │ ├── Group.tsx │ │ │ │ ├── IconButton.tsx │ │ │ │ ├── IconCheckbox.tsx │ │ │ │ ├── Input.tsx │ │ │ │ ├── InputSelect.tsx │ │ │ │ ├── Label.tsx │ │ │ │ ├── NumberInput.tsx │ │ │ │ ├── NumberInputSelect.tsx │ │ │ │ ├── Pill.tsx │ │ │ │ ├── ReadOnlyInput.tsx │ │ │ │ ├── Select.tsx │ │ │ │ ├── Separator.tsx │ │ │ │ ├── Slider.tsx │ │ │ │ ├── Toggle.tsx │ │ │ │ └── index.ts │ │ │ ├── fields │ │ │ │ ├── ArrayField.tsx │ │ │ │ ├── AutoField.tsx │ │ │ │ ├── ColorField.tsx │ │ │ │ ├── Expandable.module.scss │ │ │ │ ├── Expandable.tsx │ │ │ │ ├── Layout.module.scss │ │ │ │ ├── Layout.tsx │ │ │ │ ├── NumberField.tsx │ │ │ │ ├── SpacingField.tsx │ │ │ │ ├── UnknownField.tsx │ │ │ │ ├── Vector2Field.tsx │ │ │ │ └── index.ts │ │ │ ├── footer │ │ │ │ ├── Footer.module.scss │ │ │ │ ├── Footer.tsx │ │ │ │ ├── Versions.module.scss │ │ │ │ ├── Versions.tsx │ │ │ │ └── index.ts │ │ │ ├── icons │ │ │ │ ├── AccountTree.tsx │ │ │ │ ├── Add.tsx │ │ │ │ ├── Bug.tsx │ │ │ │ ├── ChevronRight.tsx │ │ │ │ ├── Clear.tsx │ │ │ │ ├── Close.tsx │ │ │ │ ├── Colorize.tsx │ │ │ │ ├── DragIndicator.tsx │ │ │ │ ├── FastForward.tsx │ │ │ │ ├── FastRewind.tsx │ │ │ │ ├── Fullscreen.tsx │ │ │ │ ├── Grid.tsx │ │ │ │ ├── HourglassBottom.tsx │ │ │ │ ├── Locate.tsx │ │ │ │ ├── MotionCanvas.tsx │ │ │ │ ├── Movie.tsx │ │ │ │ ├── OpenInNew.tsx │ │ │ │ ├── Pause.tsx │ │ │ │ ├── PhotoCamera.tsx │ │ │ │ ├── PlayArrow.tsx │ │ │ │ ├── Recenter.tsx │ │ │ │ ├── Repeat.tsx │ │ │ │ ├── Schedule.tsx │ │ │ │ ├── School.tsx │ │ │ │ ├── Science.tsx │ │ │ │ ├── Settings.tsx │ │ │ │ ├── SkipNext.tsx │ │ │ │ ├── SkipPrevious.tsx │ │ │ │ ├── Tune.tsx │ │ │ │ ├── UnfoldMore.tsx │ │ │ │ ├── VideoSettings.tsx │ │ │ │ ├── Videocam.tsx │ │ │ │ ├── VolumeOff.tsx │ │ │ │ ├── VolumeOn.tsx │ │ │ │ ├── Warning.tsx │ │ │ │ └── index.ts │ │ │ ├── layout │ │ │ │ ├── Collapse.module.scss │ │ │ │ ├── Collapse.tsx │ │ │ │ ├── ElementSwitch.tsx │ │ │ │ ├── Header.module.scss │ │ │ │ ├── Header.tsx │ │ │ │ ├── Navigation.module.scss │ │ │ │ ├── Navigation.tsx │ │ │ │ ├── ResizeableLayout.module.scss │ │ │ │ ├── ResizeableLayout.tsx │ │ │ │ └── index.ts │ │ │ ├── meta │ │ │ │ ├── BoolMetaFieldView.tsx │ │ │ │ ├── ColorMetaFieldView.tsx │ │ │ │ ├── EnumMetaFieldView.tsx │ │ │ │ ├── MetaFieldGroup.tsx │ │ │ │ ├── MetaFieldView.tsx │ │ │ │ ├── NumberMetaFieldView.tsx │ │ │ │ ├── ObjectMetaFieldView.tsx │ │ │ │ ├── RangeMetaFieldView.tsx │ │ │ │ ├── StringMetaFieldView.tsx │ │ │ │ ├── UnknownMetaFieldView.tsx │ │ │ │ ├── Vector2MetaFieldView.tsx │ │ │ │ └── index.ts │ │ │ ├── playback │ │ │ │ ├── CurrentTime.tsx │ │ │ │ ├── Framerate.tsx │ │ │ │ ├── Playback.module.scss │ │ │ │ ├── PlaybackControls.tsx │ │ │ │ ├── PlaybackProgress.tsx │ │ │ │ └── index.ts │ │ │ ├── presentation │ │ │ │ ├── PresentationControls.module.scss │ │ │ │ ├── PresentationControls.tsx │ │ │ │ ├── PresentationMode.tsx │ │ │ │ ├── SceneGroup.tsx │ │ │ │ ├── SlideElement.tsx │ │ │ │ ├── SlideGraph.module.scss │ │ │ │ ├── SlideGraph.tsx │ │ │ │ └── index.ts │ │ │ ├── sidebar │ │ │ │ ├── Settings.tsx │ │ │ │ ├── Sidebar.module.scss │ │ │ │ ├── Threads.tsx │ │ │ │ ├── VideoSettings.tsx │ │ │ │ └── index.ts │ │ │ ├── tabs │ │ │ │ ├── Badge.tsx │ │ │ │ ├── Pane.tsx │ │ │ │ ├── Tabs.module.scss │ │ │ │ ├── Tabs.tsx │ │ │ │ └── index.ts │ │ │ ├── timeline │ │ │ │ ├── AudioTrack.tsx │ │ │ │ ├── Label.tsx │ │ │ │ ├── LabelGroup.tsx │ │ │ │ ├── LabelTrack.tsx │ │ │ │ ├── Playhead.tsx │ │ │ │ ├── RangeSelector.tsx │ │ │ │ ├── SceneTrack.tsx │ │ │ │ ├── SlideTrack.tsx │ │ │ │ ├── Timeline.module.scss │ │ │ │ ├── Timeline.tsx │ │ │ │ ├── Timestamps.tsx │ │ │ │ └── index.ts │ │ │ └── viewport │ │ │ │ ├── ColorPicker.tsx │ │ │ │ ├── Coordinates.tsx │ │ │ │ ├── EditorPreview.tsx │ │ │ │ ├── Inspector.tsx │ │ │ │ ├── OverlayCanvas.tsx │ │ │ │ ├── PreviewStage.tsx │ │ │ │ ├── StageView.tsx │ │ │ │ ├── Timestamp.tsx │ │ │ │ ├── Viewport.module.scss │ │ │ │ ├── Viewport.tsx │ │ │ │ └── index.ts │ │ ├── contexts │ │ │ ├── application.tsx │ │ │ ├── index.ts │ │ │ ├── panels.tsx │ │ │ ├── shortcuts.tsx │ │ │ ├── timeline.tsx │ │ │ └── viewport.ts │ │ ├── global.d.ts │ │ ├── hooks │ │ │ ├── index.ts │ │ │ ├── useClickOutside.ts │ │ │ ├── useCurrentFrame.ts │ │ │ ├── useDuration.ts │ │ │ ├── useFormattedNumber.tsx │ │ │ ├── useHover.ts │ │ │ ├── usePlayerState.ts │ │ │ ├── usePlayerTime.ts │ │ │ ├── usePresenterState.ts │ │ │ ├── useReducedMotion.ts │ │ │ ├── useRendererState.ts │ │ │ ├── useScenes.ts │ │ │ ├── useSettings.ts │ │ │ ├── useSize.tsx │ │ │ ├── useStateChange.ts │ │ │ ├── useStorage.ts │ │ │ ├── useSubscribable.ts │ │ │ └── useViewportMatrix.ts │ │ ├── img │ │ │ ├── checkmark.svg │ │ │ ├── dropdown-light.svg │ │ │ ├── dropdown.svg │ │ │ └── grid.svg │ │ ├── index.scss │ │ ├── main.tsx │ │ ├── plugin │ │ │ ├── EditorPlugin.ts │ │ │ ├── GridPlugin │ │ │ │ └── index.ts │ │ │ ├── OverlayWrapper.tsx │ │ │ ├── index.module.scss │ │ │ ├── index.ts │ │ │ └── makeEditorPlugin.ts │ │ ├── signals │ │ │ ├── EditorPanel.ts │ │ │ ├── index.ts │ │ │ ├── labelClip.ts │ │ │ ├── projectName.ts │ │ │ └── storedSignal.ts │ │ ├── utils │ │ │ ├── LoggerManager.ts │ │ │ ├── clamp.ts │ │ │ ├── compareVersions.ts │ │ │ ├── formatDuration.ts │ │ │ ├── index.ts │ │ │ ├── localStorage.ts │ │ │ ├── mouse.ts │ │ │ ├── openOutputPath.ts │ │ │ ├── sourceMaps.ts │ │ │ └── withLoader.ts │ │ └── vite-env.d.ts │ ├── tsconfig.json │ ├── tsconfig.plugin.json │ ├── tsdoc.json │ ├── types │ │ └── main.d.ts │ ├── vite.config.ts │ └── vite.showcase.ts └── vite-plugin │ ├── CHANGELOG.md │ ├── package.json │ ├── src │ ├── globals.d.ts │ ├── index.ts │ ├── main.ts │ ├── openInExplorer.ts │ ├── partials │ │ ├── assets.ts │ │ ├── corsProxy.ts │ │ ├── editor.ts │ │ ├── exporter.ts │ │ ├── index.ts │ │ ├── meta.ts │ │ ├── projects.ts │ │ ├── scenes.ts │ │ ├── settings.ts │ │ └── webgl.ts │ ├── plugins.ts │ ├── utils.ts │ └── versions.ts │ ├── tsconfig.json │ └── tsdoc.json └── tsdoc.json /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | * @aarthificial 2 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/documentation.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Documentation 3 | about: Report an issue or suggest improvements for documentation 4 | title: '' 5 | labels: b-documentation 6 | assignees: aarthificial 7 | --- 8 | 9 | **Description** 10 | A clear and concise description of what the issue is. 11 | How can it be improved? 12 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | 3 | /packages/*/dist 4 | /packages/*/editor 5 | /packages/*/lib 6 | /packages/*/static 7 | /packages/*/build 8 | /packages/*/output 9 | 10 | .idea 11 | .nx/cache 12 | .nx/workspace-data 13 | -------------------------------------------------------------------------------- /.husky/commit-msg: -------------------------------------------------------------------------------- 1 | npx --no -- commitlint --edit $1 2 | -------------------------------------------------------------------------------- /.husky/pre-commit: -------------------------------------------------------------------------------- 1 | npx lint-staged 2 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | access = public -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | .git 2 | .docusaurus 3 | .github 4 | node_modules 5 | package-lock.json 6 | CHANGELOG.md 7 | 8 | /packages/*/dist 9 | /packages/*/editor 10 | /packages/*/lib 11 | /packages/*/static 12 | /packages/*/build 13 | 14 | /packages/docs/src/generated 15 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "bracketSpacing": false, 3 | "singleQuote": true, 4 | "printWidth": 80, 5 | "trailingComma": "all", 6 | "arrowParens": "avoid", 7 | "proseWrap": "always", 8 | "plugins": ["prettier-plugin-organize-imports"] 9 | } 10 | -------------------------------------------------------------------------------- /FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: [aarthificial] 4 | patreon: aarthificial 5 | -------------------------------------------------------------------------------- /commitlint.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | extends: ['@commitlint/config-conventional'], 3 | ignores: [commit => commit.includes('[skip ci]')], 4 | rules: { 5 | 'scope-enum': [ 6 | 2, 7 | 'always', 8 | [ 9 | '2d', 10 | 'core', 11 | 'create', 12 | 'docs', 13 | 'e2e', 14 | 'examples', 15 | 'ffmpeg', 16 | 'legacy', 17 | 'player', 18 | 'ui', 19 | 'vite-plugin', 20 | ], 21 | ], 22 | }, 23 | }; 24 | -------------------------------------------------------------------------------- /lerna.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "node_modules/lerna/schemas/lerna-schema.json", 3 | "private": false, 4 | "version": "3.17.2", 5 | "ignoreChanges": ["**/*.test.ts", "**/*.md"], 6 | "message": "ci(release): %v [skip ci]", 7 | "conventionalCommits": true, 8 | "createRelease": "github" 9 | } 10 | -------------------------------------------------------------------------------- /packages/2d/src/editor/icons/CircleIcon.tsx: -------------------------------------------------------------------------------- 1 | export function CircleIcon() { 2 | return ( 3 | <svg viewBox="0 0 20 20" fill="currentColor"> 4 | <path d="M10,5C12.76,5 15,7.24 15,10C15,12.76 12.76,15 10,15C7.24,15 5,12.76 5,10C5,7.24 7.24,5 10,5ZM10,7C8.344,7 7,8.344 7,10C7,11.656 8.344,13 10,13C11.656,13 13,11.656 13,10C13,8.344 11.656,7 10,7Z" /> 5 | </svg> 6 | ); 7 | } 8 | -------------------------------------------------------------------------------- /packages/2d/src/editor/icons/CodeBlockIcon.tsx: -------------------------------------------------------------------------------- 1 | export function CodeBlockIcon() { 2 | return ( 3 | <svg viewBox="0 0 20 20" fill="currentColor"> 4 | <path d="M5,9L5,6.999C5,6.469 5.211,5.96 5.585,5.586C5.96,5.211 6.469,5 6.999,5L9,5L9,7L6.999,7L7,9L7,11L7,13L8.985,13L9,15L7,15C5.895,15 5,14.105 5,13L5,11L4,11L4,9L5,9Z" /> 5 | <path d="M15,11L15,13.001C15,13.531 14.789,14.04 14.415,14.414C14.04,14.789 13.531,15 13.001,15L11,15L11,13L13,13L13,11L13,9L13,7L11.015,7L11,5L13,5C14.105,5 15,5.895 15,7L15,9L16,9L16,11L15,11Z" /> 6 | </svg> 7 | ); 8 | } 9 | -------------------------------------------------------------------------------- /packages/2d/src/editor/icons/GridIcon.tsx: -------------------------------------------------------------------------------- 1 | export function GridIcon() { 2 | return ( 3 | <svg viewBox="0 0 20 20" fill="currentColor"> 4 | <path d="M6,8L4,8L4,6L6,6L6,4L8,4L8,6L12,6L12,4L14,4L14,6L16,6L16,8L14,8L14,12L16,12L16,14L14,14L14,16L12,16L12,14L8,14L8,16L6,16L6,14L4,14L4,12L6,12L6,8ZM8,12L12,12L12,8L8,8L8,12Z" /> 5 | </svg> 6 | ); 7 | } 8 | -------------------------------------------------------------------------------- /packages/2d/src/editor/icons/ImgIcon.tsx: -------------------------------------------------------------------------------- 1 | export function ImgIcon() { 2 | return ( 3 | <svg viewBox="0 0 20 20" fill="currentColor"> 4 | <path d="M5,15L15,15L15,10L13,8L8,13L5,10L5,15Z" /> 5 | <circle cx="8" cy="7" r="2" /> 6 | </svg> 7 | ); 8 | } 9 | -------------------------------------------------------------------------------- /packages/2d/src/editor/icons/LayoutIcon.tsx: -------------------------------------------------------------------------------- 1 | export function LayoutIcon() { 2 | return ( 3 | <svg viewBox="0 0 20 20" fill="currentColor"> 4 | <path d="M14,5C14.552,5 15,5.448 15,6C15,7.916 15,12.084 15,14C15,14.552 14.552,15 14,15C12.815,15 11,15 11,15L11,5L14,5Z" /> 5 | <path d="M9,5L9,9L5,9L5,6C5,5.448 5.448,5 6,5L9,5Z" /> 6 | <path d="M9,11L9,15L6,15C5.448,15 5,14.552 5,14L5,11L9,11Z" /> 7 | </svg> 8 | ); 9 | } 10 | -------------------------------------------------------------------------------- /packages/2d/src/editor/icons/LineIcon.tsx: -------------------------------------------------------------------------------- 1 | export function LineIcon() { 2 | return ( 3 | <svg viewBox="0 0 20 20" fill="currentColor"> 4 | <path d="M9.906,4.589L11.411,5.906L8.529,9.2L13.859,8.439C14.273,8.379 14.68,8.584 14.879,8.952C15.078,9.319 15.028,9.772 14.753,10.087L10.094,15.411L8.589,14.094L11.471,10.8L6.141,11.561C5.727,11.621 5.32,11.416 5.121,11.048C4.922,10.681 4.972,10.228 5.247,9.913L9.906,4.589Z" /> 5 | </svg> 6 | ); 7 | } 8 | -------------------------------------------------------------------------------- /packages/2d/src/editor/icons/NodeIcon.tsx: -------------------------------------------------------------------------------- 1 | export function NodeIcon() { 2 | return ( 3 | <svg viewBox="0 0 20 20" fill="currentColor"> 4 | <path d="M7,9L5,9L5,7L7,7L7,5L9,5L9,7L12,7L12,5L15,8L12,11L12,9L9,9L9,12L11,12L8,15L5,12L7,12L7,9Z" /> 5 | </svg> 6 | ); 7 | } 8 | -------------------------------------------------------------------------------- /packages/2d/src/editor/icons/RayIcon.tsx: -------------------------------------------------------------------------------- 1 | export function RayIcon() { 2 | return ( 3 | <svg viewBox="0 0 20 20" fill="currentColor"> 4 | <path d="M12,9.414L6.707,14.707L5.293,13.293L10.586,8L8,8L8,6L13,6C13.552,6 14,6.448 14,7L14,12L12,12L12,9.414Z" /> 5 | </svg> 6 | ); 7 | } 8 | -------------------------------------------------------------------------------- /packages/2d/src/editor/icons/RectIcon.tsx: -------------------------------------------------------------------------------- 1 | export function RectIcon() { 2 | return ( 3 | <svg viewBox="0 0 20 20" fill="currentColor"> 4 | <path d="M15,6L15,14C15,14.552 14.552,15 14,15L6,15C5.448,15 5,14.552 5,14L5,6C5,5.448 5.448,5 6,5L14,5C14.552,5 15,5.448 15,6ZM13,7L7,7L7,13L13,13L13,7Z" /> 5 | </svg> 6 | ); 7 | } 8 | -------------------------------------------------------------------------------- /packages/2d/src/editor/icons/TxtIcon.tsx: -------------------------------------------------------------------------------- 1 | export function TxtIcon() { 2 | return ( 3 | <svg viewBox="0 0 20 20" fill="currentColor"> 4 | <path d="M9,13L9,6L11,6L11,13L12,13L12,15L8,15L8,13L9,13Z" /> 5 | <path d="M7,8L5,8L5,6C5,5.448 5.448,5 6,5L14,5C14.552,5 15,5.448 15,6L15,8L13,8L13,7L7,7L7,8Z" /> 6 | </svg> 7 | ); 8 | } 9 | -------------------------------------------------------------------------------- /packages/2d/src/editor/icons/VideoIcon.tsx: -------------------------------------------------------------------------------- 1 | export function VideoIcon() { 2 | return ( 3 | <svg viewBox="0 0 20 20" fill="currentColor"> 4 | <path d="M14,10.866L7.25,14.763C6.941,14.942 6.559,14.942 6.25,14.763C5.941,14.585 5.75,14.254 5.75,13.897L5.75,6.103C5.75,5.746 5.941,5.415 6.25,5.237C6.559,5.058 6.941,5.058 7.25,5.237L14,9.134C14.309,9.313 14.5,9.643 14.5,10C14.5,10.357 14.309,10.687 14,10.866ZM11.5,10L7.75,7.835L7.75,12.165L11.5,10Z" /> 5 | </svg> 6 | ); 7 | } 8 | -------------------------------------------------------------------------------- /packages/2d/src/editor/icons/View2DIcon.tsx: -------------------------------------------------------------------------------- 1 | export function View2DIcon() { 2 | return ( 3 | <svg viewBox="0 0 20 20" fill="currentColor"> 4 | <path d="M9,5L9,7L7,7L7,9L5,9L5,6C5,5.448 5.448,5 6,5L9,5Z" /> 5 | <path d="M5,11L7,11L7,13L9,13L9,15L6,15C5.448,15 5,14.552 5,14L5,11Z" /> 6 | <path d="M11,15L11,13L13,13L13,11L15,11L15,14C15,14.552 14.552,15 14,15L11,15Z" /> 7 | <path d="M15,9L13,9L13,7L11,7L11,5L14,5C14.552,5 15,5.448 15,6L15,9Z" /> 8 | </svg> 9 | ); 10 | } 11 | -------------------------------------------------------------------------------- /packages/2d/src/editor/index.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/motion-canvas/motion-canvas/00639cd4cca76d60275d48fa15211d1e17e78228/packages/2d/src/editor/index.css -------------------------------------------------------------------------------- /packages/2d/src/editor/tree/TreeRoot.tsx: -------------------------------------------------------------------------------- 1 | import {clsx} from 'clsx'; 2 | import {JSX} from 'preact'; 3 | import styles from './index.module.scss'; 4 | 5 | export function TreeRoot({ 6 | className, 7 | ...props 8 | }: JSX.HTMLAttributes<HTMLDivElement>) { 9 | return <div className={clsx(styles.root, className)} {...props} />; 10 | } 11 | -------------------------------------------------------------------------------- /packages/2d/src/editor/tree/ViewRoot.tsx: -------------------------------------------------------------------------------- 1 | import {useSignal, useSignalEffect} from '@preact/signals'; 2 | import {usePluginState} from '../Provider'; 3 | import {NodeElement} from './NodeElement'; 4 | import {TreeRoot} from './TreeRoot'; 5 | 6 | export function ViewRoot() { 7 | const {scene} = usePluginState(); 8 | const view = useSignal(scene.value?.getView()); 9 | 10 | useSignalEffect(() => { 11 | view.value = scene.value?.getView(); 12 | return scene.value?.onReset.subscribe(() => { 13 | view.value = scene.value?.getView(); 14 | }); 15 | }); 16 | 17 | return view.value ? ( 18 | <TreeRoot>{<NodeElement node={view.value} />}</TreeRoot> 19 | ) : null; 20 | } 21 | -------------------------------------------------------------------------------- /packages/2d/src/editor/tree/index.ts: -------------------------------------------------------------------------------- 1 | export * from './DetachedRoot'; 2 | export * from './NodeElement'; 3 | export * from './ViewRoot'; 4 | export * from './navigation'; 5 | -------------------------------------------------------------------------------- /packages/2d/src/editor/tsconfig.build.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "references": [{"path": "../lib/tsconfig.build.json"}], 4 | "include": ["**/*"] 5 | } 6 | -------------------------------------------------------------------------------- /packages/2d/src/editor/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.base.json", 3 | "compilerOptions": { 4 | "jsx": "react-jsx", 5 | "jsxImportSource": "preact", 6 | "paths": { 7 | "@motion-canvas/2d": ["../lib"] 8 | } 9 | }, 10 | "references": [{"path": "../lib/tsconfig.json"}], 11 | "include": ["**/*"] 12 | } 13 | -------------------------------------------------------------------------------- /packages/2d/src/editor/tsdoc.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://developer.microsoft.com/json-schemas/tsdoc/v0/tsdoc.schema.json", 3 | "extends": ["../../../../tsdoc.json"] 4 | } 5 | -------------------------------------------------------------------------------- /packages/2d/src/editor/utils/index.ts: -------------------------------------------------------------------------------- 1 | export * from './SignalSet'; 2 | -------------------------------------------------------------------------------- /packages/2d/src/editor/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// <reference types="vite/client" /> 2 | -------------------------------------------------------------------------------- /packages/2d/src/lib/code/index.ts: -------------------------------------------------------------------------------- 1 | export * from './CodeCursor'; 2 | export * from './CodeDiffer'; 3 | export * from './CodeFragment'; 4 | export * from './CodeHighlighter'; 5 | export * from './CodeRange'; 6 | export * from './CodeScope'; 7 | export * from './CodeSelection'; 8 | export * from './CodeSignal'; 9 | export * from './CodeTokenizer'; 10 | export * from './DefaultHighlightStyle'; 11 | export * from './LezerHighlighter'; 12 | export * from './diff'; 13 | export * from './extractRange'; 14 | -------------------------------------------------------------------------------- /packages/2d/src/lib/components/__logs__/image-without-source.md: -------------------------------------------------------------------------------- 1 | The image won't be visible unless you specify a source: 2 | 3 | ```tsx 4 | import myImage from './example.png'; 5 | // ... 6 | <Img src={myImage} />; 7 | ``` 8 | 9 | If you did this intentionally, and don't want to see this warning, set the `src` 10 | property to `null`: 11 | 12 | ```tsx 13 | <Img src={null} /> 14 | ``` 15 | 16 | [Learn more](https://motioncanvas.io/docs/media#images) about working with 17 | images. 18 | -------------------------------------------------------------------------------- /packages/2d/src/lib/components/__logs__/reactive-playback-rate.md: -------------------------------------------------------------------------------- 1 | The `playbackRate` of a `Video` cannot be reactive. 2 | 3 | Make sure to use a concrete value and not a function: 4 | 5 | ```ts wrong 6 | video.playbackRate(() => 7); 7 | ``` 8 | 9 | ```ts correct 10 | video.playbackRate(7); 11 | ``` 12 | 13 | If you're using a signal, extract its value before passing it to the property: 14 | 15 | ```ts wrong 16 | video.playbackRate(mySignal); 17 | ``` 18 | 19 | ```ts correct 20 | video.playbackRate(mySignal()); 21 | ``` 22 | -------------------------------------------------------------------------------- /packages/2d/src/lib/components/__logs__/spline-with-insufficient-knots.md: -------------------------------------------------------------------------------- 1 | The spline won't be visible unless you specify at least two knots: 2 | 3 | ```tsx 4 | <Spline 5 | stroke="#fff" 6 | lineWidth={8} 7 | points={[ 8 | [100, 0], 9 | [0, 0], 10 | [0, 100], 11 | ]} 12 | /> 13 | ``` 14 | 15 | For more control over the knot handles, you can alternatively provide the knots 16 | as children to the spline using the `Knot` component: 17 | 18 | ```tsx 19 | <Spline stroke="#fff" lineWidth={8}> 20 | <Knot x={100} endHandle={[-50, 0]} /> 21 | <Knot /> 22 | <Knot y={100} startHandle={[-100, 50]} /> 23 | </Spline> 24 | ``` 25 | -------------------------------------------------------------------------------- /packages/2d/src/lib/curves/CurveDrawingInfo.ts: -------------------------------------------------------------------------------- 1 | import {Vector2} from '@motion-canvas/core'; 2 | 3 | export interface CurveDrawingInfo { 4 | path: Path2D; 5 | arrowSize: number; 6 | endPoint: Vector2; 7 | endTangent: Vector2; 8 | startPoint: Vector2; 9 | startTangent: Vector2; 10 | startOffset: number; 11 | } 12 | -------------------------------------------------------------------------------- /packages/2d/src/lib/curves/CurvePoint.ts: -------------------------------------------------------------------------------- 1 | import type {Vector2} from '@motion-canvas/core'; 2 | 3 | export interface CurvePoint { 4 | position: Vector2; 5 | /** 6 | * @deprecated 7 | * The tangent is currently inconsistent for different types of curves and may 8 | * sometimes return the normal of the point, instead. This will be fixed in 9 | * the next major version but is kept as is for now for backwards 10 | * compatibility reasons. To always get the real tangent of the point, you can 11 | * use `normal.flipped.perpendicular`, instead. 12 | */ 13 | tangent: Vector2; 14 | normal: Vector2; 15 | } 16 | -------------------------------------------------------------------------------- /packages/2d/src/lib/curves/CurveProfile.ts: -------------------------------------------------------------------------------- 1 | import {Segment} from './Segment'; 2 | 3 | export interface CurveProfile { 4 | arcLength: number; 5 | segments: Segment[]; 6 | minSin: number; 7 | } 8 | -------------------------------------------------------------------------------- /packages/2d/src/lib/curves/KnotInfo.ts: -------------------------------------------------------------------------------- 1 | import {Vector2} from '@motion-canvas/core'; 2 | 3 | export type KnotAutoHandles = {start: number; end: number}; 4 | 5 | export interface KnotInfo { 6 | position: Vector2; 7 | startHandle: Vector2; 8 | endHandle: Vector2; 9 | auto: KnotAutoHandles; 10 | } 11 | -------------------------------------------------------------------------------- /packages/2d/src/lib/curves/Segment.ts: -------------------------------------------------------------------------------- 1 | import {Vector2} from '@motion-canvas/core'; 2 | import {CurvePoint} from './CurvePoint'; 3 | 4 | export abstract class Segment { 5 | public abstract readonly points: Vector2[]; 6 | 7 | public abstract draw( 8 | context: CanvasRenderingContext2D | Path2D, 9 | start: number, 10 | end: number, 11 | move: boolean, 12 | ): [CurvePoint, CurvePoint]; 13 | 14 | public abstract getPoint(distance: number): CurvePoint; 15 | 16 | public abstract get arcLength(): number; 17 | } 18 | -------------------------------------------------------------------------------- /packages/2d/src/lib/curves/getPolylineProfile.test.ts: -------------------------------------------------------------------------------- 1 | import {Vector2} from '@motion-canvas/core'; 2 | import {describe, expect, test} from 'vitest'; 3 | import {getPolylineProfile} from './getPolylineProfile'; 4 | 5 | describe('getPolylineProfile', () => { 6 | test('Correct arc length', () => { 7 | const profile = getPolylineProfile( 8 | [ 9 | Vector2.zero, 10 | Vector2.zero, 11 | new Vector2(100, 0), 12 | new Vector2(100, 100), 13 | new Vector2(0, 100), 14 | ], 15 | 0, 16 | true, 17 | ); 18 | 19 | expect(profile.arcLength).toBe(400); 20 | }); 21 | }); 22 | -------------------------------------------------------------------------------- /packages/2d/src/lib/curves/index.ts: -------------------------------------------------------------------------------- 1 | export * from './CircleSegment'; 2 | export * from './CubicBezierSegment'; 3 | export * from './CurveDrawingInfo'; 4 | export * from './CurvePoint'; 5 | export * from './CurveProfile'; 6 | export * from './KnotInfo'; 7 | export * from './LineSegment'; 8 | export * from './Polynomial'; 9 | export * from './Polynomial2D'; 10 | export * from './QuadBezierSegment'; 11 | export * from './Segment'; 12 | export * from './getBezierSplineProfile'; 13 | export * from './getCircleProfile'; 14 | export * from './getPointAtDistance'; 15 | export * from './getPolylineProfile'; 16 | export * from './getRectProfile'; 17 | -------------------------------------------------------------------------------- /packages/2d/src/lib/decorators/canvasStyleSignal.ts: -------------------------------------------------------------------------------- 1 | import {Color, Signal} from '@motion-canvas/core'; 2 | import type {CanvasStyle, PossibleCanvasStyle} from '../partials'; 3 | import {canvasStyleParser} from '../utils'; 4 | import {initial, interpolation, parser, signal} from './signal'; 5 | 6 | export type CanvasStyleSignal<T> = Signal<PossibleCanvasStyle, CanvasStyle, T>; 7 | 8 | export function canvasStyleSignal(): PropertyDecorator { 9 | return (target, key) => { 10 | signal()(target, key); 11 | parser(canvasStyleParser)(target, key); 12 | interpolation(Color.lerp)(target, key); 13 | initial(null)(target, key); 14 | }; 15 | } 16 | -------------------------------------------------------------------------------- /packages/2d/src/lib/decorators/colorSignal.ts: -------------------------------------------------------------------------------- 1 | import {Color} from '@motion-canvas/core'; 2 | import {signal, wrapper} from './signal'; 3 | 4 | export function colorSignal(): PropertyDecorator { 5 | return (target, key) => { 6 | signal()(target, key); 7 | wrapper(Color)(target, key); 8 | }; 9 | } 10 | -------------------------------------------------------------------------------- /packages/2d/src/lib/decorators/computed.ts: -------------------------------------------------------------------------------- 1 | import {createComputed} from '@motion-canvas/core'; 2 | import {addInitializer} from './initializers'; 3 | 4 | /** 5 | * Create a computed method decorator. 6 | * 7 | * @remarks 8 | * This decorator turns the given method into a computed value. 9 | * See {@link createComputed} for more information. 10 | */ 11 | export function computed(): MethodDecorator { 12 | return (target: any, key) => { 13 | addInitializer(target, (instance: any) => { 14 | const method = Object.getPrototypeOf(instance)[key]; 15 | instance[key] = createComputed(method.bind(instance), instance); 16 | }); 17 | }; 18 | } 19 | -------------------------------------------------------------------------------- /packages/2d/src/lib/decorators/defaultStyle.ts: -------------------------------------------------------------------------------- 1 | import {capitalize} from '@motion-canvas/core'; 2 | import {Layout} from '../components'; 3 | 4 | export function defaultStyle<T>(initial?: T): PropertyDecorator { 5 | return (target: any, key) => { 6 | target[`getDefault${capitalize(<string>key)}`] = function (this: Layout) { 7 | const parent = this.parentTransform(); 8 | if (parent && this.layout() !== false && parent.layoutEnabled()) { 9 | return (parent as any)[key](); 10 | } 11 | 12 | return initial; 13 | }; 14 | }; 15 | } 16 | -------------------------------------------------------------------------------- /packages/2d/src/lib/decorators/index.ts: -------------------------------------------------------------------------------- 1 | export * from './canvasStyleSignal'; 2 | export * from './colorSignal'; 3 | export * from './compound'; 4 | export * from './computed'; 5 | export * from './defaultStyle'; 6 | export * from './filtersSignal'; 7 | export * from './initializers'; 8 | export * from './nodeName'; 9 | export * from './signal'; 10 | export * from './vector2Signal'; 11 | -------------------------------------------------------------------------------- /packages/2d/src/lib/decorators/nodeName.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @internal 3 | */ 4 | export const NODE_NAME = Symbol.for('@motion-canvas/2d/nodeName'); 5 | 6 | /** 7 | * @internal 8 | */ 9 | export function nodeName(name: string) { 10 | return function (target: any) { 11 | target.prototype[NODE_NAME] = name; 12 | }; 13 | } 14 | -------------------------------------------------------------------------------- /packages/2d/src/lib/decorators/spacingSignal.ts: -------------------------------------------------------------------------------- 1 | import {Spacing} from '@motion-canvas/core'; 2 | import {compound} from './compound'; 3 | import {wrapper} from './signal'; 4 | 5 | export function spacingSignal(prefix?: string): PropertyDecorator { 6 | return (target, key) => { 7 | compound({ 8 | top: prefix ? `${prefix}Top` : 'top', 9 | right: prefix ? `${prefix}Right` : 'right', 10 | bottom: prefix ? `${prefix}Bottom` : 'bottom', 11 | left: prefix ? `${prefix}Left` : 'left', 12 | })(target, key); 13 | wrapper(Spacing)(target, key); 14 | }; 15 | } 16 | -------------------------------------------------------------------------------- /packages/2d/src/lib/globals.d.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/no-unused-vars */ 2 | /// <reference types="@motion-canvas/core/project" /> 3 | /// <reference types="@motion-canvas/internal" /> 4 | -------------------------------------------------------------------------------- /packages/2d/src/lib/index.ts: -------------------------------------------------------------------------------- 1 | export * from './code'; 2 | export * from './components'; 3 | export * from './curves'; 4 | export * from './decorators'; 5 | export * from './jsx-runtime'; 6 | export * from './partials'; 7 | export * from './scenes'; 8 | export * from './utils'; 9 | -------------------------------------------------------------------------------- /packages/2d/src/lib/jsx-dev-runtime.ts: -------------------------------------------------------------------------------- 1 | import {Fragment, jsx} from './jsx-runtime'; 2 | export {Fragment, jsx as jsxDEV}; 3 | -------------------------------------------------------------------------------- /packages/2d/src/lib/parse-svg-path.d.ts: -------------------------------------------------------------------------------- 1 | declare module 'parse-svg-path' { 2 | export type PathCommand = [ 3 | string, 4 | number, 5 | number, 6 | number, 7 | number, 8 | number, 9 | number, 10 | number, 11 | ]; 12 | function parse(path: string): PathCommand[]; 13 | export default parse; 14 | } 15 | -------------------------------------------------------------------------------- /packages/2d/src/lib/partials/index.ts: -------------------------------------------------------------------------------- 1 | export * from './Filter'; 2 | export * from './Gradient'; 3 | export * from './Pattern'; 4 | export * from './types'; 5 | -------------------------------------------------------------------------------- /packages/2d/src/lib/scenes/index.ts: -------------------------------------------------------------------------------- 1 | export * from './Scene2D'; 2 | export * from './makeScene2D'; 3 | export * from './useScene2D'; 4 | -------------------------------------------------------------------------------- /packages/2d/src/lib/scenes/makeScene2D.ts: -------------------------------------------------------------------------------- 1 | import { 2 | createSceneMetadata, 3 | DescriptionOf, 4 | ThreadGeneratorFactory, 5 | } from '@motion-canvas/core'; 6 | import type {View2D} from '../components'; 7 | import {Scene2D} from './Scene2D'; 8 | 9 | export function makeScene2D( 10 | runner: ThreadGeneratorFactory<View2D>, 11 | ): DescriptionOf<Scene2D> { 12 | return { 13 | klass: Scene2D, 14 | config: runner, 15 | stack: new Error().stack, 16 | meta: createSceneMetadata(), 17 | plugins: ['@motion-canvas/2d/editor'], 18 | }; 19 | } 20 | -------------------------------------------------------------------------------- /packages/2d/src/lib/scenes/useScene2D.ts: -------------------------------------------------------------------------------- 1 | import {useScene} from '@motion-canvas/core'; 2 | import type {Scene2D} from './Scene2D'; 3 | 4 | export function useScene2D(): Scene2D { 5 | return <Scene2D>useScene(); 6 | } 7 | -------------------------------------------------------------------------------- /packages/2d/src/lib/tsconfig.build.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "include": ["**/*"], 4 | "exclude": ["**/*.test.*", "**/__tests__/**/*"] 5 | } 6 | -------------------------------------------------------------------------------- /packages/2d/src/lib/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.base.json", 3 | "compilerOptions": { 4 | "types": ["node"], 5 | "jsx": "react-jsx", 6 | "jsxImportSource": "@motion-canvas/2d/src/lib", 7 | "plugins": [ 8 | { 9 | "transform": "@motion-canvas/internal/transformers/markdown-literals.js" 10 | } 11 | ] 12 | }, 13 | "include": ["**/*"] 14 | } 15 | -------------------------------------------------------------------------------- /packages/2d/src/lib/tsdoc.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://developer.microsoft.com/json-schemas/tsdoc/v0/tsdoc.schema.json", 3 | "extends": ["../../../../tsdoc.json"] 4 | } 5 | -------------------------------------------------------------------------------- /packages/2d/src/lib/utils/index.ts: -------------------------------------------------------------------------------- 1 | export * from './CanvasUtils'; 2 | export * from './is'; 3 | export * from './withDefaults'; 4 | -------------------------------------------------------------------------------- /packages/2d/src/lib/utils/is.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Create a predicate that checks if the given object is an instance of the 3 | * given class. 4 | * 5 | * @param klass - The class to check against. 6 | */ 7 | export function is<T>( 8 | klass: new (...args: any[]) => T, 9 | ): (object: any) => object is T { 10 | return (object): object is T => object instanceof klass; 11 | } 12 | -------------------------------------------------------------------------------- /packages/2d/src/tsconfig.base.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "rootDir": ".", 4 | "outDir": "..", 5 | "strict": true, 6 | "noImplicitOverride": true, 7 | "module": "esnext", 8 | "target": "es2020", 9 | "moduleResolution": "node", 10 | "resolveJsonModule": true, 11 | "declaration": true, 12 | "declarationMap": true, 13 | "inlineSourceMap": true, 14 | "experimentalDecorators": true, 15 | "skipLibCheck": true, 16 | "composite": true 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /packages/2d/src/tsconfig.build.json: -------------------------------------------------------------------------------- 1 | { 2 | "files": [], 3 | "include": [], 4 | "references": [ 5 | {"path": "./lib/tsconfig.build.json"}, 6 | {"path": "./editor/tsconfig.build.json"} 7 | ] 8 | } 9 | -------------------------------------------------------------------------------- /packages/2d/src/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "files": [], 3 | "include": [], 4 | "references": [{"path": "./lib"}, {"path": "./editor"}] 5 | } 6 | -------------------------------------------------------------------------------- /packages/2d/tsconfig.project.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@motion-canvas/core/tsconfig.project.json", 3 | "compilerOptions": { 4 | "jsx": "react-jsx", 5 | "jsxImportSource": "@motion-canvas/2d/lib" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /packages/2d/vitest.config.ts: -------------------------------------------------------------------------------- 1 | import markdownLiterals from '@motion-canvas/internal/vite/markdown-literals'; 2 | import {defineConfig} from 'vitest/config'; 3 | 4 | export default defineConfig({ 5 | plugins: [markdownLiterals()], 6 | test: { 7 | include: ['./src/lib/**/*.test.*'], 8 | environment: 'jsdom', 9 | }, 10 | }); 11 | -------------------------------------------------------------------------------- /packages/core/rollup.config.mjs: -------------------------------------------------------------------------------- 1 | import typescript from '@motion-canvas/internal/rollup/typescript.mjs'; 2 | import commonjs from '@rollup/plugin-commonjs'; 3 | import resolve from '@rollup/plugin-node-resolve'; 4 | import terser from '@rollup/plugin-terser'; 5 | 6 | export default [ 7 | { 8 | input: 'src/index.ts', 9 | output: { 10 | file: 'dist/index.js', 11 | format: 'es', 12 | sourcemap: true, 13 | }, 14 | plugins: [resolve(), commonjs(), typescript(), terser()], 15 | }, 16 | ]; 17 | -------------------------------------------------------------------------------- /packages/core/shaders/common.glsl: -------------------------------------------------------------------------------- 1 | in vec2 screenUV; 2 | in vec2 sourceUV; 3 | in vec2 destinationUV; 4 | 5 | out vec4 outColor; 6 | 7 | uniform float time; 8 | uniform float deltaTime; 9 | uniform float framerate; 10 | uniform int frame; 11 | uniform vec2 resolution; 12 | uniform sampler2D sourceTexture; 13 | uniform sampler2D destinationTexture; 14 | uniform mat4 sourceMatrix; 15 | uniform mat4 destinationMatrix; -------------------------------------------------------------------------------- /packages/core/shaders/fragment.glsl: -------------------------------------------------------------------------------- 1 | #version 300 es 2 | precision highp float; 3 | 4 | #include "./common.glsl" 5 | 6 | void main() { 7 | outColor = textureLod(sourceTexture, sourceUV, 0.0); 8 | } -------------------------------------------------------------------------------- /packages/core/src/app/__logs__/include-without-preprocessor.md: -------------------------------------------------------------------------------- 1 | The `#include` directive requires the use of a preprocessor. 2 | 3 | Make sure to import the shader from a file: 4 | 5 | ```ts 6 | import shader from './shader.glsl'; 7 | ``` 8 | 9 | Do **NOT** use the raw loader: 10 | 11 | ```ts 12 | import shader from './shader.glsl?raw'; 13 | ``` 14 | 15 | Do **NOT** use `#include` in an inline string: 16 | 17 | ```ts 18 | const shader = `\ 19 | #include "example.glsl" 20 | `; 21 | ``` 22 | 23 | [Learn more](https://motioncanvas.io/docs/shaders) about working with shaders. 24 | -------------------------------------------------------------------------------- /packages/core/src/app/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Main Motion Canvas classes. 3 | * 4 | * @packageDocumentation 5 | */ 6 | 7 | export * from './Exporter'; 8 | export * from './ImageExporter'; 9 | export * from './Logger'; 10 | export * from './PlaybackManager'; 11 | export * from './PlaybackStatus'; 12 | export * from './Player'; 13 | export * from './Presenter'; 14 | export * from './Project'; 15 | export * from './ProjectMetadata'; 16 | export * from './Renderer'; 17 | export * from './SettingsMetadata'; 18 | export * from './SharedWebGLContext'; 19 | export * from './Stage'; 20 | export * from './bootstrap'; 21 | -------------------------------------------------------------------------------- /packages/core/src/decorators/decorate.ts: -------------------------------------------------------------------------------- 1 | export function decorate(fn: Callback, ...decorators: MethodDecorator[]) { 2 | const target = {[fn.name]: fn}; 3 | const descriptor = Object.getOwnPropertyDescriptor(target, fn.name); 4 | if (descriptor) { 5 | for (let i = decorators.length - 1; i >= 0; i--) { 6 | decorators[i](target, fn.name, descriptor); 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /packages/core/src/decorators/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Property decorators. 3 | * 4 | * @packageDocumentation 5 | */ 6 | 7 | export * from './decorate'; 8 | export * from './lazy'; 9 | export * from './threadable'; 10 | -------------------------------------------------------------------------------- /packages/core/src/decorators/threadable.ts: -------------------------------------------------------------------------------- 1 | export function threadable(customName?: string): MethodDecorator { 2 | return function ( 3 | _: unknown, 4 | propertyKey: string | symbol, 5 | descriptor: PropertyDescriptor, 6 | ) { 7 | descriptor.value.prototype.name = customName ?? propertyKey; 8 | descriptor.value.prototype.threadable = true; 9 | }; 10 | } 11 | -------------------------------------------------------------------------------- /packages/core/src/events/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Subscriptions and triggering of events. 3 | * 4 | * @packageDocumentation 5 | */ 6 | 7 | export * from './AsyncEventDispatcher'; 8 | export * from './EventDispatcher'; 9 | export * from './EventDispatcherBase'; 10 | export * from './FlagDispatcher'; 11 | export * from './ValueDispatcher'; 12 | -------------------------------------------------------------------------------- /packages/core/src/flow/__logs__/infinite-loop.md: -------------------------------------------------------------------------------- 1 | Make sure to use `yield` or `spawn()` to execute the loop concurrently in a 2 | separate thread: 3 | 4 | ```ts wrong 5 | // prettier-ignore 6 | yield* loop(() => rect().opacity(0).opacity(1, 1)); 7 | ``` 8 | 9 | ```ts correct 10 | yield loop(() => rect().opacity(0).opacity(1, 1)); 11 | // or 12 | spawn(loop(() => rect().opacity(0).opacity(1, 1))); 13 | ``` 14 | 15 | If you want to execute the loop a finite number of times, specify the iteration 16 | count as the first argument: 17 | 18 | ```ts 19 | // prettier-ignore 20 | yield* loop(10, () => rect().opacity(0).opacity(1, 1)); 21 | // ^ iteration count 22 | ``` 23 | -------------------------------------------------------------------------------- /packages/core/src/flow/all.ts: -------------------------------------------------------------------------------- 1 | import {decorate, threadable} from '../decorators'; 2 | import {ThreadGenerator, join} from '../threading'; 3 | 4 | decorate(all, threadable()); 5 | /** 6 | * Run all tasks concurrently and wait for all of them to finish. 7 | * 8 | * @example 9 | * ```ts 10 | * // current time: 0s 11 | * yield* all( 12 | * rect.fill('#ff0000', 2), 13 | * rect.opacity(1, 1), 14 | * ); 15 | * // current time: 2s 16 | * ``` 17 | * 18 | * @param tasks - A list of tasks to run. 19 | */ 20 | export function* all(...tasks: ThreadGenerator[]): ThreadGenerator { 21 | for (const task of tasks) { 22 | yield task; 23 | } 24 | yield* join(...tasks); 25 | } 26 | -------------------------------------------------------------------------------- /packages/core/src/flow/any.ts: -------------------------------------------------------------------------------- 1 | import {decorate, threadable} from '../decorators'; 2 | import {ThreadGenerator, join} from '../threading'; 3 | 4 | decorate(any, threadable()); 5 | /** 6 | * Run all tasks concurrently and wait for any of them to finish. 7 | * 8 | * @example 9 | * ```ts 10 | * // current time: 0s 11 | * yield* any( 12 | * rect.fill('#ff0000', 2), 13 | * rect.opacity(1, 1), 14 | * ); 15 | * // current time: 1s 16 | * ``` 17 | * 18 | * @param tasks - A list of tasks to run. 19 | */ 20 | export function* any(...tasks: ThreadGenerator[]): ThreadGenerator { 21 | for (const task of tasks) { 22 | yield task; 23 | } 24 | yield* join(false, ...tasks); 25 | } 26 | -------------------------------------------------------------------------------- /packages/core/src/flow/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Utilities for controlling the flow and timing of an animation. 3 | * @packageDocumentation 4 | */ 5 | 6 | export * from './all'; 7 | export * from './any'; 8 | export * from './chain'; 9 | export * from './delay'; 10 | export * from './every'; 11 | export * from './loop'; 12 | export * from './loopFor'; 13 | export * from './loopUntil'; 14 | export * from './noop'; 15 | export * from './run'; 16 | export * from './scheduling'; 17 | export * from './sequence'; 18 | -------------------------------------------------------------------------------- /packages/core/src/flow/noop.ts: -------------------------------------------------------------------------------- 1 | import {decorate, threadable} from '../decorators'; 2 | import {ThreadGenerator} from '../threading'; 3 | 4 | decorate(noop, threadable()); 5 | /** 6 | * Do nothing. 7 | */ 8 | export function* noop(): ThreadGenerator { 9 | // do nothing 10 | } 11 | -------------------------------------------------------------------------------- /packages/core/src/globals.d.ts: -------------------------------------------------------------------------------- 1 | /// <reference types="@motion-canvas/internal" /> 2 | 3 | declare type Callback = (...args: any[]) => void; 4 | -------------------------------------------------------------------------------- /packages/core/src/index.ts: -------------------------------------------------------------------------------- 1 | // Custom order makes rollup arrange the ObjectMetaField correctly 2 | export * from './meta'; 3 | 4 | export * from './app'; 5 | export * from './decorators'; 6 | export * from './events'; 7 | export * from './flow'; 8 | export * from './media'; 9 | export * from './plugin'; 10 | export {default as DefaultPlugin} from './plugin/DefaultPlugin'; 11 | export * from './scenes'; 12 | export * from './signals'; 13 | export * from './threading'; 14 | export * from './transitions'; 15 | export * from './tweening'; 16 | export * from './types'; 17 | export * from './utils'; 18 | -------------------------------------------------------------------------------- /packages/core/src/media/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Multi-media management. 3 | * 4 | * @packageDocumentation 5 | */ 6 | 7 | export * from './AudioData'; 8 | export * from './AudioManager'; 9 | export * from './AudioManagerPool'; 10 | export * from './AudioResourceManager'; 11 | export * from './loadImage'; 12 | -------------------------------------------------------------------------------- /packages/core/src/meta/BoolMetaField.ts: -------------------------------------------------------------------------------- 1 | import {MetaField} from './MetaField'; 2 | 3 | /** 4 | * Represents a boolean value stored in a meta file. 5 | */ 6 | export class BoolMetaField extends MetaField<any, boolean> { 7 | public readonly type = Boolean; 8 | 9 | public parse(value: any): boolean { 10 | return !!value; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /packages/core/src/meta/ColorMetaField.ts: -------------------------------------------------------------------------------- 1 | import {Color, PossibleColor} from '../types'; 2 | import {MetaField} from './MetaField'; 3 | 4 | /** 5 | * Represents a color stored in a meta file. 6 | */ 7 | export class ColorMetaField extends MetaField< 8 | PossibleColor | null, 9 | Color | null 10 | > { 11 | public readonly type = Color.symbol; 12 | 13 | public override parse(value: PossibleColor | null): Color | null { 14 | return value === null ? null : new Color(value); 15 | } 16 | 17 | public override serialize(): PossibleColor | null { 18 | return this.value.current?.serialize() ?? null; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /packages/core/src/meta/MetaOption.ts: -------------------------------------------------------------------------------- 1 | export interface MetaOption<T> { 2 | text: string; 3 | value: T; 4 | } 5 | -------------------------------------------------------------------------------- /packages/core/src/meta/StringMetaField.ts: -------------------------------------------------------------------------------- 1 | import {MetaField} from './MetaField'; 2 | import {MetaOption} from './MetaOption'; 3 | 4 | /** 5 | * Represents a string stored in a meta file. 6 | */ 7 | export class StringMetaField<T extends string = string> extends MetaField<T> { 8 | public readonly type = String; 9 | protected presets: MetaOption<T>[] = []; 10 | 11 | public getPresets() { 12 | return this.presets; 13 | } 14 | 15 | public setPresets(options: MetaOption<T>[]): this { 16 | this.presets = options; 17 | return this; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /packages/core/src/meta/Vector2MetaField.ts: -------------------------------------------------------------------------------- 1 | import {PossibleVector2, Vector2} from '../types'; 2 | import {MetaField} from './MetaField'; 3 | 4 | /** 5 | * Represents a two-dimensional vector stored in a meta file. 6 | */ 7 | export class Vector2MetaField extends MetaField<PossibleVector2, Vector2> { 8 | public readonly type = Vector2.symbol; 9 | 10 | public override parse(value: PossibleVector2): Vector2 { 11 | return new Vector2(value); 12 | } 13 | 14 | public override serialize(): PossibleVector2 { 15 | return this.value.current.serialize(); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /packages/core/src/meta/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * The runtime representation of meta files. 3 | * 4 | * @packageDocumentation 5 | */ 6 | 7 | // Custom order makes rollup arrange the ObjectMetaField correctly 8 | export * from './MetaField'; 9 | export * from './ObjectMetaField'; 10 | 11 | export * from './BoolMetaField'; 12 | export * from './ColorMetaField'; 13 | export * from './EnumMetaField'; 14 | export * from './ExporterMetaFile'; 15 | export * from './MetaFile'; 16 | export * from './MetaOption'; 17 | export * from './NumberMetaField'; 18 | export * from './RangeMetaField'; 19 | export * from './StringMetaField'; 20 | export * from './Vector2MetaField'; 21 | -------------------------------------------------------------------------------- /packages/core/src/plugin/DefaultPlugin.ts: -------------------------------------------------------------------------------- 1 | import {ImageExporter} from '../app'; 2 | import {makePlugin} from './makePlugin'; 3 | 4 | /** 5 | * The default plugin included in every Motion Canvas project. 6 | * 7 | * @internal 8 | */ 9 | export default makePlugin({ 10 | name: '@motion-canvas/core/default', 11 | exporters() { 12 | return [ImageExporter]; 13 | }, 14 | }); 15 | -------------------------------------------------------------------------------- /packages/core/src/plugin/index.ts: -------------------------------------------------------------------------------- 1 | export * from './Plugin'; 2 | export * from './makePlugin'; 3 | -------------------------------------------------------------------------------- /packages/core/src/plugin/makePlugin.ts: -------------------------------------------------------------------------------- 1 | import type {Plugin} from './Plugin'; 2 | 3 | /** 4 | * A helper function for exporting Motion Canvas plugins. 5 | * 6 | * @param plugin - The plugin configuration. 7 | * 8 | * @example 9 | * ```ts 10 | * export default makePlugin({ 11 | * name: 'my-custom-plugin', 12 | * }); 13 | * ``` 14 | */ 15 | export function makePlugin(plugin: Plugin | (() => Plugin)): () => Plugin { 16 | return typeof plugin === 'function' ? plugin : () => plugin; 17 | } 18 | -------------------------------------------------------------------------------- /packages/core/src/scenes/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Abstract scene representations and related utilities. 3 | * 4 | * @packageDocumentation 5 | */ 6 | 7 | export * from './GeneratorScene'; 8 | export * from './Inspectable'; 9 | export * from './LifecycleEvents'; 10 | export * from './Random'; 11 | export * from './Scene'; 12 | export * from './SceneMetadata'; 13 | export * from './SceneState'; 14 | export * from './Shaders'; 15 | export * from './Slides'; 16 | export * from './Sounds'; 17 | export * from './Threadable'; 18 | export * from './Variables'; 19 | -------------------------------------------------------------------------------- /packages/core/src/scenes/timeEvents/SerializedTimeEvent.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Represents a time event stored in a meta file. 3 | */ 4 | export interface SerializedTimeEvent { 5 | /** 6 | * {@inheritDoc TimeEvent.name} 7 | */ 8 | name: string; 9 | /** 10 | * {@inheritDoc TimeEvent.targetTime} 11 | */ 12 | targetTime: number; 13 | } 14 | -------------------------------------------------------------------------------- /packages/core/src/scenes/timeEvents/index.ts: -------------------------------------------------------------------------------- 1 | export * from './EditableTimeEvents'; 2 | export * from './ReadOnlyTimeEvents'; 3 | export * from './SerializedTimeEvent'; 4 | export * from './TimeEvent'; 5 | export * from './TimeEvents'; 6 | -------------------------------------------------------------------------------- /packages/core/src/signals/EffectContext.ts: -------------------------------------------------------------------------------- 1 | import {DependencyContext} from './DependencyContext'; 2 | 3 | export class EffectContext extends DependencyContext { 4 | public constructor(private readonly callback: () => void) { 5 | super(); 6 | this.event.subscribe(this.update); 7 | this.markDirty(); 8 | } 9 | 10 | private update = () => { 11 | this.clearDependencies(); 12 | this.startCollecting(); 13 | this.callback(); 14 | this.finishCollecting(); 15 | this.event.reset(); 16 | }; 17 | } 18 | -------------------------------------------------------------------------------- /packages/core/src/signals/createComputed.ts: -------------------------------------------------------------------------------- 1 | import {Computed, ComputedContext} from '../signals'; 2 | 3 | export function createComputed<TValue>( 4 | factory: (...args: any[]) => TValue, 5 | owner?: any, 6 | ): Computed<TValue> { 7 | return new ComputedContext<TValue>(factory, owner).toSignal(); 8 | } 9 | -------------------------------------------------------------------------------- /packages/core/src/signals/createDeferredEffect.ts: -------------------------------------------------------------------------------- 1 | import {DeferredEffectContext} from './DeferredEffectContext'; 2 | 3 | /** 4 | * Invoke the callback at the end of each frame if any of its dependencies 5 | * changed. 6 | * 7 | * @param callback - The callback to invoke. 8 | */ 9 | export function createDeferredEffect(callback: () => void): () => void { 10 | const context = new DeferredEffectContext(callback); 11 | return () => context.dispose(); 12 | } 13 | -------------------------------------------------------------------------------- /packages/core/src/signals/createEffect.ts: -------------------------------------------------------------------------------- 1 | import {EffectContext} from './EffectContext'; 2 | 3 | /** 4 | * Invoke the callback immediately after any of its dependencies change. 5 | * 6 | * @param callback - The callback to invoke. 7 | */ 8 | export function createEffect(callback: () => void): () => void { 9 | const context = new EffectContext(callback); 10 | return () => context.dispose(); 11 | } 12 | -------------------------------------------------------------------------------- /packages/core/src/signals/createSignal.ts: -------------------------------------------------------------------------------- 1 | import {SignalContext, SignalValue, SimpleSignal} from '../signals'; 2 | import {InterpolationFunction, deepLerp} from '../tweening'; 3 | 4 | export function createSignal<TValue, TOwner = void>( 5 | initial?: SignalValue<TValue>, 6 | interpolation: InterpolationFunction<TValue> = deepLerp, 7 | owner?: TOwner, 8 | ): SimpleSignal<TValue, TOwner> { 9 | return new SignalContext<TValue, TValue, TOwner>( 10 | initial, 11 | interpolation, 12 | owner, 13 | ).toSignal(); 14 | } 15 | -------------------------------------------------------------------------------- /packages/core/src/signals/symbols.ts: -------------------------------------------------------------------------------- 1 | export const DEFAULT = Symbol.for('@motion-canvas/core/signals/default'); 2 | -------------------------------------------------------------------------------- /packages/core/src/signals/utils.ts: -------------------------------------------------------------------------------- 1 | import {SignalValue} from './types'; 2 | 3 | export function isReactive<T>(value: SignalValue<T>): value is () => T { 4 | return typeof value === 'function'; 5 | } 6 | 7 | export function modify<TFrom, TTo>( 8 | value: SignalValue<TFrom>, 9 | modification: (value: TFrom) => TTo, 10 | ): SignalValue<TTo> { 11 | return isReactive(value) ? () => modification(value()) : modification(value); 12 | } 13 | 14 | export function unwrap<T>(value: SignalValue<T>): T { 15 | return isReactive(value) ? value() : value; 16 | } 17 | -------------------------------------------------------------------------------- /packages/core/src/threading/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Thread management. 3 | * 4 | * @packageDocumentation 5 | */ 6 | 7 | export * from './Thread'; 8 | export * from './ThreadGenerator'; 9 | export * from './cancel'; 10 | export * from './join'; 11 | export * from './names'; 12 | export * from './spawn'; 13 | export * from './threads'; 14 | -------------------------------------------------------------------------------- /packages/core/src/threading/names.ts: -------------------------------------------------------------------------------- 1 | export function setTaskName(task: Generator, source: Generator | string): void { 2 | const prototype = Object.getPrototypeOf(task); 3 | if (!prototype.threadable) { 4 | prototype.threadable = true; 5 | prototype.name = typeof source === 'string' ? source : getTaskName(source); 6 | } 7 | } 8 | 9 | export function getTaskName(task: Generator): string { 10 | return Object.getPrototypeOf(task).name ?? null; 11 | } 12 | -------------------------------------------------------------------------------- /packages/core/src/transitions/fadeTransition.ts: -------------------------------------------------------------------------------- 1 | import {createSignal} from '../signals'; 2 | import {ThreadGenerator} from '../threading'; 3 | import {useTransition} from './useTransition'; 4 | 5 | /** 6 | * Perform a transition that fades between the scenes. 7 | * 8 | * @param duration - The duration of the transition. 9 | */ 10 | export function* fadeTransition(duration = 0.6): ThreadGenerator { 11 | const progress = createSignal(0); 12 | const endTransition = useTransition(ctx => { 13 | ctx.globalAlpha = progress(); 14 | }); 15 | 16 | yield* progress(1, duration); 17 | endTransition(); 18 | } 19 | -------------------------------------------------------------------------------- /packages/core/src/transitions/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Transitions between scenes. 3 | * 4 | * @packageDocumentation 5 | */ 6 | 7 | export * from './fadeTransition'; 8 | export * from './slideTransition'; 9 | export * from './useTransition'; 10 | export * from './waitTransition'; 11 | export * from './zoomInTransition'; 12 | export * from './zoomOutTransition'; 13 | -------------------------------------------------------------------------------- /packages/core/src/tweening/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Interpolation and timing of tweens. 3 | * 4 | * @packageDocumentation 5 | */ 6 | 7 | export * from './interpolationFunctions'; 8 | export * from './spring'; 9 | export * from './timingFunctions'; 10 | export * from './tween'; 11 | -------------------------------------------------------------------------------- /packages/core/src/types/Canvas.ts: -------------------------------------------------------------------------------- 1 | export type CanvasColorSpace = 'srgb' | 'display-p3'; 2 | export type CanvasOutputMimeType = 'image/png' | 'image/jpeg' | 'image/webp'; 3 | -------------------------------------------------------------------------------- /packages/core/src/types/Matrix.ts: -------------------------------------------------------------------------------- 1 | import {Vector2} from './Vector'; 2 | 3 | export function transformAngle(angle: number, matrix: DOMMatrix) { 4 | return Vector2.fromDegrees(angle).transform(matrix).degrees; 5 | } 6 | 7 | export function transformScalar(scalar: number, matrix: DOMMatrix) { 8 | return Vector2.magnitude(matrix.m11, matrix.m12) * scalar; 9 | } 10 | -------------------------------------------------------------------------------- /packages/core/src/types/Type.ts: -------------------------------------------------------------------------------- 1 | export const EPSILON = 0.000001; 2 | 3 | export interface Type { 4 | toSymbol(): symbol; 5 | } 6 | 7 | export interface WebGLConvertible { 8 | toUniform(gl: WebGL2RenderingContext, location: WebGLUniformLocation): void; 9 | } 10 | 11 | export function isType(value: any): value is Type { 12 | return value && typeof value === 'object' && 'toSymbol' in value; 13 | } 14 | -------------------------------------------------------------------------------- /packages/core/src/types/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Complex types used in animations. 3 | * 4 | * @packageDocumentation 5 | */ 6 | 7 | export * from './BBox'; 8 | export * from './Canvas'; 9 | export * from './Color'; 10 | export * from './Matrix'; 11 | export * from './Matrix2D'; 12 | export * from './Origin'; 13 | export * from './Spacing'; 14 | export * from './Type'; 15 | export * from './Vector'; 16 | -------------------------------------------------------------------------------- /packages/core/src/utils/Semaphore.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * A simple semaphore implementation with a capacity of 1. 3 | * 4 | * @internal 5 | */ 6 | export class Semaphore { 7 | private resolveCurrent: (() => void) | null = null; 8 | private current: Promise<void> | null = null; 9 | 10 | public async acquire() { 11 | while (this.current) { 12 | await this.current; 13 | } 14 | this.current = new Promise(resolve => { 15 | this.resolveCurrent = resolve; 16 | }); 17 | } 18 | 19 | public release() { 20 | this.current = null; 21 | this.resolveCurrent?.(); 22 | this.resolveCurrent = null; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /packages/core/src/utils/__logs__/experimental-features.md: -------------------------------------------------------------------------------- 1 | This feature requires enabling the `experimentalFeatures` flag in your project 2 | configuration: 3 | 4 | ```ts 5 | export default makeProject({ 6 | experimentalFeatures: true, 7 | // ... 8 | }); 9 | ``` 10 | 11 | [Learn more](https://motioncanvas.io/docs/experimental) about experimental 12 | features. 13 | -------------------------------------------------------------------------------- /packages/core/src/utils/beginSlide.ts: -------------------------------------------------------------------------------- 1 | import type {ThreadGenerator} from '../threading'; 2 | import {useScene} from './useScene'; 3 | import {useThread} from './useThread'; 4 | 5 | export function* beginSlide(name: string): ThreadGenerator { 6 | const {slides} = useScene(); 7 | const thread = useThread(); 8 | slides.register(name, thread.fixed); 9 | yield; 10 | 11 | while (slides.shouldWait(name)) { 12 | yield; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /packages/core/src/utils/capitalize.ts: -------------------------------------------------------------------------------- 1 | export function capitalize<T extends string>(value: T): Capitalize<T> { 2 | return <Capitalize<T>>(value[0].toUpperCase() + value.slice(1)); 3 | } 4 | -------------------------------------------------------------------------------- /packages/core/src/utils/deprecate.ts: -------------------------------------------------------------------------------- 1 | import {useLogger} from './useScene'; 2 | 3 | /** 4 | * Mark the given function as deprecated. 5 | * 6 | * @param fn - The function to deprecate. 7 | * @param message - The log message. 8 | * @param remarks - The optional log remarks. 9 | */ 10 | export function deprecate<TArgs extends any[], TReturn>( 11 | fn: (...args: TArgs) => TReturn, 12 | message: string, 13 | remarks?: string, 14 | ): (...args: TArgs) => TReturn { 15 | return function (this: any, ...args) { 16 | useLogger().warn({message, remarks, stack: new Error().stack}); 17 | return fn.apply(this, args); 18 | }; 19 | } 20 | -------------------------------------------------------------------------------- /packages/core/src/utils/errorToLog.ts: -------------------------------------------------------------------------------- 1 | import type {LogPayload} from '../app'; 2 | 3 | export function errorToLog(error: any): LogPayload { 4 | return { 5 | message: error.message, 6 | stack: error.stack, 7 | remarks: error.remarks, 8 | }; 9 | } 10 | -------------------------------------------------------------------------------- /packages/core/src/utils/experimentalLog.ts: -------------------------------------------------------------------------------- 1 | import {LogLevel, LogPayload} from '../app'; 2 | import experimentalFeatures from './__logs__/experimental-features.md'; 3 | 4 | export function experimentalLog(message: string, remarks?: string): LogPayload { 5 | return { 6 | level: LogLevel.Error, 7 | message, 8 | remarks: (remarks ?? '') + experimentalFeatures, 9 | }; 10 | } 11 | -------------------------------------------------------------------------------- /packages/core/src/utils/getContext.ts: -------------------------------------------------------------------------------- 1 | export function getContext( 2 | options?: CanvasRenderingContext2DSettings, 3 | canvas: HTMLCanvasElement = document.createElement('canvas'), 4 | ): CanvasRenderingContext2D { 5 | const context = canvas.getContext('2d', options); 6 | if (!context) { 7 | throw new Error('Could not create a 2D context.'); 8 | } 9 | return context; 10 | } 11 | -------------------------------------------------------------------------------- /packages/core/src/utils/math.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * A constant for converting radians to degrees 3 | * 4 | * @example 5 | * const degrees = 0.6 * RAD2DEG; 6 | */ 7 | export const RAD2DEG = 180 / Math.PI; 8 | 9 | /** 10 | * A constant for converting degrees to radians 11 | * 12 | * @example 13 | * const radians = 30 * DEG2RAD; 14 | */ 15 | export const DEG2RAD = Math.PI / 180; 16 | -------------------------------------------------------------------------------- /packages/core/src/utils/useTime.ts: -------------------------------------------------------------------------------- 1 | import {useThread} from './useThread'; 2 | 3 | /** 4 | * Get the real time since the start of the animation. 5 | * 6 | * @remarks 7 | * The returned value accounts for offsets caused by functions such as 8 | * {@link flow.waitFor}. 9 | * 10 | * @example 11 | * ```ts 12 | * // current time: 0s 13 | * yield* waitFor(0.02); 14 | * 15 | * // current time: 0.016(6)s 16 | * // real time: 0.02s 17 | * const realTime = useTime(); 18 | * ``` 19 | */ 20 | export function useTime() { 21 | return useThread().time(); 22 | } 23 | -------------------------------------------------------------------------------- /packages/core/src/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// <reference types="vite/client" /> 2 | 3 | import 'vite/types/customEvent'; 4 | 5 | declare module 'vite/types/customEvent' { 6 | interface CustomEventMap { 7 | 'motion-canvas:meta': {source: string; data: any}; 8 | 'motion-canvas:meta-ack': {source: string}; 9 | 'motion-canvas:export': { 10 | data: string; 11 | subDirectories: string[]; 12 | mimeType: string; 13 | frame: number; 14 | name: string; 15 | }; 16 | 'motion-canvas:export-ack': {frame: number}; 17 | 'motion-canvas:assets': {urls: string[]}; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /packages/core/tsconfig.build.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "exclude": ["src/**/*.test.*"] 4 | } 5 | -------------------------------------------------------------------------------- /packages/core/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "outDir": "lib", 4 | "inlineSourceMap": true, 5 | "noImplicitAny": true, 6 | "strict": true, 7 | "module": "esnext", 8 | "target": "es2020", 9 | "allowJs": true, 10 | "moduleResolution": "node", 11 | "resolveJsonModule": true, 12 | "declaration": true, 13 | "declarationMap": true, 14 | "allowSyntheticDefaultImports": true, 15 | "experimentalDecorators": true, 16 | "plugins": [ 17 | { 18 | "transform": "@motion-canvas/internal/transformers/markdown-literals.js" 19 | } 20 | ] 21 | }, 22 | "include": ["src"] 23 | } 24 | -------------------------------------------------------------------------------- /packages/core/tsconfig.project.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "sourceMap": true, 4 | "noImplicitAny": true, 5 | "module": "esnext", 6 | "target": "es2020", 7 | "allowJs": true, 8 | "noEmit": true, 9 | "moduleResolution": "node", 10 | "resolveJsonModule": true, 11 | "allowSyntheticDefaultImports": true, 12 | "experimentalDecorators": true 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /packages/core/tsdoc.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://developer.microsoft.com/json-schemas/tsdoc/v0/tsdoc.schema.json", 3 | "extends": ["../../tsdoc.json"] 4 | } 5 | -------------------------------------------------------------------------------- /packages/core/vitest.config.ts: -------------------------------------------------------------------------------- 1 | import markdownLiterals from '@motion-canvas/internal/vite/markdown-literals'; 2 | import {defineConfig} from 'vitest/config'; 3 | 4 | export default defineConfig({ 5 | plugins: [markdownLiterals()], 6 | test: { 7 | environment: 'jsdom', 8 | setupFiles: ['./vitest.setup.ts'], 9 | }, 10 | }); 11 | -------------------------------------------------------------------------------- /packages/core/vitest.setup.ts: -------------------------------------------------------------------------------- 1 | import {vi} from 'vitest'; 2 | 3 | vi.stubGlobal('DOMMatrix', class {}); 4 | -------------------------------------------------------------------------------- /packages/create/template-2d-js/gitignore: -------------------------------------------------------------------------------- 1 | # Generated files 2 | node_modules 3 | output 4 | dist 5 | 6 | # Editor directories and files 7 | .vscode/* 8 | !.vscode/extensions.json 9 | .idea 10 | .DS_Store 11 | *.suo 12 | *.ntvs* 13 | *.njsproj 14 | *.sln 15 | *.sw? 16 | -------------------------------------------------------------------------------- /packages/create/template-2d-js/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "2d-starter", 3 | "private": true, 4 | "version": "0.0.0", 5 | "scripts": { 6 | "start": "vite", 7 | "serve": "vite", 8 | "build": "vite build" 9 | }, 10 | "dependencies": { 11 | "@motion-canvas/core": "*", 12 | "@motion-canvas/2d": "*" 13 | }, 14 | "devDependencies": { 15 | "@motion-canvas/ui": "*", 16 | "@motion-canvas/vite-plugin": "*", 17 | "vite": "^4.0.0" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /packages/create/template-2d-js/public/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/motion-canvas/motion-canvas/00639cd4cca76d60275d48fa15211d1e17e78228/packages/create/template-2d-js/public/.gitkeep -------------------------------------------------------------------------------- /packages/create/template-2d-js/src/project.js: -------------------------------------------------------------------------------- 1 | import {makeProject} from '@motion-canvas/core'; 2 | 3 | import example from './scenes/example?scene'; 4 | 5 | export default makeProject({ 6 | scenes: [example], 7 | }); 8 | -------------------------------------------------------------------------------- /packages/create/template-2d-js/src/scenes/example.jsx: -------------------------------------------------------------------------------- 1 | import {Circle, makeScene2D} from '@motion-canvas/2d'; 2 | import {createRef} from '@motion-canvas/core'; 3 | 4 | export default makeScene2D(function* (view) { 5 | // Create your animations here 6 | 7 | const circle = createRef(); 8 | 9 | view.add(<Circle ref={circle} size={320} fill={'lightseagreen'} />); 10 | 11 | yield* circle().scale(2, 2).to(1, 2); 12 | }); 13 | -------------------------------------------------------------------------------- /packages/create/template-2d-ts/gitignore: -------------------------------------------------------------------------------- 1 | # Generated files 2 | node_modules 3 | output 4 | dist 5 | 6 | # Editor directories and files 7 | .vscode/* 8 | !.vscode/extensions.json 9 | .idea 10 | .DS_Store 11 | *.suo 12 | *.ntvs* 13 | *.njsproj 14 | *.sln 15 | *.sw? 16 | -------------------------------------------------------------------------------- /packages/create/template-2d-ts/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "2d-starter", 3 | "private": true, 4 | "version": "0.0.0", 5 | "scripts": { 6 | "start": "vite", 7 | "serve": "vite", 8 | "build": "tsc && vite build" 9 | }, 10 | "dependencies": { 11 | "@motion-canvas/core": "*", 12 | "@motion-canvas/2d": "*" 13 | }, 14 | "devDependencies": { 15 | "@motion-canvas/ui": "*", 16 | "@motion-canvas/vite-plugin": "*", 17 | "typescript": "^5.2.2", 18 | "vite": "^4.0.0" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /packages/create/template-2d-ts/public/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/motion-canvas/motion-canvas/00639cd4cca76d60275d48fa15211d1e17e78228/packages/create/template-2d-ts/public/.gitkeep -------------------------------------------------------------------------------- /packages/create/template-2d-ts/src/motion-canvas.d.ts: -------------------------------------------------------------------------------- 1 | /// <reference types="@motion-canvas/core/project" /> 2 | -------------------------------------------------------------------------------- /packages/create/template-2d-ts/src/project.ts: -------------------------------------------------------------------------------- 1 | import {makeProject} from '@motion-canvas/core'; 2 | 3 | import example from './scenes/example?scene'; 4 | 5 | export default makeProject({ 6 | scenes: [example], 7 | }); 8 | -------------------------------------------------------------------------------- /packages/create/template-2d-ts/src/scenes/example.tsx: -------------------------------------------------------------------------------- 1 | import {Circle, makeScene2D} from '@motion-canvas/2d'; 2 | import {createRef} from '@motion-canvas/core'; 3 | 4 | export default makeScene2D(function* (view) { 5 | // Create your animations here 6 | 7 | const circle = createRef<Circle>(); 8 | 9 | view.add(<Circle ref={circle} size={320} fill={'lightseagreen'} />); 10 | 11 | yield* circle().scale(2, 2).to(1, 2); 12 | }); 13 | -------------------------------------------------------------------------------- /packages/create/template-2d-ts/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@motion-canvas/2d/tsconfig.project.json", 3 | "include": ["src"] 4 | } 5 | -------------------------------------------------------------------------------- /packages/docs/.gitignore: -------------------------------------------------------------------------------- 1 | # Dependencies 2 | /node_modules 3 | 4 | # Production 5 | /build 6 | 7 | # Generated files 8 | .docusaurus 9 | .cache-loader 10 | docs/core-api 11 | docs/2d-api 12 | static/examples 13 | static/editor 14 | static/modules 15 | src/generated 16 | 17 | # Misc 18 | .DS_Store 19 | .env.local 20 | .env.development.local 21 | .env.test.local 22 | .env.production.local 23 | 24 | npm-debug.log* 25 | yarn-debug.log* 26 | yarn-error.log* 27 | -------------------------------------------------------------------------------- /packages/docs/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [require.resolve('@docusaurus/core/lib/babel/preset')], 3 | }; 4 | -------------------------------------------------------------------------------- /packages/docs/blog/2023-04-06-example-cubic.tsx: -------------------------------------------------------------------------------- 1 | // snippet Cubic Bezier 2 | import {CubicBezier, makeScene2D} from '@motion-canvas/2d'; 3 | import {createRef} from '@motion-canvas/core'; 4 | 5 | export default makeScene2D(function* (view) { 6 | const bezier = createRef<CubicBezier>(); 7 | 8 | view.add( 9 | <CubicBezier 10 | ref={bezier} 11 | lineWidth={6} 12 | stroke={'lightseagreen'} 13 | p0={[-200, 0]} 14 | p1={[50, -200]} 15 | p2={[-50, 200]} 16 | p3={[200, 0]} 17 | />, 18 | ); 19 | 20 | yield* bezier().start(1, 1); 21 | yield* bezier().start(0).end(0).end(1, 1); 22 | }); 23 | -------------------------------------------------------------------------------- /packages/docs/blog/2023-04-06-example-quadratic.tsx: -------------------------------------------------------------------------------- 1 | // snippet Quadratic Bezier 2 | import {QuadBezier, makeScene2D} from '@motion-canvas/2d'; 3 | import {createRef} from '@motion-canvas/core'; 4 | 5 | export default makeScene2D(function* (view) { 6 | const bezier = createRef<QuadBezier>(); 7 | 8 | view.add( 9 | <QuadBezier 10 | ref={bezier} 11 | lineWidth={6} 12 | stroke={'lightseagreen'} 13 | p0={[-200, 70]} 14 | p1={[0, -200]} 15 | p2={[200, 70]} 16 | />, 17 | ); 18 | 19 | yield* bezier().start(1, 1); 20 | yield* bezier().start(0).end(0).end(1, 1); 21 | }); 22 | -------------------------------------------------------------------------------- /packages/docs/blog/2023-07-23-curve.tsx: -------------------------------------------------------------------------------- 1 | import {Circle, makeScene2D} from '@motion-canvas/2d'; 2 | import {all, createRef, easeInCubic, easeOutCubic} from '@motion-canvas/core'; 3 | 4 | export default makeScene2D(function* (view) { 5 | const ref = createRef<Circle>(); 6 | view.add( 7 | <Circle 8 | ref={ref} 9 | size={160} 10 | stroke={'lightseagreen'} 11 | lineWidth={8} 12 | endAngle={270} 13 | endArrow 14 | />, 15 | ); 16 | 17 | yield* all(ref().start(1, 1), ref().rotation(180, 1, easeInCubic)); 18 | ref().start(0).end(0); 19 | yield* all(ref().end(1, 1), ref().rotation(360, 1, easeOutCubic)); 20 | }); 21 | -------------------------------------------------------------------------------- /packages/docs/blog/2023-07-23-path.tsx: -------------------------------------------------------------------------------- 1 | import {makeScene2D, Path} from '@motion-canvas/2d'; 2 | 3 | export default makeScene2D(function* (view) { 4 | view.add( 5 | <Path 6 | scale={8} 7 | position={-96} 8 | fill={'lightseagreen'} 9 | data={ 10 | 'M4 6.47L5.76 10H20v8H4V6.47M22 4h-4l2 4h-3l-2-4h-2l2 4h-3l-2-4H8l2 4H7L5 4H4c-1.1 0-1.99.9-1.99 2L2 18c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V4z' 11 | } 12 | />, 13 | ); 14 | }); 15 | -------------------------------------------------------------------------------- /packages/docs/blog/2023-10-14-skew.tsx: -------------------------------------------------------------------------------- 1 | import {Img, makeScene2D} from '@motion-canvas/2d'; 2 | import {createRef, easeOutElastic} from '@motion-canvas/core'; 3 | 4 | export default makeScene2D(function* (view) { 5 | const ref = createRef<Img>(); 6 | yield view.add( 7 | <Img 8 | ref={ref} 9 | skew={[-24, -12]} 10 | src="https://images.unsplash.com/photo-1696931073577-5638a6891e1e" 11 | width={240} 12 | radius={20} 13 | />, 14 | ); 15 | 16 | yield* ref().skew([24, 12], 1, easeOutElastic).back(1); 17 | }); 18 | -------------------------------------------------------------------------------- /packages/docs/blog/2023-12-31-glob.tsx: -------------------------------------------------------------------------------- 1 | import motionCanvas from '@motion-canvas/vite-plugin'; 2 | import {defineConfig} from 'vite'; 3 | 4 | export default defineConfig({ 5 | plugins: [ 6 | motionCanvas({ 7 | project: './src/projects/*.ts', 8 | }), 9 | ], 10 | }); 11 | -------------------------------------------------------------------------------- /packages/docs/blog/2023-12-31-spawner.tsx: -------------------------------------------------------------------------------- 1 | view.add( 2 | // before 3 | <Node spawner={() => range(count()).map(() => <Node />)} />, 4 | ); 5 | 6 | view.add( 7 | // after 8 | <Node>{() => range(count()).map(() => <Node />)}</Node>, 9 | ); 10 | -------------------------------------------------------------------------------- /packages/docs/blog/2023-12-31-txt.tsx: -------------------------------------------------------------------------------- 1 | import {Txt, makeScene2D} from '@motion-canvas/2d'; 2 | 3 | export default makeScene2D(function* (view) { 4 | view.fill('#141414'); 5 | view.add( 6 | // prettier-ignore 7 | <Txt fill={'white'} fontSize={32} width={720} textWrap> 8 | Whereas recognition of the inherent dignity and of the{' '} 9 | <Txt.i>equal</Txt.i> and inalienable rights of all members of 10 | the human family is the foundation of <Txt.b>freedom</Txt.b>, 11 | justice and <Txt fill="#25C281">peace</Txt> in the world. 12 | </Txt>, 13 | ); 14 | }); 15 | -------------------------------------------------------------------------------- /packages/docs/blog/2024-01-10-version-3.13.0-scene-graph.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/motion-canvas/motion-canvas/00639cd4cca76d60275d48fa15211d1e17e78228/packages/docs/blog/2024-01-10-version-3.13.0-scene-graph.png -------------------------------------------------------------------------------- /packages/docs/blog/2024-01-10-version-3.13.0-scene-graph.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/motion-canvas/motion-canvas/00639cd4cca76d60275d48fa15211d1e17e78228/packages/docs/blog/2024-01-10-version-3.13.0-scene-graph.webp -------------------------------------------------------------------------------- /packages/docs/blog/2024-03-21-defaults.tsx: -------------------------------------------------------------------------------- 1 | import {Txt, withDefaults} from '@motion-canvas/2d'; 2 | 3 | export const MyTxt = withDefaults(Txt, { 4 | fill: 'rgba(255, 255, 255, 0.6)', 5 | fontFamily: 'JetBrains Mono', 6 | fontSize: 28, 7 | }); 8 | -------------------------------------------------------------------------------- /packages/docs/blog/2024-03-21-loop.tsx: -------------------------------------------------------------------------------- 1 | // These two are equivalent: 2 | // prettier-ignore 3 | yield* loop(() => { /* animation */ }); 4 | // prettier-ignore 5 | yield* loop(Infinity, () => { /* animation */ }); 6 | -------------------------------------------------------------------------------- /packages/docs/blog/2024-05-16-effects.tsx: -------------------------------------------------------------------------------- 1 | import {makeScene2D} from '@motion-canvas/2d'; 2 | import {createEffect, createSignal} from '@motion-canvas/core'; 3 | 4 | export default makeScene2D(function* () { 5 | const signal = createSignal(0); 6 | 7 | createEffect(() => { 8 | console.log('Signal changed: ', signal()); 9 | }); 10 | 11 | yield* signal(1, 2); 12 | }); 13 | -------------------------------------------------------------------------------- /packages/docs/blog/2024-05-16-vectors.tsx: -------------------------------------------------------------------------------- 1 | // Before 2 | // prettier-ignore 3 | yield* node().position(node().position().add([200, 100]), 2); 4 | 5 | // Now 6 | // prettier-ignore 7 | yield* node().position.add([200, 100], 2); 8 | -------------------------------------------------------------------------------- /packages/docs/blog/2024-08-13-latex.tsx: -------------------------------------------------------------------------------- 1 | import {Latex, makeScene2D} from '@motion-canvas/2d'; 2 | import {createRef, waitFor} from '@motion-canvas/core'; 3 | 4 | export default makeScene2D(function* (view) { 5 | const tex = createRef<Latex>(); 6 | view.add(<Latex ref={tex} tex="{{y=}}{{a}}{{x^2}}" fill="white" />); 7 | 8 | yield* waitFor(0.2); 9 | yield* tex().tex('{{y=}}{{a}}{{x^2}} + {{bx}}', 1); 10 | yield* waitFor(0.2); 11 | yield* tex().tex( 12 | '{{y=}}{{\\left(}}{{a}}{{x^2}} + {{bx}}{{\\over 1}}{{\\right)}}', 13 | 1, 14 | ); 15 | yield* waitFor(0.2); 16 | yield* tex().tex('{{y=}}{{a}}{{x^2}}', 1); 17 | }); 18 | -------------------------------------------------------------------------------- /packages/docs/blog/authors.yml: -------------------------------------------------------------------------------- 1 | aarthificial: 2 | name: Jacob 3 | title: Motion Canvas Creator 4 | url: https://twitter.com/aarthificial 5 | image_url: https://github.com/aarthificial.png 6 | -------------------------------------------------------------------------------- /packages/docs/docs/advanced/_category_.yml: -------------------------------------------------------------------------------- 1 | label: Advanced 2 | position: 4 3 | link: 4 | type: generated-index 5 | -------------------------------------------------------------------------------- /packages/docs/docs/advanced/code/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@motion-canvas/2d/tsconfig.project.json", 3 | "include": ["."] 4 | } 5 | -------------------------------------------------------------------------------- /packages/docs/docs/components/_category_.yml: -------------------------------------------------------------------------------- 1 | label: Components 2 | position: 3 3 | link: 4 | type: generated-index 5 | -------------------------------------------------------------------------------- /packages/docs/docs/getting-started/_category_.yml: -------------------------------------------------------------------------------- 1 | label: Getting Started 2 | position: 2 3 | collapsed: false 4 | link: 5 | type: generated-index 6 | -------------------------------------------------------------------------------- /packages/docs/docs/migration/_category_.yml: -------------------------------------------------------------------------------- 1 | label: Migration 2 | position: 100 3 | link: 4 | type: generated-index 5 | -------------------------------------------------------------------------------- /packages/docs/docs/migration/updating.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: -1 3 | slug: /updating 4 | --- 5 | 6 | # Update Guide 7 | 8 | The latest patch notes can be found 9 | [here](https://github.com/motion-canvas/motion-canvas/releases). 10 | 11 | To update all the `@motion-canvas` packages to the latest version use the 12 | following: 13 | 14 | ```bash 15 | npm update --save @motion-canvas/2d @motion-canvas/core @motion-canvas/ui @motion-canvas/vite-plugin 16 | ``` 17 | 18 | ## Troubleshooting 19 | 20 | If you get any errors, try removing `package-lock.json` and the `node_modules` 21 | folder, then run `npm install` again. 22 | -------------------------------------------------------------------------------- /packages/docs/sidebars.js: -------------------------------------------------------------------------------- 1 | // @ts-check 2 | 3 | /** @type {import('@docusaurus/plugin-content-docs').SidebarsConfig} */ 4 | const sidebars = { 5 | docs: [ 6 | { 7 | type: 'autogenerated', 8 | dirName: '.', 9 | }, 10 | ], 11 | }; 12 | 13 | module.exports = sidebars; 14 | -------------------------------------------------------------------------------- /packages/docs/src/Icon/Code/index.tsx: -------------------------------------------------------------------------------- 1 | import React, {ComponentProps} from 'react'; 2 | 3 | export default function IconCode({ 4 | width = 24, 5 | height = 24, 6 | ...props 7 | }: ComponentProps<'svg'>): JSX.Element { 8 | return ( 9 | <svg 10 | viewBox="0 0 24 24" 11 | width={width} 12 | height={height} 13 | aria-hidden 14 | {...props} 15 | > 16 | <path 17 | d="M9.4 16.6L4.8 12l4.6-4.6L8 6l-6 6 6 6 1.4-1.4zm5.2 0l4.6-4.6-4.6-4.6L16 6l6 6-6 6-1.4-1.4z" 18 | fill="currentColor" 19 | /> 20 | </svg> 21 | ); 22 | } 23 | -------------------------------------------------------------------------------- /packages/docs/src/Icon/Filters/index.tsx: -------------------------------------------------------------------------------- 1 | import React, {ComponentProps} from 'react'; 2 | 3 | export default function IconFilters({ 4 | width = 24, 5 | height = 24, 6 | ...props 7 | }: ComponentProps<'svg'>): JSX.Element { 8 | return ( 9 | <svg 10 | viewBox="0 0 24 24" 11 | width={width} 12 | height={height} 13 | aria-hidden 14 | {...props} 15 | > 16 | <path 17 | d="M10 18h4v-2h-4v2zM3 6v2h18V6H3zm3 7h12v-2H6v2z" 18 | fill="currentColor" 19 | /> 20 | </svg> 21 | ); 22 | } 23 | -------------------------------------------------------------------------------- /packages/docs/src/Icon/Image/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | export default function Image() { 4 | return ( 5 | <svg width={24} height={24} viewBox="0 0 24 24" fill="currentColor"> 6 | <path d="M21 19V5c0-1.1-.9-2-2-2H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2zM8.5 13.5l2.5 3.01L14.5 12l4.5 6H5l3.5-4.5z" /> 7 | </svg> 8 | ); 9 | } 10 | -------------------------------------------------------------------------------- /packages/docs/src/Icon/Pause/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | export function Pause() { 4 | return ( 5 | <svg width={24} height={24} viewBox="0 0 24 24" fill="currentColor"> 6 | <path d="M6 19h4V5H6v14zm8-14v14h4V5h-4z" /> 7 | </svg> 8 | ); 9 | } 10 | -------------------------------------------------------------------------------- /packages/docs/src/Icon/PhotoCamera/index.tsx: -------------------------------------------------------------------------------- 1 | import React, {ComponentProps} from 'react'; 2 | 3 | export function PhotoCamera({ 4 | width = 24, 5 | height = 24, 6 | ...props 7 | }: ComponentProps<'svg'>): JSX.Element { 8 | return ( 9 | <svg 10 | width={width} 11 | height={height} 12 | viewBox="0 0 24 24" 13 | fill="currentColor" 14 | {...props} 15 | > 16 | <circle cx="12" cy="12" r="3.2" /> 17 | <path d="M9 2L7.17 4H4c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2h-3.17L15 2H9zm3 15c-2.76 0-5-2.24-5-5s2.24-5 5-5 5 2.24 5 5-2.24 5-5 5z" /> 18 | </svg> 19 | ); 20 | } 21 | -------------------------------------------------------------------------------- /packages/docs/src/Icon/PlayArrow/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | export function PlayArrow() { 4 | return ( 5 | <svg width={24} height={24} viewBox="0 0 24 24" fill="currentColor"> 6 | <path d="M8 5v14l11-7z" /> 7 | </svg> 8 | ); 9 | } 10 | -------------------------------------------------------------------------------- /packages/docs/src/Icon/Science/index.tsx: -------------------------------------------------------------------------------- 1 | import React, {ComponentProps} from 'react'; 2 | 3 | export default function IconScience({ 4 | width = 24, 5 | height = 24, 6 | ...props 7 | }: ComponentProps<'svg'>): JSX.Element { 8 | return ( 9 | <svg 10 | viewBox="0 0 24 24" 11 | width={width} 12 | height={height} 13 | aria-hidden 14 | fill="currentColor" 15 | {...props} 16 | > 17 | <path d="M19.8,18.4L14,10.67V6.5l1.35-1.69C15.61,4.48,15.38,4,14.96,4H9.04C8.62,4,8.39,4.48,8.65,4.81L10,6.5v4.17L4.2,18.4 C3.71,19.06,4.18,20,5,20h14C19.82,20,20.29,19.06,19.8,18.4z" /> 18 | </svg> 19 | ); 20 | } 21 | -------------------------------------------------------------------------------- /packages/docs/src/Icon/SkipNext/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | export function SkipNext() { 4 | return ( 5 | <svg width={24} height={24} viewBox="0 0 24 24" fill="currentColor"> 6 | <path d="M6 18l8.5-6L6 6v12zM16 6v12h2V6h-2z" /> 7 | </svg> 8 | ); 9 | } 10 | -------------------------------------------------------------------------------- /packages/docs/src/Icon/SkipPrevious/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | export function SkipPrevious() { 4 | return ( 5 | <svg width={24} height={24} viewBox="0 0 24 24" fill="currentColor"> 6 | <path d="M6 6h2v12H6zm3.5 6l8.5 6V6z" /> 7 | </svg> 8 | ); 9 | } 10 | -------------------------------------------------------------------------------- /packages/docs/src/Icon/Split/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | export default function Split() { 4 | return ( 5 | <svg width={24} height={24} viewBox="0 0 24 24" fill="currentColor"> 6 | <path d="M3 15h8v-2H3v2zm0 4h8v-2H3v2zm0-8h8V9H3v2zm0-6v2h8V5H3zm10 0h8v14h-8V5z" /> 7 | </svg> 8 | ); 9 | } 10 | -------------------------------------------------------------------------------- /packages/docs/src/Icon/Text/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | export default function Text() { 4 | return ( 5 | <svg width={24} height={24} viewBox="0 0 24 24" fill="currentColor"> 6 | <path d="M4 15h16v-2H4v2zm0 4h16v-2H4v2zm0-8h16V9H4v2zm0-6v2h16V5H4z" /> 7 | </svg> 8 | ); 9 | } 10 | -------------------------------------------------------------------------------- /packages/docs/src/Icon/VideoSettings/index.tsx: -------------------------------------------------------------------------------- 1 | import React, {ComponentProps} from 'react'; 2 | 3 | export function VideoSettings({ 4 | width = 24, 5 | height = 24, 6 | ...props 7 | }: ComponentProps<'svg'>): JSX.Element { 8 | return ( 9 | <svg 10 | width={width} 11 | height={height} 12 | viewBox="0 0 24 24" 13 | fill="currentColor" 14 | {...props} 15 | > 16 | <path d="M15 8v8H5V8h10m1-2H4c-.55 0-1 .45-1 1v10c0 .55.45 1 1 1h12c.55 0 1-.45 1-1v-3.5l4 4v-11l-4 4V7c0-.55-.45-1-1-1z" /> 17 | </svg> 18 | ); 19 | } 20 | -------------------------------------------------------------------------------- /packages/docs/src/components/Api/ApiSnippet.tsx: -------------------------------------------------------------------------------- 1 | import Item from '@site/src/components/Api/Item'; 2 | import {useUrlLookup} from '@site/src/contexts/api'; 3 | import React from 'react'; 4 | 5 | export default function ApiSnippet({url}: {url: string}) { 6 | const reflection = useUrlLookup()(url); 7 | return <Item reflection={reflection} headless />; 8 | } 9 | -------------------------------------------------------------------------------- /packages/docs/src/components/Api/Code/Container.tsx: -------------------------------------------------------------------------------- 1 | import React, {ReactNode} from 'react'; 2 | 3 | import Container from '@theme/CodeBlock/Container'; 4 | import clsx from 'clsx'; 5 | import styles from './styles.module.css'; 6 | 7 | export default function ApiContainer({ 8 | children, 9 | }: { 10 | children: ReactNode | ReactNode[]; 11 | }) { 12 | return ( 13 | <Container 14 | as="div" 15 | className={clsx(styles.codeBlockContainer, 'language-typescript')} 16 | > 17 | {children} 18 | </Container> 19 | ); 20 | } 21 | -------------------------------------------------------------------------------- /packages/docs/src/components/Api/Comment/Summary.tsx: -------------------------------------------------------------------------------- 1 | import * as summaries from '@site/src/generated/markdown'; 2 | import React from 'react'; 3 | 4 | export default function Summary({id}: {id: string}) { 5 | const Content = summaries[id] ?? React.Fragment; 6 | return <Content />; 7 | } 8 | -------------------------------------------------------------------------------- /packages/docs/src/components/Api/Filters/index.module.css: -------------------------------------------------------------------------------- 1 | .header { 2 | flex-direction: row-reverse; 3 | } 4 | 5 | .filters { 6 | width: auto; 7 | flex-shrink: 0; 8 | flex-grow: 0; 9 | white-space: nowrap; 10 | display: flex; 11 | justify-content: flex-end; 12 | align-items: flex-start; 13 | } 14 | 15 | .icon { 16 | margin-left: 0.3em; 17 | margin-bottom: -0.3em; 18 | } 19 | -------------------------------------------------------------------------------- /packages/docs/src/components/Api/Preview/FunctionPreview.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import SignaturePreview from '@site/src/components/Api/Preview/SignaturePreview'; 4 | import type {JSONOutput} from 'typedoc'; 5 | 6 | export default function FunctionPreview({ 7 | reflection, 8 | }: { 9 | reflection: JSONOutput.DeclarationReflection; 10 | }) { 11 | const signature = 12 | reflection.signatures?.[0] ?? 13 | reflection.getSignature ?? 14 | reflection.setSignature ?? 15 | reflection.indexSignature; 16 | 17 | return <SignaturePreview reflection={signature} />; 18 | } 19 | -------------------------------------------------------------------------------- /packages/docs/src/components/Api/Preview/PropertyPreview.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import ParameterPreview from '@site/src/components/Api/Preview/ParameterPreview'; 4 | import type {JSONOutput} from 'typedoc'; 5 | 6 | export default function PropertyPreview({ 7 | reflection, 8 | }: { 9 | reflection: JSONOutput.SignatureReflection; 10 | }) { 11 | return <ParameterPreview reflection={reflection} />; 12 | } 13 | -------------------------------------------------------------------------------- /packages/docs/src/components/Api/ReflectionKind.ts: -------------------------------------------------------------------------------- 1 | export enum ReflectionKind { 2 | Project = 1, 3 | Module = 2, 4 | Namespace = 4, 5 | Enum = 8, 6 | EnumMember = 16, 7 | Variable = 32, 8 | Function = 64, 9 | Class = 128, 10 | Interface = 256, 11 | Constructor = 512, 12 | Property = 1024, 13 | Method = 2048, 14 | CallSignature = 4096, 15 | IndexSignature = 8192, 16 | ConstructorSignature = 16384, 17 | Parameter = 32768, 18 | TypeLiteral = 65536, 19 | TypeParameter = 131072, 20 | Accessor = 262144, 21 | GetSignature = 524288, 22 | SetSignature = 1048576, 23 | ObjectLiteral = 2097152, 24 | TypeAlias = 4194304, 25 | Reference = 8388608, 26 | } 27 | -------------------------------------------------------------------------------- /packages/docs/src/components/Api/Type/ArrayType.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import Type from '@site/src/components/Api/Type'; 4 | import type {JSONOutput} from 'typedoc'; 5 | 6 | export default function ArrayType({type}: {type: JSONOutput.ArrayType}) { 7 | return ( 8 | <> 9 | <Type type={type.elementType} /> 10 | [] 11 | </> 12 | ); 13 | } 14 | -------------------------------------------------------------------------------- /packages/docs/src/components/Api/Type/ConditionalType.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import Token from '@site/src/components/Api/Code/Token'; 4 | import Type from '@site/src/components/Api/Type'; 5 | import type {JSONOutput} from 'typedoc'; 6 | 7 | export default function ConditionalType({ 8 | type, 9 | }: { 10 | type: JSONOutput.ConditionalType; 11 | }) { 12 | return ( 13 | <> 14 | <Type type={type.checkType} /> 15 | <Token type="keyword"> extends </Token> 16 | <Type type={type.extendsType} /> 17 | {' ? '} 18 | <Type type={type.trueType} /> 19 | {' : '} 20 | <Type type={type.falseType} /> 21 | </> 22 | ); 23 | } 24 | -------------------------------------------------------------------------------- /packages/docs/src/components/Api/Type/IndexedAccessType.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import Type from '@site/src/components/Api/Type'; 4 | import type {JSONOutput} from 'typedoc'; 5 | 6 | export default function IndexedAccessType({ 7 | type, 8 | }: { 9 | type: JSONOutput.IndexedAccessType; 10 | }) { 11 | return ( 12 | <> 13 | <Type type={type.objectType} /> 14 | [<Type type={type.indexType} />] 15 | </> 16 | ); 17 | } 18 | -------------------------------------------------------------------------------- /packages/docs/src/components/Api/Type/InferredType.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import Token from '@site/src/components/Api/Code/Token'; 4 | import type {JSONOutput} from 'typedoc'; 5 | 6 | export default function InferredType({type}: {type: JSONOutput.InferredType}) { 7 | return ( 8 | <> 9 | <Token type="keyword">infer </Token> 10 | <Token type="constant">{type.name}</Token> 11 | </> 12 | ); 13 | } 14 | -------------------------------------------------------------------------------- /packages/docs/src/components/Api/Type/IntersectionType.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import TokenList, { 4 | ListType, 5 | Separator, 6 | } from '@site/src/components/Api/Code/TokenList'; 7 | import Type from '@site/src/components/Api/Type'; 8 | import type {JSONOutput} from 'typedoc'; 9 | 10 | export default function IntersectionType({ 11 | type, 12 | }: { 13 | type: JSONOutput.IntersectionType; 14 | }) { 15 | return ( 16 | <TokenList type={ListType.Parentheses} separator={Separator.Ampersand}> 17 | {type.types.map((item, index) => ( 18 | <Type key={index} type={item} /> 19 | ))} 20 | </TokenList> 21 | ); 22 | } 23 | -------------------------------------------------------------------------------- /packages/docs/src/components/Api/Type/IntrinsicType.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import Token from '@site/src/components/Api/Code/Token'; 4 | import type {JSONOutput} from 'typedoc'; 5 | 6 | export default function IntrinsicType({ 7 | type, 8 | }: { 9 | type: JSONOutput.IntrinsicType; 10 | }) { 11 | return <Token type="keyword">{type.name}</Token>; 12 | } 13 | -------------------------------------------------------------------------------- /packages/docs/src/components/Api/Type/NamedTupleMemberType.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import Type from '@site/src/components/Api/Type'; 4 | import type {JSONOutput} from 'typedoc'; 5 | 6 | export default function NamedTupleMemberType({ 7 | type, 8 | }: { 9 | type: JSONOutput.NamedTupleMemberType; 10 | }) { 11 | return ( 12 | <> 13 | {type.name}: <Type type={type.element} /> 14 | </> 15 | ); 16 | } 17 | -------------------------------------------------------------------------------- /packages/docs/src/components/Api/Type/PredicateType.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import Token from '@site/src/components/Api/Code/Token'; 4 | import Type from '@site/src/components/Api/Type'; 5 | import type {JSONOutput} from 'typedoc'; 6 | 7 | export default function PredicateType({ 8 | type, 9 | }: { 10 | type: JSONOutput.PredicateType; 11 | }) { 12 | return type.asserts ? ( 13 | <> 14 | <Token type="keyword">asserts </Token> 15 | <Token>{type.name} </Token> 16 | </> 17 | ) : ( 18 | <> 19 | <Token>{type.name} </Token> 20 | <Token type="keyword">is </Token> 21 | <Type type={type.targetType} /> 22 | </> 23 | ); 24 | } 25 | -------------------------------------------------------------------------------- /packages/docs/src/components/Api/Type/QueryType.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import Token from '@site/src/components/Api/Code/Token'; 4 | import Type from '@site/src/components/Api/Type'; 5 | import type {JSONOutput} from 'typedoc'; 6 | 7 | export default function QueryType({type}: {type: JSONOutput.QueryType}) { 8 | return ( 9 | <> 10 | <Token type="keyword">typeof </Token> 11 | <Type type={type.queryType} /> 12 | </> 13 | ); 14 | } 15 | -------------------------------------------------------------------------------- /packages/docs/src/components/Api/Type/ReflectionType.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import Preview from '@site/src/components/Api/Preview'; 4 | import {useApiFinder} from '@site/src/contexts/api'; 5 | import type {JSONOutput} from 'typedoc'; 6 | 7 | export default function ReflectionType({ 8 | type, 9 | }: { 10 | type: JSONOutput.ReflectionType; 11 | }) { 12 | const find = useApiFinder(); 13 | return <Preview reflection={find(type.declaration)} />; 14 | } 15 | -------------------------------------------------------------------------------- /packages/docs/src/components/Api/Type/TupleType.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import TokenList, {ListType} from '@site/src/components/Api/Code/TokenList'; 4 | import Type from '@site/src/components/Api/Type'; 5 | import type {JSONOutput} from 'typedoc'; 6 | 7 | export default function TupleType({type}: {type: JSONOutput.TupleType}) { 8 | return type.elements ? ( 9 | <TokenList type={ListType.Square}> 10 | {type.elements.map((type, index) => ( 11 | <Type key={index} type={type} /> 12 | ))} 13 | </TokenList> 14 | ) : ( 15 | <>[]</> 16 | ); 17 | } 18 | -------------------------------------------------------------------------------- /packages/docs/src/components/Api/Type/TypeOperatorType.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import Token from '@site/src/components/Api/Code/Token'; 4 | import Type from '@site/src/components/Api/Type'; 5 | import type {JSONOutput} from 'typedoc'; 6 | 7 | export default function TypeOperatorType({ 8 | type, 9 | }: { 10 | type: JSONOutput.TypeOperatorType; 11 | }) { 12 | return ( 13 | <> 14 | <Token type="keyword">{type.operator} </Token> 15 | <Type type={type.target} /> 16 | </> 17 | ); 18 | } 19 | -------------------------------------------------------------------------------- /packages/docs/src/components/Api/Type/UnionType.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import TokenList, { 4 | ListType, 5 | Separator, 6 | } from '@site/src/components/Api/Code/TokenList'; 7 | import Type from '@site/src/components/Api/Type'; 8 | import type {JSONOutput} from 'typedoc'; 9 | 10 | export default function UnionType({type}: {type: JSONOutput.UnionType}) { 11 | return ( 12 | <TokenList type={ListType.Parentheses} separator={Separator.Pipe}> 13 | {type.types.map((item, index) => ( 14 | <Type key={index} type={item} /> 15 | ))} 16 | </TokenList> 17 | ); 18 | } 19 | -------------------------------------------------------------------------------- /packages/docs/src/components/DocPage/index.tsx: -------------------------------------------------------------------------------- 1 | import Tooltip from '@site/src/components/Tooltip'; 2 | import {ApiProvider} from '@site/src/contexts/api'; 3 | import {ThemeDictProvider} from '@site/src/contexts/codeTheme'; 4 | import api from '@site/src/generated/api'; 5 | import DocItem from '@theme/DocItem'; 6 | import React from 'react'; 7 | 8 | export default function DocPage(props) { 9 | return ( 10 | <ApiProvider lookup={api.lookups} urlLookup={api.urlLookups}> 11 | <ThemeDictProvider> 12 | <Tooltip> 13 | <DocItem {...props} /> 14 | </Tooltip> 15 | </ThemeDictProvider> 16 | </ApiProvider> 17 | ); 18 | } 19 | -------------------------------------------------------------------------------- /packages/docs/src/components/ExperimentalWarning/index.tsx: -------------------------------------------------------------------------------- 1 | import Link from '@docusaurus/Link'; 2 | import Admonition from '@theme/Admonition'; 3 | import React from 'react'; 4 | 5 | export default function ExperimentalWarning() { 6 | return ( 7 | <Admonition type="experimental"> 8 | This is an <Link to="/docs/experimental">experimental feature</Link>. The 9 | behavior and API may change drastically between minor releases. 10 | </Admonition> 11 | ); 12 | } 13 | -------------------------------------------------------------------------------- /packages/docs/src/components/Release/Contributor.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import styles from './styles.module.css'; 3 | 4 | export interface ContributorProps { 5 | name: string; 6 | } 7 | 8 | export default function Contributor({name}: ContributorProps) { 9 | return ( 10 | <a 11 | className={styles.contributor} 12 | title={name} 13 | href={`https://github.com/${name}`} 14 | target="_blank" 15 | > 16 | <img 17 | loading="lazy" 18 | src={`https://github.com/${name}.png`} 19 | width={20} 20 | height={20} 21 | alt={`${name}'s avatar`} 22 | /> 23 | </a> 24 | ); 25 | } 26 | -------------------------------------------------------------------------------- /packages/docs/src/components/Release/PullRequest.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import styles from './styles.module.css'; 3 | 4 | export interface PullRequestProps { 5 | id: number; 6 | } 7 | 8 | export default function PullRequest({id}: PullRequestProps) { 9 | return ( 10 | <a 11 | className={styles.pr} 12 | title={`Pull request #${id}`} 13 | href={`https://github.com/motion-canvas/motion-canvas/pull/${id}`} 14 | target="_blank" 15 | > 16 | <small>#{id}</small> 17 | </a> 18 | ); 19 | } 20 | -------------------------------------------------------------------------------- /packages/docs/src/components/UI/index.tsx: -------------------------------------------------------------------------------- 1 | import Svg from '@site/static/img/ui.svg'; 2 | import React from 'react'; 3 | import styles from './styles.module.css'; 4 | 5 | export default function UI() { 6 | return <Svg className={styles.root} />; 7 | } 8 | -------------------------------------------------------------------------------- /packages/docs/src/components/UI/styles.module.css: -------------------------------------------------------------------------------- 1 | .root { 2 | display: block; 3 | max-width: 640px; 4 | margin: 0 auto var(--ifm-leading); 5 | background-color: var(--ifm-background-surface-color); 6 | border-radius: var(--ifm-global-radius); 7 | border: 1px solid var(--ifm-color-emphasis-100); 8 | } 9 | 10 | .root g { 11 | animation: slide 4s infinite; 12 | } 13 | 14 | @keyframes slide { 15 | 0% { 16 | transform: translateX(0px); 17 | } 18 | 50% { 19 | transform: translateX(-100px); 20 | } 21 | 100% { 22 | transform: translateX(0px); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /packages/docs/src/components/YouTubeVideo/styles.module.css: -------------------------------------------------------------------------------- 1 | .root { 2 | margin-bottom: var(--ifm-leading); 3 | } 4 | -------------------------------------------------------------------------------- /packages/docs/src/theme/Admonition/styles.module.css: -------------------------------------------------------------------------------- 1 | span .icon { 2 | width: 24px; 3 | height: 24px; 4 | 5 | fill: var(--ifm-alert-border-color); 6 | } 7 | -------------------------------------------------------------------------------- /packages/docs/src/theme/Icon/Danger/index.tsx: -------------------------------------------------------------------------------- 1 | import React, {ComponentProps} from 'react'; 2 | 3 | export default function IconDanger(props: ComponentProps<'svg'>): JSX.Element { 4 | return ( 5 | <svg viewBox="0 0 24 24" fill="currentColor" {...props}> 6 | <path d="M15.73 3H8.27L3 8.27v7.46L8.27 21h7.46L21 15.73V8.27L15.73 3zM12 17.3c-.72 0-1.3-.58-1.3-1.3 0-.72.58-1.3 1.3-1.3.72 0 1.3.58 1.3 1.3 0 .72-.58 1.3-1.3 1.3zm1-4.3h-2V7h2v6z" /> 7 | </svg> 8 | ); 9 | } 10 | -------------------------------------------------------------------------------- /packages/docs/src/theme/Icon/DarkMode/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | export default function IconDarkMode(props) { 3 | return ( 4 | <svg 5 | height="24px" 6 | width="24px" 7 | viewBox="0 0 24 24" 8 | fill="currentColor" 9 | {...props} 10 | > 11 | <path d="M12,3c-4.97,0-9,4.03-9,9s4.03,9,9,9s9-4.03,9-9c0-0.46-0.04-0.92-0.1-1.36c-0.98,1.37-2.58,2.26-4.4,2.26 c-2.98,0-5.4-2.42-5.4-5.4c0-1.81,0.89-3.42,2.26-4.4C12.92,3.04,12.46,3,12,3L12,3z" /> 12 | </svg> 13 | ); 14 | } 15 | -------------------------------------------------------------------------------- /packages/docs/src/theme/Icon/Edit/styles.module.css: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) Facebook, Inc. and its affiliates. 3 | * 4 | * This source code is licensed under the MIT license found in the 5 | * LICENSE file in the root directory of this source tree. 6 | */ 7 | 8 | .iconEdit { 9 | margin-right: 0.3em; 10 | margin-bottom: -0.3em; 11 | } 12 | -------------------------------------------------------------------------------- /packages/docs/src/theme/Icon/ExternalLink/styles.module.css: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) Facebook, Inc. and its affiliates. 3 | * 4 | * This source code is licensed under the MIT license found in the 5 | * LICENSE file in the root directory of this source tree. 6 | */ 7 | 8 | .iconExternalLink { 9 | margin-left: 0.3rem; 10 | margin-bottom: -0.2rem; 11 | } 12 | -------------------------------------------------------------------------------- /packages/docs/src/theme/Icon/Home/index.tsx: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) Facebook, Inc. and its affiliates. 3 | * 4 | * This source code is licensed under the MIT license found in the 5 | * LICENSE file in the root directory of this source tree. 6 | */ 7 | 8 | import type {Props} from '@theme/Icon/Home'; 9 | import React from 'react'; 10 | 11 | export default function IconHome(props: Props): JSX.Element { 12 | return ( 13 | <svg 14 | viewBox="0 0 24 24" 15 | style={{width: 16, height: 16, marginTop: '0.1rem'}} 16 | {...props} 17 | > 18 | <path d="M10 20v-6h4v6h5v-8h3L12 3 2 12h3v8z" fill="currentColor" /> 19 | </svg> 20 | ); 21 | } 22 | -------------------------------------------------------------------------------- /packages/docs/src/theme/Icon/Info/index.tsx: -------------------------------------------------------------------------------- 1 | import React, {ComponentProps} from 'react'; 2 | 3 | export default function IconInfo(props: ComponentProps<'svg'>): JSX.Element { 4 | return ( 5 | <svg viewBox="0 0 24 24" fill="currentColor" {...props}> 6 | <path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 15h-2v-6h2v6zm0-8h-2V7h2v2z" /> 7 | </svg> 8 | ); 9 | } 10 | -------------------------------------------------------------------------------- /packages/docs/src/theme/Icon/LightBulb/index.tsx: -------------------------------------------------------------------------------- 1 | import React, {ComponentProps} from 'react'; 2 | 3 | export default function IconLightBulb( 4 | props: ComponentProps<'svg'>, 5 | ): JSX.Element { 6 | return ( 7 | <svg viewBox="0 0 24 24" fill="currentColor" {...props}> 8 | <path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 17h-2v-2h2v2zm2.07-7.75l-.9.92C13.45 12.9 13 13.5 13 15h-2v-.5c0-1.1.45-2.1 1.17-2.83l1.24-1.26c.37-.36.59-.86.59-1.41 0-1.1-.9-2-2-2s-2 .9-2 2H8c0-2.21 1.79-4 4-4s4 1.79 4 4c0 .88-.36 1.68-.93 2.25z" /> 9 | </svg> 10 | ); 11 | } 12 | -------------------------------------------------------------------------------- /packages/docs/src/theme/Icon/Menu/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | export default function IconMenu(props) { 3 | return ( 4 | <svg viewBox="0 0 24 24" {...props} width={24} height={24}> 5 | <path 6 | d="M3 18h18v-2H3v2zm0-5h18v-2H3v2zm0-7v2h18V6H3z" 7 | fill="currentColor" 8 | /> 9 | </svg> 10 | ); 11 | } 12 | -------------------------------------------------------------------------------- /packages/docs/src/theme/Icon/Warning/index.tsx: -------------------------------------------------------------------------------- 1 | import React, {ComponentProps} from 'react'; 2 | 3 | export default function IconWarning(props: ComponentProps<'svg'>): JSX.Element { 4 | return ( 5 | <svg viewBox="0 0 24 24" fill="currentColor" {...props}> 6 | <path d="M1 21h22L12 2 1 21zm12-3h-2v-2h2v2zm0-4h-2v-4h2v4z" /> 7 | </svg> 8 | ); 9 | } 10 | -------------------------------------------------------------------------------- /packages/docs/src/theme/MDXComponents.js: -------------------------------------------------------------------------------- 1 | import FiddleCodeBlock from '@site/src/components/Fiddle/FiddleCodeBlock'; 2 | import MDXComponents from '@theme-original/MDXComponents'; 3 | 4 | export default { 5 | ...MDXComponents, 6 | pre: FiddleCodeBlock, 7 | }; 8 | -------------------------------------------------------------------------------- /packages/docs/src/theme/MDXComponents/Details.module.css: -------------------------------------------------------------------------------- 1 | .root { 2 | padding: var(--ifm-global-spacing); 3 | background-color: var(--ifm-background-surface-color); 4 | border-radius: var(--ifm-global-radius); 5 | box-shadow: var(--ifm-global-shadow-lw); 6 | margin-bottom: 0.5rem; 7 | --hl-background: var(--ifm-background-color); 8 | --ifm-code-background: var(--ifm-background-color); 9 | --docusaurus-details-decoration-color: var(--ifm-color-emphasis-300); 10 | } 11 | -------------------------------------------------------------------------------- /packages/docs/src/theme/MDXComponents/Details.tsx: -------------------------------------------------------------------------------- 1 | import {Details} from '@docusaurus/theme-common/Details'; 2 | import React from 'react'; 3 | import styles from './Details.module.css'; 4 | 5 | export default function MDXDetails(props) { 6 | const items = React.Children.toArray(props.children); 7 | const summary = items.find( 8 | item => React.isValidElement(item) && item.props?.mdxType === 'summary', 9 | ); 10 | const children = <>{items.filter(item => item !== summary)}</>; 11 | return ( 12 | <> 13 | <Details {...props} summary={summary} className={styles.root}> 14 | {children} 15 | </Details> 16 | </> 17 | ); 18 | } 19 | -------------------------------------------------------------------------------- /packages/docs/src/theme/Navbar/Content/styles.module.css: -------------------------------------------------------------------------------- 1 | @media (max-width: 996px) { 2 | .colorModeToggle { 3 | display: none; 4 | } 5 | } 6 | 7 | .colorModeToggle { 8 | margin-right: var(--ifm-button-padding-vertical); 9 | } 10 | -------------------------------------------------------------------------------- /packages/docs/static/.nojekyll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/motion-canvas/motion-canvas/00639cd4cca76d60275d48fa15211d1e17e78228/packages/docs/static/.nojekyll -------------------------------------------------------------------------------- /packages/docs/static/CNAME: -------------------------------------------------------------------------------- 1 | motioncanvas.io 2 | -------------------------------------------------------------------------------- /packages/docs/static/google2548701e78413b2d.html: -------------------------------------------------------------------------------- 1 | google-site-verification: google2548701e78413b2d.html 2 | -------------------------------------------------------------------------------- /packages/docs/static/img/banner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/motion-canvas/motion-canvas/00639cd4cca76d60275d48fa15211d1e17e78228/packages/docs/static/img/banner.png -------------------------------------------------------------------------------- /packages/docs/static/img/logpayload-message.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/motion-canvas/motion-canvas/00639cd4cca76d60275d48fa15211d1e17e78228/packages/docs/static/img/logpayload-message.png -------------------------------------------------------------------------------- /packages/docs/static/img/media/editor-audio-track-delayed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/motion-canvas/motion-canvas/00639cd4cca76d60275d48fa15211d1e17e78228/packages/docs/static/img/media/editor-audio-track-delayed.png -------------------------------------------------------------------------------- /packages/docs/static/img/media/editor-audio-track.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/motion-canvas/motion-canvas/00639cd4cca76d60275d48fa15211d1e17e78228/packages/docs/static/img/media/editor-audio-track.png -------------------------------------------------------------------------------- /packages/docs/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@tsconfig/docusaurus/tsconfig.json", 3 | "compilerOptions": { 4 | "baseUrl": "." 5 | }, 6 | "include": ["."] 7 | } 8 | -------------------------------------------------------------------------------- /packages/docs/tsdoc.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://developer.microsoft.com/json-schemas/tsdoc/v0/tsdoc.schema.json", 3 | "extends": ["../../tsdoc.json"] 4 | } 5 | -------------------------------------------------------------------------------- /packages/docs/typedoc-standalone.js: -------------------------------------------------------------------------------- 1 | const factory = require('./typedoc.js'); 2 | 3 | (async () => { 4 | process.env.NODE_ENV = 'production'; 5 | const plugin = factory(); 6 | await plugin.loadContent(); 7 | })(); 8 | -------------------------------------------------------------------------------- /packages/e2e/.gitignore: -------------------------------------------------------------------------------- 1 | src/__image_snapshots__/__diff_output__ -------------------------------------------------------------------------------- /packages/e2e/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@motion-canvas/e2e", 3 | "private": true, 4 | "version": "0.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite", 8 | "test": "vitest" 9 | }, 10 | "dependencies": { 11 | "@motion-canvas/2d": "*", 12 | "@motion-canvas/core": "*", 13 | "jest-image-snapshot": "^6.2.0", 14 | "playwright": "^1.46.1", 15 | "vitest": "^0.34.6" 16 | }, 17 | "devDependencies": { 18 | "@motion-canvas/ui": "*", 19 | "@motion-canvas/vite-plugin": "*" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /packages/e2e/src/__image_snapshots__/circle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/motion-canvas/motion-canvas/00639cd4cca76d60275d48fa15211d1e17e78228/packages/e2e/src/__image_snapshots__/circle.png -------------------------------------------------------------------------------- /packages/e2e/src/__image_snapshots__/rect.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/motion-canvas/motion-canvas/00639cd4cca76d60275d48fa15211d1e17e78228/packages/e2e/src/__image_snapshots__/rect.png -------------------------------------------------------------------------------- /packages/e2e/src/global.d.ts: -------------------------------------------------------------------------------- 1 | declare namespace jest { 2 | interface Matchers<R> { 3 | toMatchImageSnapshot(...args: unknown[]): R; 4 | } 5 | } 6 | 7 | declare module 'jest-image-snapshot' { 8 | const toMatchImageSnapshot: any; 9 | export {toMatchImageSnapshot}; 10 | } 11 | -------------------------------------------------------------------------------- /packages/e2e/tests/motion-canvas.d.ts: -------------------------------------------------------------------------------- 1 | /// <reference types="@motion-canvas/core/project" /> 2 | -------------------------------------------------------------------------------- /packages/e2e/tests/project.meta: -------------------------------------------------------------------------------- 1 | { 2 | "version": 0, 3 | "shared": { 4 | "background": "rgb(255,255,255)", 5 | "range": [ 6 | 0, 7 | null 8 | ], 9 | "size": { 10 | "x": 320, 11 | "y": 320 12 | }, 13 | "audioOffset": 0 14 | }, 15 | "preview": { 16 | "fps": 30, 17 | "resolutionScale": 1 18 | }, 19 | "rendering": { 20 | "fps": 30, 21 | "resolutionScale": 1, 22 | "colorSpace": "srgb", 23 | "exporter": { 24 | "name": "@motion-canvas/core/image-sequence", 25 | "options": { 26 | "fileType": "image/png", 27 | "quality": 100, 28 | "groupByScene": true 29 | } 30 | } 31 | } 32 | } -------------------------------------------------------------------------------- /packages/e2e/tests/project.ts: -------------------------------------------------------------------------------- 1 | import {makeProject} from '@motion-canvas/core'; 2 | 3 | import circle from './scenes/circle?scene'; 4 | import rect from './scenes/rect?scene'; 5 | 6 | export default makeProject({ 7 | scenes: [circle, rect], 8 | }); 9 | -------------------------------------------------------------------------------- /packages/e2e/tests/scenes/circle.meta: -------------------------------------------------------------------------------- 1 | { 2 | "version": 0, 3 | "timeEvents": [], 4 | "seed": 4241314046 5 | } -------------------------------------------------------------------------------- /packages/e2e/tests/scenes/circle.tsx: -------------------------------------------------------------------------------- 1 | import {Circle, makeScene2D} from '@motion-canvas/2d'; 2 | 3 | export default makeScene2D(function* (view) { 4 | view.add( 5 | <Circle 6 | size={200} 7 | stroke={'blue'} 8 | lineWidth={40} 9 | arrowSize={40} 10 | fill={'lightseagreen'} 11 | strokeFirst={true} 12 | endArrow 13 | endAngle={-90} 14 | end={0.5} 15 | lineCap={'round'} 16 | cache 17 | />, 18 | ); 19 | yield; 20 | }); 21 | -------------------------------------------------------------------------------- /packages/e2e/tests/scenes/rect.meta: -------------------------------------------------------------------------------- 1 | { 2 | "version": 0, 3 | "timeEvents": [], 4 | "seed": 2621987976 5 | } -------------------------------------------------------------------------------- /packages/e2e/tests/scenes/rect.tsx: -------------------------------------------------------------------------------- 1 | import {makeScene2D, Rect} from '@motion-canvas/2d'; 2 | 3 | export default makeScene2D(function* (view) { 4 | view.add( 5 | <Rect 6 | width={300} 7 | height={200} 8 | fill={'#0008'} 9 | radius={[0, 100, 30, 200]} 10 | start={0.35} 11 | />, 12 | ); 13 | yield; 14 | }); 15 | -------------------------------------------------------------------------------- /packages/e2e/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@motion-canvas/2d/tsconfig.project.json", 3 | "include": ["src", "tests"] 4 | } 5 | -------------------------------------------------------------------------------- /packages/e2e/vite.config.ts: -------------------------------------------------------------------------------- 1 | /// <reference types="vitest" /> 2 | 3 | import motionCanvas from '@motion-canvas/vite-plugin'; 4 | import {defineConfig} from 'vite'; 5 | 6 | export default defineConfig({ 7 | plugins: [ 8 | motionCanvas.default({ 9 | project: ['./tests/project.ts'], 10 | }), 11 | ], 12 | test: { 13 | testTimeout: 60000, 14 | }, 15 | }); 16 | -------------------------------------------------------------------------------- /packages/examples/assets/example.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/motion-canvas/motion-canvas/00639cd4cca76d60275d48fa15211d1e17e78228/packages/examples/assets/example.mp4 -------------------------------------------------------------------------------- /packages/examples/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@motion-canvas/examples", 3 | "private": true, 4 | "version": "0.0.0", 5 | "scripts": { 6 | "dev": "vite", 7 | "build": "tsc && vite build --base /examples/" 8 | }, 9 | "dependencies": { 10 | "@motion-canvas/2d": "*", 11 | "@motion-canvas/core": "*" 12 | }, 13 | "devDependencies": { 14 | "@motion-canvas/ui": "*", 15 | "@motion-canvas/vite-plugin": "*" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /packages/examples/src/code-block.ts: -------------------------------------------------------------------------------- 1 | import {makeProject} from '@motion-canvas/core'; 2 | 3 | import scene from './scenes/code-block?scene'; 4 | 5 | export default makeProject({ 6 | scenes: [scene], 7 | }); 8 | -------------------------------------------------------------------------------- /packages/examples/src/code.ts: -------------------------------------------------------------------------------- 1 | import {makeProject} from '@motion-canvas/core'; 2 | import scene from './scenes/code?scene'; 3 | 4 | import {parser} from '@lezer/javascript'; 5 | import {Code, LezerHighlighter} from '@motion-canvas/2d'; 6 | 7 | Code.defaultHighlighter = new LezerHighlighter(parser); 8 | 9 | export default makeProject({ 10 | scenes: [scene], 11 | }); 12 | -------------------------------------------------------------------------------- /packages/examples/src/components.meta: -------------------------------------------------------------------------------- 1 | { 2 | "version": 0, 3 | "shared": { 4 | "background": null, 5 | "range": [ 6 | 0, 7 | null 8 | ], 9 | "size": { 10 | "x": 960, 11 | "y": 540 12 | }, 13 | "audioOffset": 0 14 | }, 15 | "preview": { 16 | "fps": 30, 17 | "resolutionScale": 1 18 | }, 19 | "rendering": { 20 | "fps": 60, 21 | "resolutionScale": 2, 22 | "colorSpace": "srgb", 23 | "fileType": "image/png", 24 | "quality": 1 25 | } 26 | } -------------------------------------------------------------------------------- /packages/examples/src/components.ts: -------------------------------------------------------------------------------- 1 | import {makeProject} from '@motion-canvas/core'; 2 | 3 | import scene from './scenes/components?scene'; 4 | 5 | export default makeProject({ 6 | scenes: [scene], 7 | }); 8 | -------------------------------------------------------------------------------- /packages/examples/src/layout-group.meta: -------------------------------------------------------------------------------- 1 | { 2 | "version": 0, 3 | "shared": { 4 | "background": null, 5 | "range": [ 6 | 0, 7 | null 8 | ], 9 | "size": { 10 | "x": 1920, 11 | "y": 1080 12 | }, 13 | "audioOffset": 0 14 | }, 15 | "preview": { 16 | "fps": 30, 17 | "resolutionScale": 1 18 | }, 19 | "rendering": { 20 | "fps": 60, 21 | "resolutionScale": 2, 22 | "colorSpace": "srgb", 23 | "fileType": "image/png", 24 | "quality": 1 25 | } 26 | } -------------------------------------------------------------------------------- /packages/examples/src/layout-group.ts: -------------------------------------------------------------------------------- 1 | import {makeProject} from '@motion-canvas/core'; 2 | 3 | import scene from './scenes/layout-group?scene'; 4 | 5 | export default makeProject({ 6 | scenes: [scene], 7 | }); 8 | -------------------------------------------------------------------------------- /packages/examples/src/layout.meta: -------------------------------------------------------------------------------- 1 | { 2 | "version": 0, 3 | "shared": { 4 | "background": "rgb(20,20,20)", 5 | "range": [ 6 | 0, 7 | null 8 | ], 9 | "size": { 10 | "x": 960, 11 | "y": 540 12 | }, 13 | "audioOffset": 0 14 | }, 15 | "preview": { 16 | "fps": 30, 17 | "resolutionScale": 1 18 | }, 19 | "rendering": { 20 | "fps": 60, 21 | "resolutionScale": 2, 22 | "colorSpace": "srgb", 23 | "fileType": "image/png", 24 | "quality": 1 25 | } 26 | } -------------------------------------------------------------------------------- /packages/examples/src/layout.ts: -------------------------------------------------------------------------------- 1 | import {makeProject} from '@motion-canvas/core'; 2 | 3 | import layout from './scenes/layout?scene'; 4 | 5 | export default makeProject({ 6 | scenes: [layout], 7 | }); 8 | -------------------------------------------------------------------------------- /packages/examples/src/logging.meta: -------------------------------------------------------------------------------- 1 | { 2 | "version": 0, 3 | "shared": { 4 | "background": null, 5 | "range": [ 6 | 0, 7 | null 8 | ], 9 | "size": { 10 | "x": 1920, 11 | "y": 1080 12 | }, 13 | "audioOffset": 0 14 | }, 15 | "preview": { 16 | "fps": 30, 17 | "resolutionScale": 1 18 | }, 19 | "rendering": { 20 | "fps": 60, 21 | "resolutionScale": 2, 22 | "colorSpace": "srgb", 23 | "fileType": "image/png", 24 | "quality": 1 25 | } 26 | } -------------------------------------------------------------------------------- /packages/examples/src/logging.ts: -------------------------------------------------------------------------------- 1 | import {makeProject} from '@motion-canvas/core'; 2 | 3 | import scene from './scenes/logging?scene'; 4 | 5 | export default makeProject({ 6 | scenes: [scene], 7 | }); 8 | -------------------------------------------------------------------------------- /packages/examples/src/media-image.meta: -------------------------------------------------------------------------------- 1 | { 2 | "version": 0, 3 | "shared": { 4 | "background": null, 5 | "range": [ 6 | 0, 7 | null 8 | ], 9 | "size": { 10 | "x": 1920, 11 | "y": 1080 12 | }, 13 | "audioOffset": 0 14 | }, 15 | "preview": { 16 | "fps": 30, 17 | "resolutionScale": 1 18 | }, 19 | "rendering": { 20 | "fps": 60, 21 | "resolutionScale": 2, 22 | "colorSpace": "srgb", 23 | "fileType": "image/png", 24 | "quality": 1 25 | } 26 | } -------------------------------------------------------------------------------- /packages/examples/src/media-image.ts: -------------------------------------------------------------------------------- 1 | import {makeProject} from '@motion-canvas/core'; 2 | 3 | import scene from './scenes/media-image?scene'; 4 | 5 | export default makeProject({ 6 | scenes: [scene], 7 | }); 8 | -------------------------------------------------------------------------------- /packages/examples/src/media-video.meta: -------------------------------------------------------------------------------- 1 | { 2 | "version": 0, 3 | "shared": { 4 | "background": "rgb(36,36,36)", 5 | "range": [ 6 | 0, 7 | null 8 | ], 9 | "size": { 10 | "x": 1920, 11 | "y": 1080 12 | }, 13 | "audioOffset": 0 14 | }, 15 | "preview": { 16 | "fps": 30, 17 | "resolutionScale": 1 18 | }, 19 | "rendering": { 20 | "fps": 60, 21 | "resolutionScale": 2, 22 | "colorSpace": "srgb", 23 | "fileType": "image/png", 24 | "quality": 1 25 | } 26 | } -------------------------------------------------------------------------------- /packages/examples/src/media-video.ts: -------------------------------------------------------------------------------- 1 | import {makeProject} from '@motion-canvas/core'; 2 | 3 | import scene from './scenes/media-video?scene'; 4 | 5 | export default makeProject({ 6 | scenes: [scene], 7 | }); 8 | -------------------------------------------------------------------------------- /packages/examples/src/motion-canvas.d.ts: -------------------------------------------------------------------------------- 1 | /// <reference types="@motion-canvas/core/project" /> 2 | -------------------------------------------------------------------------------- /packages/examples/src/node-signal.meta: -------------------------------------------------------------------------------- 1 | { 2 | "version": 0, 3 | "shared": { 4 | "background": null, 5 | "range": [ 6 | 0, 7 | null 8 | ], 9 | "size": { 10 | "x": 1920, 11 | "y": 1080 12 | }, 13 | "audioOffset": 0 14 | }, 15 | "preview": { 16 | "fps": 30, 17 | "resolutionScale": 1 18 | }, 19 | "rendering": { 20 | "fps": 60, 21 | "resolutionScale": 2, 22 | "colorSpace": "srgb", 23 | "fileType": "image/png", 24 | "quality": 1 25 | } 26 | } -------------------------------------------------------------------------------- /packages/examples/src/node-signal.ts: -------------------------------------------------------------------------------- 1 | import {makeProject} from '@motion-canvas/core'; 2 | 3 | import scene from './scenes/node-signal?scene'; 4 | 5 | export default makeProject({ 6 | scenes: [scene], 7 | }); 8 | -------------------------------------------------------------------------------- /packages/examples/src/positioning.meta: -------------------------------------------------------------------------------- 1 | { 2 | "version": 0, 3 | "shared": { 4 | "background": "rgb(20,20,20)", 5 | "range": [ 6 | 0, 7 | null 8 | ], 9 | "size": { 10 | "x": 960, 11 | "y": 540 12 | }, 13 | "audioOffset": 0 14 | }, 15 | "preview": { 16 | "fps": 30, 17 | "resolutionScale": 1 18 | }, 19 | "rendering": { 20 | "fps": 60, 21 | "resolutionScale": 2, 22 | "colorSpace": "srgb", 23 | "fileType": "image/png", 24 | "quality": 1 25 | } 26 | } -------------------------------------------------------------------------------- /packages/examples/src/positioning.ts: -------------------------------------------------------------------------------- 1 | import {makeProject} from '@motion-canvas/core'; 2 | 3 | import node from './scenes/positioning?scene'; 4 | 5 | export default makeProject({ 6 | scenes: [node], 7 | }); 8 | -------------------------------------------------------------------------------- /packages/examples/src/presentation.meta: -------------------------------------------------------------------------------- 1 | { 2 | "version": 0, 3 | "shared": { 4 | "background": "rgb(20,20,20)", 5 | "range": [ 6 | 0, 7 | null 8 | ], 9 | "size": { 10 | "x": 1920, 11 | "y": 1080 12 | }, 13 | "audioOffset": 0 14 | }, 15 | "preview": { 16 | "fps": 30, 17 | "resolutionScale": 1 18 | }, 19 | "rendering": { 20 | "fps": 60, 21 | "resolutionScale": 2, 22 | "colorSpace": "srgb", 23 | "fileType": "image/png", 24 | "quality": 1 25 | } 26 | } -------------------------------------------------------------------------------- /packages/examples/src/presentation.ts: -------------------------------------------------------------------------------- 1 | import {makeProject} from '@motion-canvas/core'; 2 | 3 | import scene from './scenes/presentation?scene'; 4 | 5 | export default makeProject({ 6 | scenes: [scene], 7 | }); 8 | -------------------------------------------------------------------------------- /packages/examples/src/quickstart.meta: -------------------------------------------------------------------------------- 1 | { 2 | "version": 0, 3 | "shared": { 4 | "background": null, 5 | "range": [ 6 | 0, 7 | null 8 | ], 9 | "size": { 10 | "x": 1920, 11 | "y": 1080 12 | }, 13 | "audioOffset": 0 14 | }, 15 | "preview": { 16 | "fps": 30, 17 | "resolutionScale": 1 18 | }, 19 | "rendering": { 20 | "fps": 60, 21 | "resolutionScale": 2, 22 | "colorSpace": "srgb", 23 | "fileType": "image/png", 24 | "quality": 1 25 | } 26 | } -------------------------------------------------------------------------------- /packages/examples/src/quickstart.ts: -------------------------------------------------------------------------------- 1 | import {makeProject} from '@motion-canvas/core'; 2 | 3 | import scene from './scenes/quickstart?scene'; 4 | 5 | export default makeProject({ 6 | scenes: [scene], 7 | }); 8 | -------------------------------------------------------------------------------- /packages/examples/src/random.meta: -------------------------------------------------------------------------------- 1 | { 2 | "version": 0, 3 | "shared": { 4 | "background": null, 5 | "range": [ 6 | 0, 7 | null 8 | ], 9 | "size": { 10 | "x": 960, 11 | "y": 540 12 | }, 13 | "audioOffset": 0 14 | }, 15 | "preview": { 16 | "fps": 30, 17 | "resolutionScale": 1 18 | }, 19 | "rendering": { 20 | "fps": 60, 21 | "resolutionScale": 2, 22 | "colorSpace": "srgb", 23 | "fileType": "image/png", 24 | "quality": 1 25 | } 26 | } -------------------------------------------------------------------------------- /packages/examples/src/random.ts: -------------------------------------------------------------------------------- 1 | import {makeProject} from '@motion-canvas/core'; 2 | 3 | import scene from './scenes/random?scene'; 4 | 5 | export default makeProject({ 6 | scenes: [scene], 7 | }); 8 | -------------------------------------------------------------------------------- /packages/examples/src/scenes/code-block.meta: -------------------------------------------------------------------------------- 1 | { 2 | "version": 1, 3 | "timeEvents": [], 4 | "seed": 4275731031 5 | } -------------------------------------------------------------------------------- /packages/examples/src/scenes/code.meta: -------------------------------------------------------------------------------- 1 | { 2 | "version": 1, 3 | "timeEvents": [], 4 | "seed": 3853578122 5 | } -------------------------------------------------------------------------------- /packages/examples/src/scenes/components.meta: -------------------------------------------------------------------------------- 1 | { 2 | "version": 0, 3 | "timeEvents": [], 4 | "seed": 1216764715 5 | } -------------------------------------------------------------------------------- /packages/examples/src/scenes/components.tsx: -------------------------------------------------------------------------------- 1 | import {makeScene2D} from '@motion-canvas/2d'; 2 | import {createRef, waitFor} from '@motion-canvas/core'; 3 | import {Switch} from '@motion-canvas/examples/src/components/Switch'; 4 | // see this import for the component ^ 5 | 6 | // usage of the component: 7 | export default makeScene2D(function* (view) { 8 | const switchRef = createRef<Switch>(); 9 | 10 | view.add(<Switch ref={switchRef} initialState={true} />); 11 | 12 | yield* switchRef().toggle(0.6); 13 | yield* waitFor(1); 14 | yield* switchRef().toggle(0.6); 15 | yield* waitFor(1); 16 | }); 17 | -------------------------------------------------------------------------------- /packages/examples/src/scenes/layout-group.meta: -------------------------------------------------------------------------------- 1 | { 2 | "version": 1, 3 | "timeEvents": [], 4 | "seed": 2413678884 5 | } -------------------------------------------------------------------------------- /packages/examples/src/scenes/layout-group.tsx: -------------------------------------------------------------------------------- 1 | import {Layout, Node, Rect, makeScene2D} from '@motion-canvas/2d'; 2 | import {createRef} from '@motion-canvas/core'; 3 | 4 | export default makeScene2D(function* (view) { 5 | const group = createRef<Node>(); 6 | 7 | view.add( 8 | <> 9 | <Layout direction={'column'} width={960} gap={40} layout> 10 | <Node ref={group}> 11 | <Rect height={240} fill={'#ff6470'} /> 12 | <Rect height={240} fill={'#ff6470'} /> 13 | </Node> 14 | <Rect height={240} fill={'#ff6470'} /> 15 | </Layout> 16 | </>, 17 | ); 18 | 19 | yield* group().opacity(0.1, 1).to(1, 1); 20 | }); 21 | -------------------------------------------------------------------------------- /packages/examples/src/scenes/layout.meta: -------------------------------------------------------------------------------- 1 | { 2 | "version": 1, 3 | "timeEvents": [], 4 | "seed": 2835047788 5 | } -------------------------------------------------------------------------------- /packages/examples/src/scenes/logging.meta: -------------------------------------------------------------------------------- 1 | { 2 | "version": 1, 3 | "timeEvents": [ 4 | { 5 | "name": "basic logging", 6 | "targetTime": 0.9814062499999999 7 | }, 8 | { 9 | "name": "logging payloads", 10 | "targetTime": 1.9671614583333334 11 | }, 12 | { 13 | "name": "profiling", 14 | "targetTime": 2.985234375 15 | } 16 | ], 17 | "seed": 1707918003 18 | } -------------------------------------------------------------------------------- /packages/examples/src/scenes/media-image.meta: -------------------------------------------------------------------------------- 1 | { 2 | "version": 1, 3 | "timeEvents": [], 4 | "seed": 3374664546 5 | } -------------------------------------------------------------------------------- /packages/examples/src/scenes/media-image.tsx: -------------------------------------------------------------------------------- 1 | import {Img, makeScene2D} from '@motion-canvas/2d'; 2 | import {all, createRef} from '@motion-canvas/core'; 3 | 4 | import logoSvg from '@motion-canvas/examples/assets/logo.svg'; 5 | 6 | export default makeScene2D(function* (view) { 7 | const imageRef = createRef<Img>(); 8 | 9 | yield view.add(<Img ref={imageRef} src={logoSvg} scale={2} />); 10 | 11 | yield* all( 12 | imageRef().scale(2.5, 1.5).to(2, 1.5), 13 | imageRef().absoluteRotation(90, 1.5).to(0, 1.5), 14 | ); 15 | }); 16 | -------------------------------------------------------------------------------- /packages/examples/src/scenes/media-video.meta: -------------------------------------------------------------------------------- 1 | { 2 | "version": 1, 3 | "timeEvents": [], 4 | "seed": 3374664546 5 | } -------------------------------------------------------------------------------- /packages/examples/src/scenes/media-video.tsx: -------------------------------------------------------------------------------- 1 | import {Video, makeScene2D} from '@motion-canvas/2d'; 2 | import {createRef} from '@motion-canvas/core'; 3 | 4 | import exampleMp4 from '@motion-canvas/examples/assets/example.mp4'; 5 | 6 | export default makeScene2D(function* (view) { 7 | const videoRef = createRef<Video>(); 8 | 9 | view.add(<Video ref={videoRef} src={exampleMp4} />); 10 | 11 | videoRef().play(); 12 | yield* videoRef().scale(1.25, 2).to(1, 2); 13 | }); 14 | -------------------------------------------------------------------------------- /packages/examples/src/scenes/node-signal.meta: -------------------------------------------------------------------------------- 1 | { 2 | "version": 1, 3 | "timeEvents": [], 4 | "seed": 1826074633 5 | } -------------------------------------------------------------------------------- /packages/examples/src/scenes/positioning.meta: -------------------------------------------------------------------------------- 1 | { 2 | "version": 1, 3 | "timeEvents": [], 4 | "seed": 3423079105 5 | } -------------------------------------------------------------------------------- /packages/examples/src/scenes/presentation.meta: -------------------------------------------------------------------------------- 1 | { 2 | "version": 0, 3 | "timeEvents": [], 4 | "seed": 513847161 5 | } -------------------------------------------------------------------------------- /packages/examples/src/scenes/quickstart.meta: -------------------------------------------------------------------------------- 1 | { 2 | "version": 1, 3 | "timeEvents": [], 4 | "seed": 2953611296 5 | } -------------------------------------------------------------------------------- /packages/examples/src/scenes/quickstart.tsx: -------------------------------------------------------------------------------- 1 | import {Circle, makeScene2D} from '@motion-canvas/2d'; 2 | import {all, createRef} from '@motion-canvas/core'; 3 | 4 | export default makeScene2D(function* (view) { 5 | const myCircle = createRef<Circle>(); 6 | 7 | view.add( 8 | <Circle 9 | //highlight-start 10 | ref={myCircle} 11 | x={-300} 12 | width={140} 13 | height={140} 14 | fill="#e13238" 15 | />, 16 | ); 17 | 18 | yield* all( 19 | myCircle().position.x(300, 1).to(-300, 1), 20 | myCircle().fill('#e6a700', 1).to('#e13238', 1), 21 | ); 22 | }); 23 | -------------------------------------------------------------------------------- /packages/examples/src/scenes/random.meta: -------------------------------------------------------------------------------- 1 | { 2 | "version": 1, 3 | "timeEvents": [], 4 | "seed": 1456280284 5 | } -------------------------------------------------------------------------------- /packages/examples/src/scenes/tex.meta: -------------------------------------------------------------------------------- 1 | { 2 | "version": 0, 3 | "timeEvents": [], 4 | "seed": 422106947 5 | } -------------------------------------------------------------------------------- /packages/examples/src/scenes/transitions-first.meta: -------------------------------------------------------------------------------- 1 | { 2 | "version": 1, 3 | "timeEvents": [], 4 | "seed": 2868148813 5 | } -------------------------------------------------------------------------------- /packages/examples/src/scenes/transitions-second.meta: -------------------------------------------------------------------------------- 1 | { 2 | "version": 1, 3 | "timeEvents": [], 4 | "seed": 1065746214 5 | } -------------------------------------------------------------------------------- /packages/examples/src/scenes/tweening-color.meta: -------------------------------------------------------------------------------- 1 | { 2 | "version": 0, 3 | "timeEvents": [], 4 | "seed": 177802521 5 | } -------------------------------------------------------------------------------- /packages/examples/src/scenes/tweening-cubic.meta: -------------------------------------------------------------------------------- 1 | { 2 | "version": 0, 3 | "timeEvents": [], 4 | "seed": 3083903239 5 | } -------------------------------------------------------------------------------- /packages/examples/src/scenes/tweening-cubic.tsx: -------------------------------------------------------------------------------- 1 | import {Circle, makeScene2D} from '@motion-canvas/2d'; 2 | import {createRef, easeInOutCubic, map, tween} from '@motion-canvas/core'; 3 | 4 | export default makeScene2D(function* (view) { 5 | const circle = createRef<Circle>(); 6 | 7 | view.add( 8 | <Circle 9 | //highlight-start 10 | ref={circle} 11 | x={-300} 12 | width={240} 13 | height={240} 14 | fill="#e13238" 15 | />, 16 | ); 17 | //highlight-start 18 | yield* tween(2, value => { 19 | circle().position.x(map(-300, 300, easeInOutCubic(value))); 20 | }); 21 | //highlight-end 22 | }); 23 | -------------------------------------------------------------------------------- /packages/examples/src/scenes/tweening-linear.meta: -------------------------------------------------------------------------------- 1 | { 2 | "version": 1, 3 | "timeEvents": [], 4 | "seed": 1564249406 5 | } -------------------------------------------------------------------------------- /packages/examples/src/scenes/tweening-linear.tsx: -------------------------------------------------------------------------------- 1 | import {Circle, makeScene2D} from '@motion-canvas/2d'; 2 | import {createRef, map, tween} from '@motion-canvas/core'; 3 | 4 | export default makeScene2D(function* (view) { 5 | const circle = createRef<Circle>(); 6 | 7 | view.add( 8 | <Circle 9 | //highlight-start 10 | ref={circle} 11 | x={-300} 12 | width={240} 13 | height={240} 14 | fill="#e13238" 15 | />, 16 | ); 17 | //highlight-start 18 | yield* tween(2, value => { 19 | circle().position.x(map(-300, 300, value)); 20 | }); 21 | //highlight-end 22 | }); 23 | -------------------------------------------------------------------------------- /packages/examples/src/scenes/tweening-save-restore.meta: -------------------------------------------------------------------------------- 1 | { 2 | "version": 1, 3 | "seed": 1271865379 4 | } -------------------------------------------------------------------------------- /packages/examples/src/scenes/tweening-spring.meta: -------------------------------------------------------------------------------- 1 | { 2 | "version": 1, 3 | "timeEvents": [], 4 | "seed": 3566829446 5 | } -------------------------------------------------------------------------------- /packages/examples/src/scenes/tweening-spring.tsx: -------------------------------------------------------------------------------- 1 | import {Circle, makeScene2D} from '@motion-canvas/2d'; 2 | import {PlopSpring, SmoothSpring, createRef, spring} from '@motion-canvas/core'; 3 | 4 | export default makeScene2D(function* (view) { 5 | const circle = createRef<Circle>(); 6 | 7 | view.add( 8 | <Circle 9 | // highlight-start 10 | ref={circle} 11 | x={-400} 12 | size={240} 13 | fill={'#e13238'} 14 | />, 15 | ); 16 | 17 | yield* spring(PlopSpring, -400, 400, 1, value => { 18 | circle().position.x(value); 19 | }); 20 | yield* spring(SmoothSpring, 400, -400, value => { 21 | circle().position.x(value); 22 | }); 23 | }); 24 | -------------------------------------------------------------------------------- /packages/examples/src/scenes/tweening-vector.meta: -------------------------------------------------------------------------------- 1 | { 2 | "version": 0, 3 | "timeEvents": [], 4 | "seed": 3538592294 5 | } -------------------------------------------------------------------------------- /packages/examples/src/scenes/tweening-visualiser.meta: -------------------------------------------------------------------------------- 1 | { 2 | "version": 0, 3 | "timeEvents": [], 4 | "seed": 579161267 5 | } -------------------------------------------------------------------------------- /packages/examples/src/tex.meta: -------------------------------------------------------------------------------- 1 | { 2 | "version": 0, 3 | "shared": { 4 | "background": null, 5 | "range": [ 6 | 0, 7 | null 8 | ], 9 | "size": { 10 | "x": 960, 11 | "y": 540 12 | }, 13 | "audioOffset": 0 14 | }, 15 | "preview": { 16 | "fps": 30, 17 | "resolutionScale": 1 18 | }, 19 | "rendering": { 20 | "fps": 60, 21 | "resolutionScale": 2, 22 | "colorSpace": "srgb", 23 | "fileType": "image/png", 24 | "quality": 1 25 | } 26 | } -------------------------------------------------------------------------------- /packages/examples/src/tex.ts: -------------------------------------------------------------------------------- 1 | import {makeProject} from '@motion-canvas/core'; 2 | 3 | import scene from './scenes/tex?scene'; 4 | 5 | export default makeProject({ 6 | scenes: [scene], 7 | }); 8 | -------------------------------------------------------------------------------- /packages/examples/src/transitions.meta: -------------------------------------------------------------------------------- 1 | { 2 | "version": 0, 3 | "shared": { 4 | "background": null, 5 | "range": [ 6 | 0, 7 | null 8 | ], 9 | "size": { 10 | "x": 1920, 11 | "y": 1080 12 | }, 13 | "audioOffset": 0 14 | }, 15 | "preview": { 16 | "fps": 30, 17 | "resolutionScale": 1 18 | }, 19 | "rendering": { 20 | "fps": 60, 21 | "resolutionScale": 2, 22 | "colorSpace": "srgb", 23 | "fileType": "image/png", 24 | "quality": 1 25 | } 26 | } -------------------------------------------------------------------------------- /packages/examples/src/transitions.ts: -------------------------------------------------------------------------------- 1 | import {makeProject} from '@motion-canvas/core'; 2 | 3 | import first from './scenes/transitions-first?scene'; 4 | import second from './scenes/transitions-second?scene'; 5 | 6 | export default makeProject({ 7 | scenes: [first, second], 8 | }); 9 | -------------------------------------------------------------------------------- /packages/examples/src/tweening-color.meta: -------------------------------------------------------------------------------- 1 | { 2 | "version": 0, 3 | "shared": { 4 | "background": null, 5 | "range": [ 6 | 0, 7 | null 8 | ], 9 | "size": { 10 | "x": 1920, 11 | "y": 1080 12 | }, 13 | "audioOffset": 0 14 | }, 15 | "preview": { 16 | "fps": 30, 17 | "resolutionScale": 1 18 | }, 19 | "rendering": { 20 | "fps": 60, 21 | "resolutionScale": 2, 22 | "colorSpace": "srgb", 23 | "fileType": "image/png", 24 | "quality": 1 25 | } 26 | } -------------------------------------------------------------------------------- /packages/examples/src/tweening-color.ts: -------------------------------------------------------------------------------- 1 | import {makeProject} from '@motion-canvas/core'; 2 | 3 | import scene from './scenes/tweening-color?scene'; 4 | 5 | export default makeProject({ 6 | scenes: [scene], 7 | }); 8 | -------------------------------------------------------------------------------- /packages/examples/src/tweening-cubic.meta: -------------------------------------------------------------------------------- 1 | { 2 | "version": 0, 3 | "shared": { 4 | "background": null, 5 | "range": [ 6 | 0, 7 | null 8 | ], 9 | "size": { 10 | "x": 1920, 11 | "y": 1080 12 | }, 13 | "audioOffset": 0 14 | }, 15 | "preview": { 16 | "fps": 30, 17 | "resolutionScale": 1 18 | }, 19 | "rendering": { 20 | "fps": 60, 21 | "resolutionScale": 2, 22 | "colorSpace": "srgb", 23 | "fileType": "image/png", 24 | "quality": 1 25 | } 26 | } -------------------------------------------------------------------------------- /packages/examples/src/tweening-cubic.ts: -------------------------------------------------------------------------------- 1 | import {makeProject} from '@motion-canvas/core'; 2 | 3 | import scene from './scenes/tweening-cubic?scene'; 4 | 5 | export default makeProject({ 6 | scenes: [scene], 7 | }); 8 | -------------------------------------------------------------------------------- /packages/examples/src/tweening-linear.meta: -------------------------------------------------------------------------------- 1 | { 2 | "version": 0, 3 | "shared": { 4 | "background": null, 5 | "range": [ 6 | 0, 7 | null 8 | ], 9 | "size": { 10 | "x": 1920, 11 | "y": 1080 12 | }, 13 | "audioOffset": 0 14 | }, 15 | "preview": { 16 | "fps": 30, 17 | "resolutionScale": 1 18 | }, 19 | "rendering": { 20 | "fps": 60, 21 | "resolutionScale": 2, 22 | "colorSpace": "srgb", 23 | "fileType": "image/png", 24 | "quality": 1 25 | } 26 | } -------------------------------------------------------------------------------- /packages/examples/src/tweening-linear.ts: -------------------------------------------------------------------------------- 1 | import {makeProject} from '@motion-canvas/core'; 2 | 3 | import scene from './scenes/tweening-linear?scene'; 4 | 5 | export default makeProject({ 6 | scenes: [scene], 7 | }); 8 | -------------------------------------------------------------------------------- /packages/examples/src/tweening-save-restore.meta: -------------------------------------------------------------------------------- 1 | { 2 | "version": 0 3 | } -------------------------------------------------------------------------------- /packages/examples/src/tweening-save-restore.ts: -------------------------------------------------------------------------------- 1 | import {makeProject} from '@motion-canvas/core'; 2 | 3 | import scene from './scenes/tweening-save-restore?scene'; 4 | 5 | export default makeProject({ 6 | scenes: [scene], 7 | }); 8 | -------------------------------------------------------------------------------- /packages/examples/src/tweening-spring.meta: -------------------------------------------------------------------------------- 1 | { 2 | "version": 0, 3 | "shared": { 4 | "background": null, 5 | "range": [ 6 | 0, 7 | null 8 | ], 9 | "size": { 10 | "x": 1920, 11 | "y": 1080 12 | }, 13 | "audioOffset": 0 14 | }, 15 | "preview": { 16 | "fps": 30, 17 | "resolutionScale": 1 18 | }, 19 | "rendering": { 20 | "fps": 60, 21 | "resolutionScale": 2, 22 | "colorSpace": "srgb", 23 | "fileType": "image/png", 24 | "quality": 1 25 | } 26 | } -------------------------------------------------------------------------------- /packages/examples/src/tweening-spring.ts: -------------------------------------------------------------------------------- 1 | import {makeProject} from '@motion-canvas/core'; 2 | 3 | import scene from './scenes/tweening-spring?scene'; 4 | 5 | export default makeProject({ 6 | scenes: [scene], 7 | }); 8 | -------------------------------------------------------------------------------- /packages/examples/src/tweening-vector.meta: -------------------------------------------------------------------------------- 1 | { 2 | "version": 0, 3 | "shared": { 4 | "background": null, 5 | "range": [ 6 | 0, 7 | null 8 | ], 9 | "size": { 10 | "x": 1920, 11 | "y": 1080 12 | }, 13 | "audioOffset": 0 14 | }, 15 | "preview": { 16 | "fps": 30, 17 | "resolutionScale": 1 18 | }, 19 | "rendering": { 20 | "fps": 60, 21 | "resolutionScale": 2, 22 | "colorSpace": "srgb", 23 | "fileType": "image/png", 24 | "quality": 1 25 | } 26 | } -------------------------------------------------------------------------------- /packages/examples/src/tweening-vector.ts: -------------------------------------------------------------------------------- 1 | import {makeProject} from '@motion-canvas/core'; 2 | 3 | import scene from './scenes/tweening-vector?scene'; 4 | 5 | export default makeProject({ 6 | scenes: [scene], 7 | }); 8 | -------------------------------------------------------------------------------- /packages/examples/src/tweening-visualiser.meta: -------------------------------------------------------------------------------- 1 | { 2 | "version": 0, 3 | "shared": { 4 | "background": null, 5 | "range": [ 6 | 0, 7 | null 8 | ], 9 | "size": { 10 | "x": 1920, 11 | "y": 1080 12 | }, 13 | "audioOffset": 0 14 | }, 15 | "preview": { 16 | "fps": 30, 17 | "resolutionScale": 1 18 | }, 19 | "rendering": { 20 | "fps": 60, 21 | "resolutionScale": 2, 22 | "colorSpace": "srgb", 23 | "fileType": "image/png", 24 | "quality": 1 25 | } 26 | } -------------------------------------------------------------------------------- /packages/examples/src/tweening-visualiser.ts: -------------------------------------------------------------------------------- 1 | import {makeProject} from '@motion-canvas/core'; 2 | 3 | import scene from './scenes/tweening-visualiser?scene'; 4 | 5 | export default makeProject({ 6 | scenes: [scene], 7 | }); 8 | -------------------------------------------------------------------------------- /packages/examples/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@motion-canvas/2d/tsconfig.project.json", 3 | "include": ["src"] 4 | } 5 | -------------------------------------------------------------------------------- /packages/ffmpeg/client/global.d.ts: -------------------------------------------------------------------------------- 1 | /// <reference types="vite/client" /> 2 | -------------------------------------------------------------------------------- /packages/ffmpeg/client/index.ts: -------------------------------------------------------------------------------- 1 | import type {ExporterClass} from '@motion-canvas/core'; 2 | import {makePlugin} from '@motion-canvas/core'; 3 | import {FFmpegExporterClient} from './FFmpegExporterClient'; 4 | 5 | export default makePlugin({ 6 | name: 'ffmpeg-plugin', 7 | exporters(): ExporterClass[] { 8 | return [FFmpegExporterClient]; 9 | }, 10 | }); 11 | -------------------------------------------------------------------------------- /packages/ffmpeg/client/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "baseUrl": ".", 4 | "outDir": "../lib/client", 5 | "inlineSourceMap": true, 6 | "noImplicitAny": true, 7 | "moduleResolution": "node", 8 | "skipLibCheck": true, 9 | "strict": true, 10 | "module": "esnext", 11 | "target": "es2020", 12 | "declaration": true, 13 | "declarationMap": true 14 | }, 15 | "include": ["."] 16 | } 17 | -------------------------------------------------------------------------------- /packages/ffmpeg/server/global.d.ts: -------------------------------------------------------------------------------- 1 | declare module '@ffmpeg-installer/ffmpeg' { 2 | let object: {path: string}; 3 | export = object; 4 | } 5 | 6 | declare module '@ffprobe-installer/ffprobe' { 7 | let object: {path: string}; 8 | export = object; 9 | } 10 | -------------------------------------------------------------------------------- /packages/ffmpeg/server/index.ts: -------------------------------------------------------------------------------- 1 | import {Plugin, PLUGIN_OPTIONS, PluginConfig} from '@motion-canvas/vite-plugin'; 2 | import {FFmpegBridge} from './FFmpegBridge'; 3 | 4 | export default (): Plugin => { 5 | let config: PluginConfig; 6 | return { 7 | name: 'motion-canvas/ffmpeg', 8 | [PLUGIN_OPTIONS]: { 9 | entryPoint: '@motion-canvas/ffmpeg/lib/client', 10 | async config(value) { 11 | config = value; 12 | }, 13 | }, 14 | configureServer(server) { 15 | new FFmpegBridge(server, config); 16 | }, 17 | }; 18 | }; 19 | -------------------------------------------------------------------------------- /packages/ffmpeg/server/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "baseUrl": ".", 4 | "outDir": "../lib/server", 5 | "inlineSourceMap": true, 6 | "esModuleInterop": true, 7 | "moduleResolution": "node", 8 | "skipLibCheck": true, 9 | "strict": true, 10 | "module": "CommonJS", 11 | "target": "es2020", 12 | "declaration": true, 13 | "declarationMap": true 14 | }, 15 | "include": ["."] 16 | } 17 | -------------------------------------------------------------------------------- /packages/ffmpeg/tsdoc.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://developer.microsoft.com/json-schemas/tsdoc/v0/tsdoc.schema.json", 3 | "extends": ["../../tsdoc.json"] 4 | } 5 | -------------------------------------------------------------------------------- /packages/internal/index.d.ts: -------------------------------------------------------------------------------- 1 | declare module '*.md' { 2 | const value: string; 3 | export default value; 4 | } 5 | -------------------------------------------------------------------------------- /packages/internal/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@motion-canvas/internal", 3 | "private": true, 4 | "version": "0.0.0", 5 | "devDependencies": { 6 | "@rollup/plugin-commonjs": "^25.0.7", 7 | "@rollup/plugin-node-resolve": "^15.2.3", 8 | "@rollup/plugin-terser": "^0.4.4", 9 | "@rollup/plugin-typescript": "^11.1.5", 10 | "highlight.js": "^11.9.0", 11 | "marked": "^10.0.0", 12 | "rollup-plugin-postcss": "^4.0.2", 13 | "ts-patch": "^3.0.2" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /packages/internal/vite/markdown-literals.js: -------------------------------------------------------------------------------- 1 | const marked = require('../common/marked'); 2 | 3 | module.exports = () => ({ 4 | name: 'markdown-literals', 5 | async transform(code, id) { 6 | if (id.endsWith('.md')) { 7 | return `export default ${JSON.stringify(marked.parse(code))};`; 8 | } 9 | }, 10 | }); 11 | -------------------------------------------------------------------------------- /packages/player/src/global.d.ts: -------------------------------------------------------------------------------- 1 | /// <reference types="vite/client" /> 2 | 3 | declare module '*.scss?inline' { 4 | const value: string; 5 | export = value; 6 | } 7 | 8 | declare module '*.html?raw' { 9 | const value: string; 10 | export = value; 11 | } 12 | -------------------------------------------------------------------------------- /packages/player/src/template.html: -------------------------------------------------------------------------------- 1 | <div class="overlay" part="overlay"> 2 | <button 3 | part="play-button" 4 | title="Play / Pause" 5 | class="button ready" 6 | tabindex="0" 7 | ></button> 8 | <div part="message" class="message error"> 9 | An error occurred while loading the animation. 10 | </div> 11 | <svg 12 | part="loader" 13 | class="loader loading" 14 | viewBox="0 0 24 24" 15 | stroke="#ffffff" 16 | stroke-width="2" 17 | fill="transparent" 18 | > 19 | <circle cx="12" cy="12" r="9" /> 20 | </svg> 21 | </div> 22 | -------------------------------------------------------------------------------- /packages/player/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "noImplicitAny": true, 4 | "module": "esnext", 5 | "target": "esnext", 6 | "moduleResolution": "node", 7 | "resolveJsonModule": true, 8 | "isolatedModules": true, 9 | "noEmit": true, 10 | "skipLibCheck": true, 11 | "esModuleInterop": false, 12 | "useDefineForClassFields": true, 13 | "forceConsistentCasingInFileNames": true, 14 | "allowSyntheticDefaultImports": true, 15 | "types": ["node"], 16 | "lib": ["DOM", "DOM.Iterable", "ESNext"] 17 | }, 18 | "include": ["src"] 19 | } 20 | -------------------------------------------------------------------------------- /packages/player/tsdoc.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://developer.microsoft.com/json-schemas/tsdoc/v0/tsdoc.schema.json", 3 | "extends": ["../../tsdoc.json"] 4 | } 5 | -------------------------------------------------------------------------------- /packages/player/types/main.d.ts: -------------------------------------------------------------------------------- 1 | export interface MotionCanvasPlayerProps { 2 | src: string; 3 | width?: number; 4 | height?: number; 5 | auto?: boolean; 6 | quality?: number; 7 | variables?: string; 8 | } 9 | -------------------------------------------------------------------------------- /packages/player/vite.config.ts: -------------------------------------------------------------------------------- 1 | import * as fs from 'fs'; 2 | import {defineConfig} from 'vite'; 3 | 4 | export default defineConfig({ 5 | build: { 6 | lib: { 7 | entry: 'src/main.ts', 8 | formats: ['es'], 9 | fileName: 'main', 10 | }, 11 | rollupOptions: { 12 | external: ['@motion-canvas/core'], 13 | }, 14 | }, 15 | plugins: [ 16 | { 17 | name: 'template', 18 | load(id) { 19 | if (id === '\0virtual:template') { 20 | return fs.readFileSync('../template/dist/project.js').toString(); 21 | } 22 | }, 23 | }, 24 | ], 25 | }); 26 | -------------------------------------------------------------------------------- /packages/template/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@motion-canvas/template", 3 | "private": true, 4 | "version": "0.0.0", 5 | "description": "Basic project template for Motion Canvas", 6 | "author": "motion-canvas", 7 | "license": "MIT", 8 | "main": "src/project.ts", 9 | "scripts": { 10 | "dev": "vite", 11 | "build": "tsc && vite build" 12 | }, 13 | "dependencies": { 14 | "@motion-canvas/core": "*", 15 | "@motion-canvas/2d": "*" 16 | }, 17 | "devDependencies": { 18 | "@motion-canvas/ui": "*", 19 | "@motion-canvas/vite-plugin": "*" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /packages/template/src/motion-canvas.d.ts: -------------------------------------------------------------------------------- 1 | /// <reference types="@motion-canvas/core/project" /> 2 | -------------------------------------------------------------------------------- /packages/template/src/project.ts: -------------------------------------------------------------------------------- 1 | import {makeProject} from '@motion-canvas/core'; 2 | 3 | import example from './scenes/example?scene'; 4 | 5 | export default makeProject({ 6 | experimentalFeatures: true, 7 | scenes: [example], 8 | }); 9 | -------------------------------------------------------------------------------- /packages/template/src/scenes/example.meta: -------------------------------------------------------------------------------- 1 | { 2 | "version": 1, 3 | "timeEvents": [ 4 | { 5 | "name": "rect", 6 | "targetTime": 0.6792665726375177 7 | } 8 | ], 9 | "seed": 3073416149 10 | } -------------------------------------------------------------------------------- /packages/template/src/scenes/example.tsx: -------------------------------------------------------------------------------- 1 | import {Rect, makeScene2D} from '@motion-canvas/2d'; 2 | import { 3 | all, 4 | createRef, 5 | easeInExpo, 6 | easeInOutExpo, 7 | waitFor, 8 | waitUntil, 9 | } from '@motion-canvas/core'; 10 | 11 | export default makeScene2D(function* (view) { 12 | const rect = createRef<Rect>(); 13 | 14 | view.add( 15 | <Rect ref={rect} size={320} radius={80} smoothCorners fill={'#f3303f'} />, 16 | ); 17 | 18 | yield* waitUntil('rect'); 19 | yield* rect().scale(2, 1, easeInOutExpo).to(1, 0.6, easeInExpo); 20 | rect().fill('#ffa56d'); 21 | yield* all(rect().ripple(1)); 22 | yield* waitFor(0.3); 23 | }); 24 | -------------------------------------------------------------------------------- /packages/template/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@motion-canvas/2d/tsconfig.project.json", 3 | "compilerOptions": { 4 | "types": ["node"] 5 | }, 6 | "include": ["src"] 7 | } 8 | -------------------------------------------------------------------------------- /packages/template/tsdoc.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://developer.microsoft.com/json-schemas/tsdoc/v0/tsdoc.schema.json", 3 | "extends": ["../../tsdoc.json"] 4 | } 5 | -------------------------------------------------------------------------------- /packages/ui/src/Editor.module.scss: -------------------------------------------------------------------------------- 1 | .root { 2 | border: 2px solid var(--border-color); 3 | display: grid; 4 | width: 100%; 5 | height: 100%; 6 | grid-template-rows: 1fr min-content; 7 | grid-template-columns: min-content 1fr; 8 | grid-template-areas: 9 | 'navigation viewport' 10 | 'footer footer'; 11 | } 12 | -------------------------------------------------------------------------------- /packages/ui/src/components/animations/borderHighlight.ts: -------------------------------------------------------------------------------- 1 | export function borderHighlight(): Keyframe[] { 2 | return [ 3 | { 4 | borderColor: 'var(--theme)', 5 | easing: 'cubic-bezier(0.32, 0, 0.67, 0)', 6 | }, 7 | {}, 8 | ]; 9 | } 10 | -------------------------------------------------------------------------------- /packages/ui/src/components/animations/emphasize.ts: -------------------------------------------------------------------------------- 1 | export function emphasize(distancePixels = 4) { 2 | return [ 3 | { 4 | translate: '0 0', 5 | easing: 'cubic-bezier(0.33, 1, 0.68, 1)', 6 | }, 7 | { 8 | translate: `0 -${distancePixels}px`, 9 | easing: 'cubic-bezier(0.34, 3, 0.64, 1)', 10 | }, 11 | { 12 | translate: `0 0`, 13 | }, 14 | ]; 15 | } 16 | -------------------------------------------------------------------------------- /packages/ui/src/components/animations/highlight.ts: -------------------------------------------------------------------------------- 1 | export function highlight(sizePixels = 4) { 2 | return [ 3 | { 4 | boxShadow: '0 0 0px 0 #ccc inset', 5 | easing: 'cubic-bezier(0.33, 1, 0.68, 1)', 6 | }, 7 | { 8 | boxShadow: `0 0 0px ${sizePixels}px #ccc inset`, 9 | easing: 'cubic-bezier(0.32, 0, 0.67, 0)', 10 | }, 11 | {boxShadow: '0 0 0px 0 #ccc inset'}, 12 | ]; 13 | } 14 | -------------------------------------------------------------------------------- /packages/ui/src/components/animations/index.ts: -------------------------------------------------------------------------------- 1 | export * from './borderHighlight'; 2 | export * from './emphasize'; 3 | export * from './highlight'; 4 | export * from './shake'; 5 | -------------------------------------------------------------------------------- /packages/ui/src/components/animations/shake.ts: -------------------------------------------------------------------------------- 1 | export function shake(distancePixels = 2) { 2 | const easing = getComputedStyle(document.documentElement).getPropertyValue( 3 | '--timing-ease-in-out', 4 | ); 5 | return [ 6 | {translate: '0px'}, 7 | { 8 | translate: `-${distancePixels}px`, 9 | easing, 10 | }, 11 | { 12 | translate: `${distancePixels}px`, 13 | easing, 14 | }, 15 | { 16 | translate: `-${distancePixels}px`, 17 | easing, 18 | }, 19 | { 20 | translate: `${distancePixels}px`, 21 | easing, 22 | }, 23 | {translate: '0px'}, 24 | ]; 25 | } 26 | -------------------------------------------------------------------------------- /packages/ui/src/components/console/index.ts: -------------------------------------------------------------------------------- 1 | export * from './Console'; 2 | -------------------------------------------------------------------------------- /packages/ui/src/components/controls/ButtonCheckbox.tsx: -------------------------------------------------------------------------------- 1 | import clsx from 'clsx'; 2 | import {Button, ButtonProps} from './Button'; 3 | import styles from './Controls.module.scss'; 4 | 5 | export interface ButtonCheckboxProps extends ButtonProps { 6 | checked?: boolean; 7 | onChecked?: (checked: boolean) => void; 8 | } 9 | 10 | export function ButtonCheckbox({ 11 | checked, 12 | onChecked, 13 | ...props 14 | }: ButtonCheckboxProps) { 15 | return ( 16 | <Button 17 | onClick={() => { 18 | onChecked?.(!checked); 19 | }} 20 | className={clsx(checked && styles.checked)} 21 | type={'button'} 22 | {...props} 23 | /> 24 | ); 25 | } 26 | -------------------------------------------------------------------------------- /packages/ui/src/components/controls/Checkbox.tsx: -------------------------------------------------------------------------------- 1 | import type {JSX} from 'preact'; 2 | import styles from './Controls.module.scss'; 3 | 4 | type InputProps = JSX.HTMLAttributes<HTMLInputElement>; 5 | 6 | export function Checkbox(props: InputProps) { 7 | return <input type="checkbox" className={styles.checkbox} {...props} />; 8 | } 9 | -------------------------------------------------------------------------------- /packages/ui/src/components/controls/ColorPreview.tsx: -------------------------------------------------------------------------------- 1 | import {JSX} from 'preact'; 2 | 3 | import styles from './Controls.module.scss'; 4 | 5 | interface ColorPreviewProps extends JSX.HTMLAttributes<HTMLDivElement> { 6 | color: string; 7 | } 8 | 9 | export function ColorPreview({color, ...props}: ColorPreviewProps) { 10 | return ( 11 | <div className={styles.colorPreview} {...props}> 12 | <div style={{background: color}} /> 13 | </div> 14 | ); 15 | } 16 | -------------------------------------------------------------------------------- /packages/ui/src/components/controls/Group.tsx: -------------------------------------------------------------------------------- 1 | import type {JSX} from 'preact'; 2 | 3 | import styles from './Controls.module.scss'; 4 | 5 | export function Group(props: JSX.HTMLAttributes<HTMLDivElement>) { 6 | return ( 7 | <div className={styles.group} {...props}> 8 | {props.children} 9 | </div> 10 | ); 11 | } 12 | -------------------------------------------------------------------------------- /packages/ui/src/components/controls/Input.tsx: -------------------------------------------------------------------------------- 1 | import type {JSX} from 'preact'; 2 | import styles from './Controls.module.scss'; 3 | 4 | type InputProps = JSX.HTMLAttributes<HTMLInputElement>; 5 | 6 | export function Input({onChange, onChangeCapture, ...props}: InputProps) { 7 | return ( 8 | <input 9 | onChangeCapture={onChangeCapture ?? onChange} 10 | onChange={onChangeCapture ? onChange : undefined} 11 | className={styles.input} 12 | {...props} 13 | /> 14 | ); 15 | } 16 | -------------------------------------------------------------------------------- /packages/ui/src/components/controls/Label.tsx: -------------------------------------------------------------------------------- 1 | import type {JSX} from 'preact'; 2 | import styles from './Controls.module.scss'; 3 | 4 | type LabelProps = JSX.HTMLAttributes<HTMLLabelElement>; 5 | 6 | export function Label(props: LabelProps) { 7 | return ( 8 | <label 9 | title={props.children as string} 10 | className={styles.label} 11 | {...props} 12 | /> 13 | ); 14 | } 15 | -------------------------------------------------------------------------------- /packages/ui/src/components/controls/ReadOnlyInput.tsx: -------------------------------------------------------------------------------- 1 | import clsx from 'clsx'; 2 | import type {JSX} from 'preact'; 3 | import styles from './Controls.module.scss'; 4 | 5 | interface ReadOnlyInputProps extends JSX.HTMLAttributes<HTMLDivElement> {} 6 | 7 | export function ReadOnlyInput({className, ...props}: ReadOnlyInputProps) { 8 | return <div className={clsx(className, styles.input)} {...props} />; 9 | } 10 | -------------------------------------------------------------------------------- /packages/ui/src/components/controls/Separator.tsx: -------------------------------------------------------------------------------- 1 | import type {JSX} from 'preact'; 2 | 3 | export interface SeparatorProps extends JSX.HTMLAttributes<HTMLDivElement> { 4 | size?: number; 5 | } 6 | 7 | export function Separator({size = 2, ...props}: SeparatorProps) { 8 | return ( 9 | <div 10 | {...props} 11 | style={{ 12 | height: size * 8, 13 | }} 14 | /> 15 | ); 16 | } 17 | -------------------------------------------------------------------------------- /packages/ui/src/components/controls/index.ts: -------------------------------------------------------------------------------- 1 | export * from './Button'; 2 | export * from './ButtonSelect'; 3 | export * from './Checkbox'; 4 | export * from './ColorInput'; 5 | export * from './Group'; 6 | export * from './IconButton'; 7 | export * from './IconCheckbox'; 8 | export * from './Input'; 9 | export * from './InputSelect'; 10 | export * from './Label'; 11 | export * from './NumberInput'; 12 | export * from './NumberInputSelect'; 13 | export * from './Pill'; 14 | export * from './ReadOnlyInput'; 15 | export * from './Select'; 16 | export * from './Separator'; 17 | export * from './Slider'; 18 | export * from './Toggle'; 19 | -------------------------------------------------------------------------------- /packages/ui/src/components/fields/Expandable.module.scss: -------------------------------------------------------------------------------- 1 | .root { 2 | border-top: 2px solid var(--background-color); 3 | margin-top: -1px; 4 | } 5 | 6 | .title { 7 | padding: 7px 0 8px; 8 | user-select: none; 9 | cursor: pointer; 10 | 11 | display: flex; 12 | align-items: center; 13 | } 14 | 15 | .content { 16 | padding-bottom: 16px; 17 | } 18 | 19 | .root:last-child .content { 20 | padding-bottom: 0; 21 | } 22 | -------------------------------------------------------------------------------- /packages/ui/src/components/fields/NumberField.tsx: -------------------------------------------------------------------------------- 1 | import {FieldSurface, NumericField} from './Layout'; 2 | 3 | export interface NumberFieldProps { 4 | value: number; 5 | } 6 | 7 | export function NumberField({value}: NumberFieldProps) { 8 | return ( 9 | <FieldSurface> 10 | <NumericField>{value}</NumericField> 11 | </FieldSurface> 12 | ); 13 | } 14 | -------------------------------------------------------------------------------- /packages/ui/src/components/fields/UnknownField.tsx: -------------------------------------------------------------------------------- 1 | import {Field, FieldSurface, FieldValue} from './Layout'; 2 | 3 | export interface UnknownFieldProps { 4 | value: any; 5 | } 6 | 7 | export function UnknownField({value}: UnknownFieldProps) { 8 | const isUnset = value === null || value === ''; 9 | return ( 10 | <FieldSurface disabled={isUnset}> 11 | <Field copy={isUnset ? undefined : value?.toString()}> 12 | <FieldValue>{value?.toString() || 'unset'}</FieldValue> 13 | </Field> 14 | </FieldSurface> 15 | ); 16 | } 17 | -------------------------------------------------------------------------------- /packages/ui/src/components/fields/index.ts: -------------------------------------------------------------------------------- 1 | export * from './AutoField'; 2 | export * from './ColorField'; 3 | export * from './Expandable'; 4 | export * from './Layout'; 5 | export * from './NumberField'; 6 | export * from './SpacingField'; 7 | export * from './UnknownField'; 8 | export * from './Vector2Field'; 9 | -------------------------------------------------------------------------------- /packages/ui/src/components/footer/Versions.module.scss: -------------------------------------------------------------------------------- 1 | .root { 2 | padding: 0 4px; 3 | position: relative; 4 | display: flex; 5 | gap: 8px; 6 | } 7 | 8 | .link { 9 | cursor: pointer; 10 | text-decoration: none; 11 | white-space: nowrap; 12 | 13 | &:hover { 14 | text-decoration: underline; 15 | } 16 | 17 | &.main { 18 | color: var(--theme); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /packages/ui/src/components/footer/index.ts: -------------------------------------------------------------------------------- 1 | export * from './Footer'; 2 | -------------------------------------------------------------------------------- /packages/ui/src/components/icons/AccountTree.tsx: -------------------------------------------------------------------------------- 1 | export function AccountTree() { 2 | return ( 3 | <svg 4 | viewBox="0 0 24 24" 5 | fill="currentColor" 6 | style={{rotate: '90deg', scale: '1 -1'}} 7 | > 8 | <path d="M22,11V3h-7v3H9V3H2v8h7V8h2v10h4v3h7v-8h-7v3h-2V8h2v3H22z M7,9H4V5h3V9z M17,15h3v4h-3V15z M17,5h3v4h-3V5z" /> 9 | </svg> 10 | ); 11 | } 12 | -------------------------------------------------------------------------------- /packages/ui/src/components/icons/Add.tsx: -------------------------------------------------------------------------------- 1 | import {HTMLAttributes} from 'preact/compat'; 2 | 3 | export function Add(props: HTMLAttributes<SVGSVGElement>) { 4 | return ( 5 | <svg viewBox="0 0 24 24" fill="currentColor" {...props}> 6 | <path d="M19 13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z" /> 7 | </svg> 8 | ); 9 | } 10 | -------------------------------------------------------------------------------- /packages/ui/src/components/icons/ChevronRight.tsx: -------------------------------------------------------------------------------- 1 | import {HTMLAttributes} from 'preact/compat'; 2 | 3 | export function ChevronRight(props: HTMLAttributes<SVGSVGElement>) { 4 | return ( 5 | <svg viewBox="0 0 24 24" fill="currentColor" {...props}> 6 | <polygon points="10.71 16.71 9.29 15.29 12.59 12 9.29 8.71 10.71 7.29 15.41 12 10.71 16.71" /> 7 | </svg> 8 | ); 9 | } 10 | -------------------------------------------------------------------------------- /packages/ui/src/components/icons/Clear.tsx: -------------------------------------------------------------------------------- 1 | export function Clear() { 2 | return ( 3 | <svg viewBox="0 0 24 24" fill="currentColor"> 4 | <path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.42 0-8-3.58-8-8 0-1.85.63-3.55 1.69-4.9L16.9 18.31C15.55 19.37 13.85 20 12 20zm6.31-3.1L7.1 5.69C8.45 4.63 10.15 4 12 4c4.42 0 8 3.58 8 8 0 1.85-.63 3.55-1.69 4.9z" /> 5 | </svg> 6 | ); 7 | } 8 | -------------------------------------------------------------------------------- /packages/ui/src/components/icons/Close.tsx: -------------------------------------------------------------------------------- 1 | export function Close() { 2 | return ( 3 | <svg viewBox="0 0 24 24" fill="currentColor"> 4 | <path d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12 19 6.41z" /> 5 | </svg> 6 | ); 7 | } 8 | -------------------------------------------------------------------------------- /packages/ui/src/components/icons/Colorize.tsx: -------------------------------------------------------------------------------- 1 | export function Colorize() { 2 | return ( 3 | <svg viewBox="0 0 16 16" fill="currentColor"> 4 | <path d="M6.954,4.818l-1.414,-1.414l1.414,-1.414l1.414,1.414l2.121,-2.122c0.188,-0.187 0.442,-0.292 0.707,-0.292c0.266,-0 0.52,0.105 0.708,0.292l2.828,2.829c0.188,0.187 0.293,0.442 0.293,0.707c-0,0.265 -0.105,0.52 -0.293,0.707l-2.121,2.121l1.414,1.415l-1.414,1.414l-1.415,-1.414l-4.949,4.949l-4.247,-0.004l0.004,-4.238l4.95,-4.95Zm1.414,1.414l-4.243,4.243l1.415,1.414l4.242,-4.243l-1.414,-1.414Z" /> 5 | </svg> 6 | ); 7 | } 8 | -------------------------------------------------------------------------------- /packages/ui/src/components/icons/DragIndicator.tsx: -------------------------------------------------------------------------------- 1 | import {HTMLAttributes} from 'preact/compat'; 2 | 3 | export function DragIndicator(props: HTMLAttributes<SVGSVGElement>) { 4 | return ( 5 | <svg viewBox="0 0 24 24" fill="currentColor" {...props}> 6 | <path d="M11 18c0 1.1-.9 2-2 2s-2-.9-2-2 .9-2 2-2 2 .9 2 2zm-2-8c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm0-6c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm6 4c1.1 0 2-.9 2-2s-.9-2-2-2-2 .9-2 2 .9 2 2 2zm0 2c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm0 6c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2z" /> 7 | </svg> 8 | ); 9 | } 10 | -------------------------------------------------------------------------------- /packages/ui/src/components/icons/FastForward.tsx: -------------------------------------------------------------------------------- 1 | export function FastForward() { 2 | return ( 3 | <svg viewBox="0 0 24 24" fill="currentColor"> 4 | <path d="M4 18l8.5-6L4 6v12zm9-12v12l8.5-6L13 6z" /> 5 | </svg> 6 | ); 7 | } 8 | -------------------------------------------------------------------------------- /packages/ui/src/components/icons/FastRewind.tsx: -------------------------------------------------------------------------------- 1 | export function FastRewind() { 2 | return ( 3 | <svg viewBox="0 0 24 24" fill="currentColor"> 4 | <path d="M11 18V6l-8.5 6 8.5 6zm.5-6l8.5 6V6l-8.5 6z" /> 5 | </svg> 6 | ); 7 | } 8 | -------------------------------------------------------------------------------- /packages/ui/src/components/icons/Fullscreen.tsx: -------------------------------------------------------------------------------- 1 | export function Fullscreen() { 2 | return ( 3 | <svg viewBox="0 0 24 24" fill="currentColor"> 4 | <path d="M7 14H5v5h5v-2H7v-3zm-2-4h2V7h3V5H5v5zm12 7h-3v2h5v-5h-2v3zM14 5v2h3v3h2V5h-5z" /> 5 | </svg> 6 | ); 7 | } 8 | -------------------------------------------------------------------------------- /packages/ui/src/components/icons/Grid.tsx: -------------------------------------------------------------------------------- 1 | export function Grid() { 2 | return ( 3 | <svg viewBox="0 0 16 16" fill="currentColor"> 4 | <path d="M14,3l0,10c-0,0.552 -0.448,1 -1,1l-10,0c-0.552,-0 -1,-0.448 -1,-1l0,-10c-0,-0.552 0.448,-1 1,-1l10,0c0.552,-0 1,0.448 1,1Zm-10,6l-0,3l3,0l-0,-3l-3,-0Zm5,3l3,0l0,-3l-3,0l-0,3Zm3,-5l0,-3l-3,-0l-0,3l3,0Zm-5,-3l-3,-0l-0,3l3,0l-0,-3Z" /> 5 | </svg> 6 | ); 7 | } 8 | -------------------------------------------------------------------------------- /packages/ui/src/components/icons/HourglassBottom.tsx: -------------------------------------------------------------------------------- 1 | export function HourglassBottom() { 2 | return ( 3 | <svg viewBox="0 0 24 24" fill="currentColor"> 4 | <path d="M18,21l-0.01,0.01l0.01,-0l0,0.99l-12,0l0,-0.99l0.01,0l-0.01,-0.01l0,-4.99l0.01,0l-0.01,-0.01l4,-4l-4,-3.99l0.01,-0.01l-0.01,0l0,-6l12,0l0,6l-0.01,0l0.01,0.01l-4,3.99l4,4l-0.01,0.01l0.01,0l0,4.99Zm-10,-2l4,-4l4,4l0,-2.5l-4,-4l-4,4l0,2.5Zm1.5,1l5,0l-2.5,-2.5l-2.5,2.5Zm2.5,-8.5l4,-4l0,-3.5l-8,0l0,3.5l4,4Z" /> 5 | </svg> 6 | ); 7 | } 8 | -------------------------------------------------------------------------------- /packages/ui/src/components/icons/Locate.tsx: -------------------------------------------------------------------------------- 1 | export function Locate() { 2 | return ( 3 | <svg viewBox="0 0 24 24" fill="currentColor"> 4 | <path d="M12 8c-2.21 0-4 1.79-4 4s1.79 4 4 4 4-1.79 4-4-1.79-4-4-4zm8.94 3c-.46-4.17-3.77-7.48-7.94-7.94V1h-2v2.06C6.83 3.52 3.52 6.83 3.06 11H1v2h2.06c.46 4.17 3.77 7.48 7.94 7.94V23h2v-2.06c4.17-.46 7.48-3.77 7.94-7.94H23v-2h-2.06zM12 19c-3.87 0-7-3.13-7-7s3.13-7 7-7 7 3.13 7 7-3.13 7-7 7z" /> 5 | </svg> 6 | ); 7 | } 8 | -------------------------------------------------------------------------------- /packages/ui/src/components/icons/Movie.tsx: -------------------------------------------------------------------------------- 1 | export function Movie() { 2 | return ( 3 | <svg viewBox="0 0 24 24" fill="currentColor"> 4 | <path d="M4 6.47L5.76 10H20v8H4V6.47M22 4h-4l2 4h-3l-2-4h-2l2 4h-3l-2-4H8l2 4H7L5 4H4c-1.1 0-1.99.9-1.99 2L2 18c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V4z" /> 5 | </svg> 6 | ); 7 | } 8 | -------------------------------------------------------------------------------- /packages/ui/src/components/icons/OpenInNew.tsx: -------------------------------------------------------------------------------- 1 | export function OpenInNew() { 2 | return ( 3 | <svg viewBox="0 0 24 24" fill="currentColor"> 4 | <path d="M19 19H5V5h7V3H5c-1.11 0-2 .9-2 2v14c0 1.1.89 2 2 2h14c1.1 0 2-.9 2-2v-7h-2v7zM14 3v2h3.59l-9.83 9.83 1.41 1.41L19 6.41V10h2V3h-7z" /> 5 | </svg> 6 | ); 7 | } 8 | -------------------------------------------------------------------------------- /packages/ui/src/components/icons/Pause.tsx: -------------------------------------------------------------------------------- 1 | export function Pause() { 2 | return ( 3 | <svg viewBox="0 0 24 24" fill="currentColor"> 4 | <path d="M6 19h4V5H6v14zm8-14v14h4V5h-4z" /> 5 | </svg> 6 | ); 7 | } 8 | -------------------------------------------------------------------------------- /packages/ui/src/components/icons/PhotoCamera.tsx: -------------------------------------------------------------------------------- 1 | export function PhotoCamera() { 2 | return ( 3 | <svg viewBox="0 0 24 24" fill="currentColor"> 4 | <circle cx="12" cy="12" r="3.2" /> 5 | <path d="M9 2L7.17 4H4c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2h-3.17L15 2H9zm3 15c-2.76 0-5-2.24-5-5s2.24-5 5-5 5 2.24 5 5-2.24 5-5 5z" /> 6 | </svg> 7 | ); 8 | } 9 | -------------------------------------------------------------------------------- /packages/ui/src/components/icons/PlayArrow.tsx: -------------------------------------------------------------------------------- 1 | export function PlayArrow() { 2 | return ( 3 | <svg viewBox="0 0 24 24" fill="currentColor"> 4 | <path d="M8 5v14l11-7z" /> 5 | </svg> 6 | ); 7 | } 8 | -------------------------------------------------------------------------------- /packages/ui/src/components/icons/Recenter.tsx: -------------------------------------------------------------------------------- 1 | export function Recenter() { 2 | return ( 3 | <svg viewBox="0 0 16 16" fill="currentColor"> 4 | <path d="M4.126,7c0.362,-1.405 1.469,-2.512 2.874,-2.874l0,-2.126l2,0l0,2.126c1.405,0.362 2.512,1.469 2.874,2.874l2.126,0l0,2l-2.126,0c-0.362,1.405 -1.469,2.512 -2.874,2.874l0,2.126l-2,0l-0,-2.126c-1.405,-0.362 -2.512,-1.469 -2.874,-2.874l-2.126,0l0,-2l2.126,0Zm3.874,-1c-1.104,0 -2,0.896 -2,2c0,1.104 0.896,2 2,2c1.104,0 2,-0.896 2,-2c0,-1.104 -0.896,-2 -2,-2Z" /> 5 | </svg> 6 | ); 7 | } 8 | -------------------------------------------------------------------------------- /packages/ui/src/components/icons/Repeat.tsx: -------------------------------------------------------------------------------- 1 | export function Repeat() { 2 | return ( 3 | <svg viewBox="0 0 24 24" fill="currentColor"> 4 | <path d="M7 7h10v3l4-4-4-4v3H5v6h2V7zm10 10H7v-3l-4 4 4 4v-3h12v-6h-2v4z" /> 5 | </svg> 6 | ); 7 | } 8 | -------------------------------------------------------------------------------- /packages/ui/src/components/icons/Schedule.tsx: -------------------------------------------------------------------------------- 1 | export function Schedule() { 2 | return ( 3 | <svg viewBox="0 0 24 24" fill="currentColor"> 4 | <path d="M11.99 2C6.47 2 2 6.48 2 12s4.47 10 9.99 10C17.52 22 22 17.52 22 12S17.52 2 11.99 2zM12 20c-4.42 0-8-3.58-8-8s3.58-8 8-8 8 3.58 8 8-3.58 8-8 8z" /> 5 | <path d="M12.5 7H11v6l5.25 3.15.75-1.23-4.5-2.67z" /> 6 | </svg> 7 | ); 8 | } 9 | -------------------------------------------------------------------------------- /packages/ui/src/components/icons/School.tsx: -------------------------------------------------------------------------------- 1 | export function School() { 2 | return ( 3 | <svg viewBox="0 0 24 24" fill="currentColor"> 4 | <path d="M12 3 1 9l4 2.18v6L12 21l7-3.82v-6l2-1.09V17h2V9L12 3zm6.82 6L12 12.72 5.18 9 12 5.28 18.82 9zM17 15.99l-5 2.73-5-2.73v-3.72L12 15l5-2.73v3.72z" /> 5 | </svg> 6 | ); 7 | } 8 | -------------------------------------------------------------------------------- /packages/ui/src/components/icons/Science.tsx: -------------------------------------------------------------------------------- 1 | export function Science() { 2 | return ( 3 | <svg viewBox="0 0 24 24" fill="currentColor"> 4 | <path d="M13,11.33L18,18H6l5-6.67V6h2 M15.96,4H8.04C7.62,4,7.39,4.48,7.65,4.81L9,6.5v4.17L3.2,18.4C2.71,19.06,3.18,20,4,20h16 c0.82,0,1.29-0.94,0.8-1.6L15,10.67V6.5l1.35-1.69C16.61,4.48,16.38,4,15.96,4L15.96,4z" /> 5 | </svg> 6 | ); 7 | } 8 | -------------------------------------------------------------------------------- /packages/ui/src/components/icons/SkipNext.tsx: -------------------------------------------------------------------------------- 1 | export function SkipNext() { 2 | return ( 3 | <svg viewBox="0 0 24 24" fill="currentColor"> 4 | <path d="M6 18l8.5-6L6 6v12zM16 6v12h2V6h-2z" /> 5 | </svg> 6 | ); 7 | } 8 | -------------------------------------------------------------------------------- /packages/ui/src/components/icons/SkipPrevious.tsx: -------------------------------------------------------------------------------- 1 | export function SkipPrevious() { 2 | return ( 3 | <svg viewBox="0 0 24 24" fill="currentColor"> 4 | <path d="M6 6h2v12H6zm3.5 6l8.5 6V6z" /> 5 | </svg> 6 | ); 7 | } 8 | -------------------------------------------------------------------------------- /packages/ui/src/components/icons/Tune.tsx: -------------------------------------------------------------------------------- 1 | export function Tune() { 2 | return ( 3 | <svg viewBox="0 0 24 24" fill="currentColor"> 4 | <path d="M3 17v2h6v-2H3zM3 5v2h10V5H3zm10 16v-2h8v-2h-8v-2h-2v6h2zM7 9v2H3v2h4v2h2V9H7zm14 4v-2H11v2h10zm-6-4h2V7h4V5h-4V3h-2v6z" /> 5 | </svg> 6 | ); 7 | } 8 | -------------------------------------------------------------------------------- /packages/ui/src/components/icons/UnfoldMore.tsx: -------------------------------------------------------------------------------- 1 | export function UnfoldMore() { 2 | return ( 3 | <svg viewBox="0 0 24 24" fill="currentColor"> 4 | <path d="M12 5.83L15.17 9l1.41-1.41L12 3 7.41 7.59 8.83 9 12 5.83zm0 12.34L8.83 15l-1.41 1.41L12 21l4.59-4.59L15.17 15 12 18.17z" /> 5 | </svg> 6 | ); 7 | } 8 | -------------------------------------------------------------------------------- /packages/ui/src/components/icons/Videocam.tsx: -------------------------------------------------------------------------------- 1 | export function Videocam() { 2 | return ( 3 | <svg viewBox="0 0 24 24" fill="currentColor"> 4 | <path d="M15 8v8H5V8h10m1-2H4c-.55 0-1 .45-1 1v10c0 .55.45 1 1 1h12c.55 0 1-.45 1-1v-3.5l4 4v-11l-4 4V7c0-.55-.45-1-1-1z" /> 5 | </svg> 6 | ); 7 | } 8 | -------------------------------------------------------------------------------- /packages/ui/src/components/icons/VolumeOff.tsx: -------------------------------------------------------------------------------- 1 | export function VolumeOff() { 2 | return ( 3 | <svg viewBox="0 0 24 24" fill="currentColor"> 4 | <path d="M16.5 12c0-1.77-1.02-3.29-2.5-4.03v2.21l2.45 2.45c.03-.2.05-.41.05-.63zm2.5 0c0 .94-.2 1.82-.54 2.64l1.51 1.51C20.63 14.91 21 13.5 21 12c0-4.28-2.99-7.86-7-8.77v2.06c2.89.86 5 3.54 5 6.71zM4.27 3L3 4.27 7.73 9H3v6h4l5 5v-6.73l4.25 4.25c-.67.52-1.42.93-2.25 1.18v2.06c1.38-.31 2.63-.95 3.69-1.81L19.73 21 21 19.73l-9-9L4.27 3zM12 4L9.91 6.09 12 8.18V4z" /> 5 | </svg> 6 | ); 7 | } 8 | -------------------------------------------------------------------------------- /packages/ui/src/components/icons/VolumeOn.tsx: -------------------------------------------------------------------------------- 1 | export function VolumeOn() { 2 | return ( 3 | <svg viewBox="0 0 24 24" fill="currentColor"> 4 | <path d="M3 9v6h4l5 5V4L7 9H3zm13.5 3c0-1.77-1.02-3.29-2.5-4.03v8.05c1.48-.73 2.5-2.25 2.5-4.02zM14 3.23v2.06c2.89.86 5 3.54 5 6.71s-2.11 5.85-5 6.71v2.06c4.01-.91 7-4.49 7-8.77s-2.99-7.86-7-8.77z" /> 5 | </svg> 6 | ); 7 | } 8 | -------------------------------------------------------------------------------- /packages/ui/src/components/icons/Warning.tsx: -------------------------------------------------------------------------------- 1 | export function Warning() { 2 | return ( 3 | <svg viewBox="0 0 24 24" fill="currentColor"> 4 | <path d="M1 21h22L12 2 1 21zm12-3h-2v-2h2v2zm0-4h-2v-4h2v4z" /> 5 | </svg> 6 | ); 7 | } 8 | -------------------------------------------------------------------------------- /packages/ui/src/components/layout/Collapse.module.scss: -------------------------------------------------------------------------------- 1 | .root { 2 | height: 0; 3 | overflow: hidden; 4 | transition: height var(--duration-normal) var(--timing-ease-in-out); 5 | } 6 | -------------------------------------------------------------------------------- /packages/ui/src/components/layout/Header.module.scss: -------------------------------------------------------------------------------- 1 | .header { 2 | font-size: var(--font-size-big); 3 | text-transform: uppercase; 4 | font-weight: bold; 5 | margin-bottom: 16px; 6 | } 7 | -------------------------------------------------------------------------------- /packages/ui/src/components/layout/Header.tsx: -------------------------------------------------------------------------------- 1 | import clsx from 'clsx'; 2 | import type {JSX} from 'preact'; 3 | import styles from './Header.module.scss'; 4 | 5 | export function Header({ 6 | className, 7 | ...props 8 | }: JSX.HTMLAttributes<HTMLDivElement>) { 9 | return <div className={clsx(styles.header, className)} {...props} />; 10 | } 11 | -------------------------------------------------------------------------------- /packages/ui/src/components/layout/Navigation.module.scss: -------------------------------------------------------------------------------- 1 | .root { 2 | box-sizing: content-box; 3 | border-right: 2px solid var(--border-color); 4 | grid-area: navigation; 5 | } 6 | -------------------------------------------------------------------------------- /packages/ui/src/components/layout/index.ts: -------------------------------------------------------------------------------- 1 | export * from './Collapse'; 2 | export * from './ElementSwitch'; 3 | export * from './Header'; 4 | export * from './Navigation'; 5 | export * from './ResizeableLayout'; 6 | -------------------------------------------------------------------------------- /packages/ui/src/components/meta/ObjectMetaFieldView.tsx: -------------------------------------------------------------------------------- 1 | import type {MetaField, ObjectMetaField} from '@motion-canvas/core'; 2 | import {useSubscribableValue} from '../../hooks'; 3 | import {MetaFieldView} from './MetaFieldView'; 4 | 5 | export interface ObjectMetaFieldViewProps { 6 | field: ObjectMetaField<any>; 7 | } 8 | 9 | export function ObjectMetaFieldView({field}: ObjectMetaFieldViewProps) { 10 | const fields: MetaField<any>[] = useSubscribableValue(field.onFieldsChanged); 11 | 12 | return ( 13 | <> 14 | {fields.map(subfield => ( 15 | <MetaFieldView field={subfield} /> 16 | ))} 17 | </> 18 | ); 19 | } 20 | -------------------------------------------------------------------------------- /packages/ui/src/components/meta/UnknownMetaFieldView.tsx: -------------------------------------------------------------------------------- 1 | import {MetaField} from '@motion-canvas/core'; 2 | import {useSubscribableValue} from '../../hooks'; 3 | import {Group, Label} from '../controls'; 4 | import {AutoField} from '../fields'; 5 | 6 | export interface UnknownMetaFieldViewProps { 7 | field: MetaField<any>; 8 | } 9 | 10 | export function UnknownMetaFieldView({field}: UnknownMetaFieldViewProps) { 11 | const value = useSubscribableValue(field.onChanged); 12 | 13 | return ( 14 | <Group> 15 | <Label>{field.name}</Label> 16 | <AutoField value={value} /> 17 | </Group> 18 | ); 19 | } 20 | -------------------------------------------------------------------------------- /packages/ui/src/components/meta/index.ts: -------------------------------------------------------------------------------- 1 | export * from './BoolMetaFieldView'; 2 | export * from './ColorMetaFieldView'; 3 | export * from './EnumMetaFieldView'; 4 | export * from './MetaFieldView'; 5 | export * from './NumberMetaFieldView'; 6 | export * from './ObjectMetaFieldView'; 7 | export * from './RangeMetaFieldView'; 8 | export * from './StringMetaFieldView'; 9 | export * from './UnknownMetaFieldView'; 10 | export * from './Vector2MetaFieldView'; 11 | -------------------------------------------------------------------------------- /packages/ui/src/components/playback/CurrentTime.tsx: -------------------------------------------------------------------------------- 1 | import {VNode} from 'preact'; 2 | import {usePlayerTime} from '../../hooks'; 3 | 4 | interface CurrentTimeProps { 5 | render: (time: number) => VNode<unknown>; 6 | } 7 | 8 | export function CurrentTime({render}: CurrentTimeProps) { 9 | const time = usePlayerTime(); 10 | 11 | return render(time.frame); 12 | } 13 | -------------------------------------------------------------------------------- /packages/ui/src/components/playback/index.ts: -------------------------------------------------------------------------------- 1 | export * from './PlaybackControls'; 2 | export * from './PlaybackProgress'; 3 | -------------------------------------------------------------------------------- /packages/ui/src/components/presentation/PresentationControls.module.scss: -------------------------------------------------------------------------------- 1 | .controls { 2 | position: absolute; 3 | bottom: 16px; 4 | 5 | display: flex; 6 | gap: 16px; 7 | padding: 16px; 8 | background-color: var(--surface-color); 9 | border-radius: var(--radius); 10 | } 11 | 12 | .count { 13 | color: rgba(255, 255, 255, 0.32); 14 | text-align: right; 15 | user-select: none; 16 | } 17 | -------------------------------------------------------------------------------- /packages/ui/src/components/presentation/SlideGraph.tsx: -------------------------------------------------------------------------------- 1 | import {useApplication} from '../../contexts'; 2 | import {useSubscribableValue} from '../../hooks'; 3 | import {Header} from '../layout'; 4 | import {SceneGroup} from './SceneGroup'; 5 | import styles from './SlideGraph.module.scss'; 6 | 7 | export function SlideGraph() { 8 | const {presenter} = useApplication(); 9 | const scenes = useSubscribableValue(presenter.playback.onScenesRecalculated); 10 | 11 | return ( 12 | <div className={styles.root}> 13 | <Header>SLIDES</Header> 14 | {scenes.map(scene => ( 15 | <SceneGroup key={scene.name} scene={scene} /> 16 | ))} 17 | </div> 18 | ); 19 | } 20 | -------------------------------------------------------------------------------- /packages/ui/src/components/presentation/index.ts: -------------------------------------------------------------------------------- 1 | export * from './PresentationMode'; 2 | -------------------------------------------------------------------------------- /packages/ui/src/components/sidebar/Settings.tsx: -------------------------------------------------------------------------------- 1 | import {useApplication} from '../../contexts'; 2 | import {Expandable} from '../fields'; 3 | import {MetaFieldView} from '../meta'; 4 | import {Pane} from '../tabs'; 5 | 6 | export function Settings() { 7 | const {settings} = useApplication(); 8 | 9 | return ( 10 | <Pane title="Settings" id="app-settings-pane"> 11 | <Expandable title={settings.appearance.name} open> 12 | <MetaFieldView field={settings.appearance} /> 13 | </Expandable> 14 | <Expandable title={settings.defaults.name}> 15 | <MetaFieldView field={settings.defaults} /> 16 | </Expandable> 17 | </Pane> 18 | ); 19 | } 20 | -------------------------------------------------------------------------------- /packages/ui/src/components/sidebar/index.ts: -------------------------------------------------------------------------------- 1 | export * from './Settings'; 2 | export * from './Threads'; 3 | export * from './VideoSettings'; 4 | -------------------------------------------------------------------------------- /packages/ui/src/components/tabs/Badge.tsx: -------------------------------------------------------------------------------- 1 | import {LogLevel} from '@motion-canvas/core'; 2 | import clsx from 'clsx'; 3 | import {ComponentChildren, Ref} from 'preact'; 4 | import styles from './Tabs.module.scss'; 5 | 6 | export interface BadgeInterface { 7 | level?: LogLevel; 8 | children?: ComponentChildren; 9 | badgeRef?: Ref<HTMLDivElement>; 10 | } 11 | 12 | export function Badge({ 13 | children, 14 | badgeRef, 15 | level = LogLevel.Error, 16 | }: BadgeInterface) { 17 | return ( 18 | <div ref={badgeRef} className={clsx(styles.badge, styles[level])}> 19 | {children} 20 | </div> 21 | ); 22 | } 23 | -------------------------------------------------------------------------------- /packages/ui/src/components/tabs/Pane.tsx: -------------------------------------------------------------------------------- 1 | import {ComponentChildren, JSX} from 'preact'; 2 | import {Ref} from 'preact/hooks'; 3 | import {Header} from '../layout'; 4 | import styles from './Tabs.module.scss'; 5 | 6 | export interface PaneProps extends JSX.HTMLAttributes<HTMLDivElement> { 7 | forwardRef?: Ref<HTMLDivElement>; 8 | title: string; 9 | id?: string; 10 | children: ComponentChildren; 11 | } 12 | 13 | export function Pane({title, id, children, forwardRef, ...props}: PaneProps) { 14 | return ( 15 | <div ref={forwardRef} className={styles.pane} id={id} {...props}> 16 | <Header>{title}</Header> 17 | {children} 18 | </div> 19 | ); 20 | } 21 | -------------------------------------------------------------------------------- /packages/ui/src/components/tabs/index.ts: -------------------------------------------------------------------------------- 1 | export * from './Badge'; 2 | export * from './Pane'; 3 | export * from './Tabs'; 4 | -------------------------------------------------------------------------------- /packages/ui/src/components/timeline/LabelTrack.tsx: -------------------------------------------------------------------------------- 1 | import styles from './Timeline.module.scss'; 2 | 3 | import {useScenes} from '../../hooks'; 4 | import {LabelGroup} from './LabelGroup'; 5 | 6 | export function LabelTrack() { 7 | const scenes = useScenes(); 8 | 9 | return ( 10 | <div className={styles.labelTrack}> 11 | {scenes.map(scene => ( 12 | <LabelGroup key={scene.name} scene={scene} /> 13 | ))} 14 | </div> 15 | ); 16 | } 17 | -------------------------------------------------------------------------------- /packages/ui/src/components/timeline/index.ts: -------------------------------------------------------------------------------- 1 | export {Playhead} from './Playhead'; 2 | export * from './Timeline'; 3 | -------------------------------------------------------------------------------- /packages/ui/src/components/viewport/index.ts: -------------------------------------------------------------------------------- 1 | export * from './StageView'; 2 | export * from './Viewport'; 3 | -------------------------------------------------------------------------------- /packages/ui/src/contexts/index.ts: -------------------------------------------------------------------------------- 1 | export * from './application'; 2 | export * from './panels'; 3 | export * from './shortcuts'; 4 | export * from './timeline'; 5 | export * from './viewport'; 6 | -------------------------------------------------------------------------------- /packages/ui/src/contexts/viewport.ts: -------------------------------------------------------------------------------- 1 | import {createContext} from 'preact'; 2 | import {useContext} from 'preact/hooks'; 3 | 4 | export interface ViewportState { 5 | x: number; 6 | y: number; 7 | rect: DOMRectReadOnly; 8 | zoom: number; 9 | grid: boolean; 10 | resolutionScale: number; 11 | } 12 | 13 | const ViewportContext = createContext<ViewportState>({ 14 | x: 0, 15 | y: 0, 16 | rect: new DOMRectReadOnly(), 17 | zoom: 1, 18 | grid: false, 19 | resolutionScale: 1, 20 | }); 21 | 22 | export const ViewportProvider = ViewportContext.Provider; 23 | export function useViewportContext() { 24 | return useContext(ViewportContext); 25 | } 26 | -------------------------------------------------------------------------------- /packages/ui/src/global.d.ts: -------------------------------------------------------------------------------- 1 | declare module '*.scss'; 2 | 3 | declare class EyeDropper { 4 | public open(): Promise<{sRGBHex: string}>; 5 | } 6 | -------------------------------------------------------------------------------- /packages/ui/src/hooks/index.ts: -------------------------------------------------------------------------------- 1 | export * from './useCurrentFrame'; 2 | export * from './useDuration'; 3 | export * from './useFormattedNumber'; 4 | export * from './usePlayerState'; 5 | export * from './usePlayerTime'; 6 | export * from './usePresenterState'; 7 | export * from './useReducedMotion'; 8 | export * from './useRendererState'; 9 | export * from './useScenes'; 10 | export * from './useSettings'; 11 | export * from './useSize'; 12 | export * from './useStateChange'; 13 | export * from './useStorage'; 14 | export * from './useSubscribable'; 15 | export * from './useViewportMatrix'; 16 | -------------------------------------------------------------------------------- /packages/ui/src/hooks/useClickOutside.ts: -------------------------------------------------------------------------------- 1 | import {MutableRef, useEffect, useRef} from 'preact/hooks'; 2 | 3 | export function useClickOutside<T extends Element>( 4 | ref: MutableRef<T>, 5 | onClick: () => void, 6 | ) { 7 | const onClickRef = useRef(onClick); 8 | onClickRef.current = onClick; 9 | useEffect(() => { 10 | const handler = (event: PointerEvent) => { 11 | if (ref.current && !ref.current.contains(event.target as Node)) { 12 | onClickRef.current(); 13 | } 14 | }; 15 | 16 | document.addEventListener('click', handler, true); 17 | return () => document.removeEventListener('click', handler, true); 18 | }, []); 19 | } 20 | -------------------------------------------------------------------------------- /packages/ui/src/hooks/useDuration.ts: -------------------------------------------------------------------------------- 1 | import {useApplication} from '../contexts'; 2 | import {useSubscribableValue} from './useSubscribable'; 3 | 4 | export function useDuration() { 5 | const {player} = useApplication(); 6 | return useSubscribableValue(player.onDurationChanged); 7 | } 8 | -------------------------------------------------------------------------------- /packages/ui/src/hooks/useFormattedNumber.tsx: -------------------------------------------------------------------------------- 1 | import {useMemo} from 'preact/hooks'; 2 | 3 | export function useFormattedNumber(value: number, precision = 6) { 4 | return useMemo(() => { 5 | if (typeof value !== 'number') { 6 | return null; 7 | } 8 | 9 | const absolute = Math.abs(value); 10 | let adjustedPrecision = precision; 11 | if (absolute >= 10) { 12 | adjustedPrecision--; 13 | } 14 | if (absolute >= 100) { 15 | adjustedPrecision--; 16 | } 17 | 18 | return value.toFixed(Math.max(0, adjustedPrecision)); 19 | }, [value, precision]); 20 | } 21 | -------------------------------------------------------------------------------- /packages/ui/src/hooks/usePlayerState.ts: -------------------------------------------------------------------------------- 1 | import {useApplication} from '../contexts'; 2 | import {useSubscribableValue} from './useSubscribable'; 3 | 4 | export function usePlayerState() { 5 | const {player} = useApplication(); 6 | return useSubscribableValue(player.onStateChanged); 7 | } 8 | -------------------------------------------------------------------------------- /packages/ui/src/hooks/usePlayerTime.ts: -------------------------------------------------------------------------------- 1 | import {useMemo} from 'preact/hooks'; 2 | import {useCurrentFrame} from './useCurrentFrame'; 3 | import {useDuration} from './useDuration'; 4 | import {usePreviewSettings} from './useSettings'; 5 | 6 | export function usePlayerTime() { 7 | const {fps} = usePreviewSettings(); 8 | const frame = useCurrentFrame(); 9 | const duration = useDuration(); 10 | 11 | return useMemo( 12 | () => ({ 13 | frame, 14 | time: frame / fps, 15 | duration: duration, 16 | durationTime: duration / fps, 17 | completion: frame / duration, 18 | }), 19 | [frame, duration, fps], 20 | ); 21 | } 22 | -------------------------------------------------------------------------------- /packages/ui/src/hooks/usePresenterState.ts: -------------------------------------------------------------------------------- 1 | import {useApplication} from '../contexts'; 2 | import {useSubscribableValue} from './useSubscribable'; 3 | 4 | export function usePresenterState() { 5 | const {presenter} = useApplication(); 6 | return useSubscribableValue(presenter.onStateChanged); 7 | } 8 | -------------------------------------------------------------------------------- /packages/ui/src/hooks/useReducedMotion.ts: -------------------------------------------------------------------------------- 1 | import {useEffect, useState} from 'preact/hooks'; 2 | 3 | const MediaQuery = window.matchMedia(`(prefers-reduced-motion: reduce)`); 4 | 5 | export function useReducedMotion() { 6 | const [reducedMotion, setReducedMotion] = useState(MediaQuery.matches); 7 | useEffect(() => { 8 | const handler = (event: MediaQueryListEvent) => { 9 | setReducedMotion(event.matches); 10 | }; 11 | MediaQuery.addEventListener('change', handler); 12 | return () => MediaQuery.removeEventListener('change', handler); 13 | }, []); 14 | 15 | return reducedMotion; 16 | } 17 | -------------------------------------------------------------------------------- /packages/ui/src/hooks/useRendererState.ts: -------------------------------------------------------------------------------- 1 | import {useApplication} from '../contexts'; 2 | import {useSubscribableValue} from './useSubscribable'; 3 | 4 | export function useRendererState() { 5 | const {renderer} = useApplication(); 6 | return useSubscribableValue(renderer.onStateChanged); 7 | } 8 | -------------------------------------------------------------------------------- /packages/ui/src/hooks/useScenes.ts: -------------------------------------------------------------------------------- 1 | import {useApplication} from '../contexts'; 2 | import {useSubscribableValue} from './useSubscribable'; 3 | 4 | export function useScenes() { 5 | const {player} = useApplication(); 6 | return useSubscribableValue(player.playback.onScenesRecalculated); 7 | } 8 | 9 | export function useCurrentScene() { 10 | const {player} = useApplication(); 11 | return useSubscribableValue(player.playback.onSceneChanged); 12 | } 13 | -------------------------------------------------------------------------------- /packages/ui/src/hooks/useSettings.ts: -------------------------------------------------------------------------------- 1 | import {useApplication} from '../contexts'; 2 | import {useSubscribableValue} from './useSubscribable'; 3 | 4 | export function useSharedSettings() { 5 | const {meta} = useApplication(); 6 | return useSubscribableValue(meta.shared.onChanged); 7 | } 8 | 9 | export function usePreviewSettings() { 10 | const {meta} = useApplication(); 11 | return useSubscribableValue(meta.preview.onChanged); 12 | } 13 | 14 | export function useRenderingSettings() { 15 | const {meta} = useApplication(); 16 | return useSubscribableValue(meta.rendering.onChanged); 17 | } 18 | -------------------------------------------------------------------------------- /packages/ui/src/hooks/useSize.tsx: -------------------------------------------------------------------------------- 1 | import {MutableRef, useEffect, useMemo, useState} from 'preact/hooks'; 2 | 3 | export function useSize<T extends Element>( 4 | ref: MutableRef<T>, 5 | ): DOMRectReadOnly { 6 | const [rect, setRect] = useState<DOMRect>(new DOMRect()); 7 | const observer = useMemo( 8 | () => 9 | new ResizeObserver(() => setRect(ref.current.getBoundingClientRect())), 10 | [], 11 | ); 12 | useEffect(() => { 13 | const {current} = ref; 14 | observer.observe(current); 15 | return () => observer.unobserve(current); 16 | }, [ref.current]); 17 | 18 | return rect; 19 | } 20 | -------------------------------------------------------------------------------- /packages/ui/src/hooks/useStateChange.ts: -------------------------------------------------------------------------------- 1 | import {Inputs, useLayoutEffect, useRef} from 'preact/hooks'; 2 | 3 | export function useStateChange<T extends Inputs>( 4 | onChange: (prev: T) => void, 5 | inputs: T, 6 | ) { 7 | const previous = useRef(inputs); 8 | useLayoutEffect(() => { 9 | onChange(previous.current); 10 | previous.current = inputs; 11 | }, inputs); 12 | } 13 | -------------------------------------------------------------------------------- /packages/ui/src/img/checkmark.svg: -------------------------------------------------------------------------------- 1 | <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"> 2 | <path 3 | d='M11.185,5l-4.25,4.24l-2.12,-2.12l-1.41,1.41l3.53,3.54l5.66,-5.66l-1.41,-1.41Z' 4 | fill="#000" 5 | /> 6 | </svg> -------------------------------------------------------------------------------- /packages/ui/src/img/dropdown-light.svg: -------------------------------------------------------------------------------- 1 | <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"> 2 | <path 3 | d="M7.995,8.67l-3.17,-3.17l-1.41,1.41l4.58,4.59l4.59,-4.59l-1.42,-1.41l-3.17,3.17Z" 4 | fill="rgba(255, 255, 255, 0.54)" 5 | /> 6 | </svg> -------------------------------------------------------------------------------- /packages/ui/src/img/dropdown.svg: -------------------------------------------------------------------------------- 1 | <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"> 2 | <path 3 | d="M7.995,8.67l-3.17,-3.17l-1.41,1.41l4.58,4.59l4.59,-4.59l-1.42,-1.41l-3.17,3.17Z" 4 | fill="#000" 5 | /> 6 | </svg> -------------------------------------------------------------------------------- /packages/ui/src/img/grid.svg: -------------------------------------------------------------------------------- 1 | <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 2 2"> 2 | <path d="M0 0H2V2H0z" fill="white"/> 3 | <path d="M0 0H1V1H2V2H1V1H0z" fill="#ccc"/> 4 | </svg> -------------------------------------------------------------------------------- /packages/ui/src/plugin/index.module.scss: -------------------------------------------------------------------------------- 1 | .overlay { 2 | position: absolute; 3 | top: 0; 4 | left: 0; 5 | right: 0; 6 | bottom: 0; 7 | } 8 | -------------------------------------------------------------------------------- /packages/ui/src/plugin/index.ts: -------------------------------------------------------------------------------- 1 | import style from './index.module.scss'; 2 | 3 | export * from './EditorPlugin'; 4 | export * from './OverlayWrapper'; 5 | export * from './makeEditorPlugin'; 6 | 7 | export {style}; 8 | -------------------------------------------------------------------------------- /packages/ui/src/plugin/makeEditorPlugin.ts: -------------------------------------------------------------------------------- 1 | import type {EditorPlugin} from './EditorPlugin'; 2 | 3 | /** 4 | * A helper function for exporting editor plugins. 5 | * 6 | * @param plugin - The plugin configuration. 7 | * 8 | * @example 9 | * ```ts 10 | * export default makePlugin({ 11 | * name: 'my-custom-plugin', 12 | * }); 13 | * ``` 14 | * 15 | * @experimental 16 | */ 17 | export function makeEditorPlugin( 18 | plugin: EditorPlugin | (() => EditorPlugin), 19 | ): () => EditorPlugin { 20 | return typeof plugin === 'function' ? plugin : () => plugin; 21 | } 22 | -------------------------------------------------------------------------------- /packages/ui/src/signals/EditorPanel.ts: -------------------------------------------------------------------------------- 1 | export enum EditorPanel { 2 | VideoSettings = 'video-settings-panel', 3 | Threads = 'threads-panel', 4 | Console = 'console-panel', 5 | Settings = 'settings-panel', 6 | Timeline = 'timeline-panel', 7 | } 8 | 9 | export function isEditorPanel(value: string): value is EditorPanel { 10 | return Object.values(EditorPanel).includes(value as EditorPanel); 11 | } 12 | -------------------------------------------------------------------------------- /packages/ui/src/signals/index.ts: -------------------------------------------------------------------------------- 1 | export * from './EditorPanel'; 2 | export * from './labelClip'; 3 | export * from './projectName'; 4 | export * from './storedSignal'; 5 | -------------------------------------------------------------------------------- /packages/ui/src/signals/labelClip.ts: -------------------------------------------------------------------------------- 1 | import {signal} from '@preact/signals'; 2 | 3 | export const labelClipDraggingLeftSignal = signal<number | null>(null); 4 | -------------------------------------------------------------------------------- /packages/ui/src/signals/projectName.ts: -------------------------------------------------------------------------------- 1 | import {signal} from '@preact/signals'; 2 | 3 | export const projectNameSignal = signal<string | null>(null); 4 | -------------------------------------------------------------------------------- /packages/ui/src/utils/clamp.ts: -------------------------------------------------------------------------------- 1 | export function clamp(min: number, max: number, value: number) { 2 | return value < min ? min : value > max ? max : value; 3 | } 4 | -------------------------------------------------------------------------------- /packages/ui/src/utils/formatDuration.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Return the given duration in a HH:MM:SS format. 3 | * 4 | * @param duration - The duration in seconds. 5 | */ 6 | export function formatDuration(duration: number) { 7 | if (duration === Infinity || duration === -Infinity || isNaN(duration)) { 8 | return '??:??:??'; 9 | } 10 | 11 | const hours = Math.floor(duration / 3600); 12 | const minutes = Math.floor(duration / 60) % 60; 13 | const seconds = Math.floor(duration % 60); 14 | 15 | return `${hours.toString().padStart(2, '0')}:${minutes 16 | .toString() 17 | .padStart(2, '0')}:${seconds.toString().padStart(2, '0')}`; 18 | } 19 | -------------------------------------------------------------------------------- /packages/ui/src/utils/index.ts: -------------------------------------------------------------------------------- 1 | export * from './LoggerManager'; 2 | export * from './clamp'; 3 | export * from './compareVersions'; 4 | export * from './formatDuration'; 5 | export * from './localStorage'; 6 | export * from './mouse'; 7 | export * from './openOutputPath'; 8 | export * from './sourceMaps'; 9 | export * from './withLoader'; 10 | -------------------------------------------------------------------------------- /packages/ui/src/utils/localStorage.ts: -------------------------------------------------------------------------------- 1 | export function getItem<T>(key: string, initial: T): T { 2 | try { 3 | return JSON.parse(localStorage.getItem(key)); 4 | } catch (_) { 5 | return initial; 6 | } 7 | } 8 | 9 | export function setItem(key: string, value: unknown) { 10 | localStorage.setItem(key, JSON.stringify(value)); 11 | } 12 | -------------------------------------------------------------------------------- /packages/ui/src/utils/mouse.ts: -------------------------------------------------------------------------------- 1 | export enum MouseButton { 2 | Left = 0, 3 | Middle = 1, 4 | Right = 2, 5 | } 6 | 7 | export enum MouseMask { 8 | None = 0, 9 | Primary = 1, 10 | Secondary = 2, 11 | Auxiliary = 4, 12 | Back = 8, 13 | Forward = 16, 14 | } 15 | -------------------------------------------------------------------------------- /packages/ui/src/utils/openOutputPath.ts: -------------------------------------------------------------------------------- 1 | import {withLoader} from './withLoader'; 2 | 3 | export function openOutputPath() { 4 | return withLoader(async () => { 5 | await fetch('/__open-output-path'); 6 | }); 7 | } 8 | -------------------------------------------------------------------------------- /packages/ui/src/utils/withLoader.ts: -------------------------------------------------------------------------------- 1 | let LoaderCount = 0; 2 | 3 | /** 4 | * Execute a given async function while displaying a loading indication. 5 | * 6 | * @param callback - A function to execute. 7 | */ 8 | export async function withLoader(callback: () => Promise<unknown>) { 9 | LoaderCount++; 10 | document.body.classList.add('wait'); 11 | try { 12 | await callback(); 13 | } finally { 14 | LoaderCount--; 15 | if (LoaderCount === 0) { 16 | document.body.classList.remove('wait'); 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /packages/ui/src/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// <reference types="vite/client" /> 2 | -------------------------------------------------------------------------------- /packages/ui/tsconfig.plugin.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "strict": true, 4 | "target": "esnext", 5 | "module": "esnext", 6 | "moduleResolution": "node", 7 | "moduleDetection": "force", 8 | "isolatedModules": true, 9 | "esModuleInterop": false, 10 | "resolveJsonModule": true, 11 | "allowJs": true, 12 | "sourceMap": true, 13 | "declaration": true, 14 | "declarationMap": true, 15 | "skipLibCheck": true, 16 | "lib": ["DOM", "DOM.Iterable", "ESNext"], 17 | "jsx": "preserve", 18 | "jsxImportSource": "preact" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /packages/ui/tsdoc.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://developer.microsoft.com/json-schemas/tsdoc/v0/tsdoc.schema.json", 3 | "extends": ["../../tsdoc.json"] 4 | } 5 | -------------------------------------------------------------------------------- /packages/ui/types/main.d.ts: -------------------------------------------------------------------------------- 1 | import type {Project} from '@motion-canvas/core'; 2 | 3 | export function editor(project: Project): void; 4 | 5 | export function index( 6 | projects: { 7 | name: string; 8 | url: string; 9 | }[], 10 | ): void; 11 | -------------------------------------------------------------------------------- /packages/ui/vite.showcase.ts: -------------------------------------------------------------------------------- 1 | import preact from '@preact/preset-vite'; 2 | import {defineConfig} from 'vite'; 3 | 4 | export default defineConfig({ 5 | build: { 6 | lib: { 7 | entry: 'src/main.tsx', 8 | formats: ['es'], 9 | fileName: 'main', 10 | }, 11 | outDir: '../docs/static/editor', 12 | }, 13 | plugins: [preact()], 14 | }); 15 | -------------------------------------------------------------------------------- /packages/vite-plugin/src/globals.d.ts: -------------------------------------------------------------------------------- 1 | /// <reference types="@motion-canvas/internal" /> 2 | -------------------------------------------------------------------------------- /packages/vite-plugin/src/index.ts: -------------------------------------------------------------------------------- 1 | import motionCanvas from './main'; 2 | 3 | export default motionCanvas; 4 | export * from './plugins'; 5 | -------------------------------------------------------------------------------- /packages/vite-plugin/src/partials/index.ts: -------------------------------------------------------------------------------- 1 | export * from './assets'; 2 | export * from './corsProxy'; 3 | export * from './editor'; 4 | export * from './exporter'; 5 | export * from './meta'; 6 | export * from './projects'; 7 | export * from './scenes'; 8 | export * from './settings'; 9 | export * from './webgl'; 10 | -------------------------------------------------------------------------------- /packages/vite-plugin/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "outDir": "lib", 4 | "strict": true, 5 | "module": "commonjs", 6 | "esModuleInterop": true, 7 | "target": "es2021", 8 | "moduleResolution": "node", 9 | "sourceMap": true, 10 | "declaration": true, 11 | "allowSyntheticDefaultImports": true, 12 | "skipLibCheck": true, 13 | "plugins": [ 14 | { 15 | "transform": "@motion-canvas/internal/transformers/markdown-literals.js" 16 | } 17 | ] 18 | }, 19 | "include": ["src"] 20 | } 21 | -------------------------------------------------------------------------------- /packages/vite-plugin/tsdoc.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://developer.microsoft.com/json-schemas/tsdoc/v0/tsdoc.schema.json", 3 | "extends": ["../../tsdoc.json"] 4 | } 5 | --------------------------------------------------------------------------------