Latest Version0.1.0-alpha.5
motionscript

Components

Some Motion Script components (Code, Latex) don't ship as npm dependencies. Instead, ms add copies their source directly into your project — full customizability, no black-box package to work around, at the cost of picking up fixes manually. This is the same approach shadcn/ui popularized for UI components.

motionscript.json

ms init writes a motionscript.json file to your project root and wires a @/components/* path alias into tsconfig.json (for editor/typechecker support — Vite resolves it directly from motionscript.json, not from tsconfig):

ms init
{
  "$schema": "https://motionscript.dev/schema/motionscript.json",
  "project": "src/project.ts",
  "aliases": { "components": "@/components" },
  "paths": { "components": "src/components" }
}

ms add runs ms init for you the first time if motionscript.json doesn't exist yet, so you rarely need to call it directly.

motionscript.lock.json

Alongside it, ms add writes a lockfile recording — for every file it installs — which registry and version it came from, and a hash of the bytes written.

That one record answers the question copying a component otherwise makes unanswerable: is this file yours or ours? Re-hashing the file on disk says exactly whether you have edited it since, which is what ms list and ms diff report and what stops ms add asking about files you never touched.

Commit it, for the reason you commit any lockfile. It is generated, so it is kept separate from motionscript.json, which you write by hand.

ms add <name>

Copies a component's source into <paths.components>/<name>/ (by default src/components/<name>/) and installs the npm packages it depends on:

ms add code
ms add code latex --yes   # add more than one, skip prompts

Run ms add with no name for an interactive picker over everything the default registry offers. Useful flags:

FlagEffect
--yesSkip prompts, accept defaults, don't ask before overwriting
--overwriteOverwrite existing files without asking
--skip-installDon't install npm dependencies after adding
--registry <url>Override the default registry (a URL or a local path)

The copied files are yours — import them from the alias ms init set up rather than from @motionscript/sdk:

import { Code, lines, word } from '@/components/code';

A component is a node type, and a document can only name a node type the engine has been given. Add it to the nodes your project entry exports, and hand those to the engine:

import { Code } from '@/components/code';

export const nodes = [Code];

From then on, any document opened on that engine can add a "type": "code" node.

A component that needs more than one file lands in a folder of its own (src/components/line-chart/), and its files keep importing each other. What they never copy is the component's engine — the algorithms behind it stay a real npm dependency, so a fix to those reaches you without a re-add, and so there is only ever one copy of the classes the renderer identifies by type.

ms list and ms diff

Copying source means fixes arrive manually. These two are how you find out what is waiting.

ms list             # what is installed, and whether you have edited it
ms list --check     # ...and whether a newer version exists upstream
ms diff             # what changed upstream, and on which side
ms diff line-chart  # just this one

ms diff marks each file: changed upstream, changed locally, or changed on both sides — the only state that needs you to decide something. Nothing is ever taken automatically; an update is applied by adding the component again, which prompts before overwriting anything you have changed.

Registries

A registry serves two endpoints: an index (registry.json, listing available components) and one JSON file per component (<name>.json, the source files plus npm dependencies). The default, unnamespaced registry is built into the CLI (https://motionscript.dev/r) — that's what ms add code resolves against.

Additional or private registries go under motionscript.json's registries field, keyed by @namespace:

{
  "registries": {
    "@acme": {
      "url": "https://registry.acme.dev/{name}.json",
      "headers": { "Authorization": "Bearer ${ACME_TOKEN}" }
    }
  }
}

ms add @acme/some-component looks up the @acme entry, substitutes some-component into the {name} placeholder, and expands ${VAR} in headers (and params) against your environment — so a token like ACME_TOKEN above never has to be written into the file. A registry url can also be a local filesystem path, for testing a registry before it's deployed anywhere.