← Files PicsartARCHIVED FILE
docs/widget-specs/motion-setup.md
8.74 KB · Oct 10, 2026 · 18:02 UTC
# Widget spec — `picsart_motion_setup`
> A **new** widget in `pa-ai-models-mcp-server/server/widgets/src/motion-setup/`, built and
> served like `film-setup` (its closest analog). Older drafts named this `mp_motion_setup`.
The setup console — a **Cascade-styled panel** where the designer sets the video-wide
motion frame: output format (ratio/resolution), fps, and energy. **Collecting these here,
in the widget, is the intended experience.** The format questions are good ones; what is
wrong is asking them in a bare Claude dialog. So this panel — not an the host's question interface
menu — is where they are answered. It opens **prefilled with inferred defaults** (ratio
from the frames' aspect, fps 30, energy read from the content) so the designer one-click
confirms rather than fills a form.
**Total duration defaults to "Derived from the motion"** — a checkbox **checked by default**,
so the length is the sum of the holds (each grown to fit its own animation) and nothing has
to be decided up front. A designer who wants a specific runtime **unchecks it and sets a
target**. That target is a *soft budget*: holds scale toward it where there is slack, but
never below each hold's motion-minimum — so a too-short target **lengthens** the video rather
than clipping an animation. Derive is the default because pacing should fall out of the
motion; the target is there for the designer who genuinely needs a fixed length.
**The standard Claude the host's question interface dialog is a degraded last resort only** — it appears
when this panel is not deployed (see the plugin's known blocker), and it is exactly the
off-brand experience this Cascade console exists to replace. A bare dialog in the transcript
means the panel has not shipped, not that a dialog was the right surface.
## Design system — Cascade
Rendered in the **Cascade design system**, re-expressed for the MCP iframe **exactly the way
the existing gen-ai widgets do it** — it reuses their `shared/foundation/themes.css` +
`shared/styles.css`, not a fresh port. The tokens (magenta primary `#C209C1`, the neutral
ramp, radii, status colors) live as a **host-color-independent, WCAG-AA-corrected** local
layer: never a host `--color-*` token (MCP hosts inject their own and some are not
contrast-safe), and both `[data-theme="dark"]` and `@media (prefers-color-scheme: dark)` are
mapped for parity. Body background transparent; the type face is **Inter**
(`--font-sans`/`--font-display`) — the same as `film-setup` and the other gen-ai widgets,
**not Gilroy**, and no external font host is referenced. This is what makes the panel read as
native Picsart rather than a generic dialog — the whole point of using it instead of
the host's question interface.
## Tool input
```ts
{
frames?: Array<{ id: string, url: string, w?: number, h?: number }>, // for the ratio inference + strip
suggested?: { // your inferences from the material, each with a one-line why
selections?: Partial<Selections> // seeded silently; the console validates each id and drops unknowns
},
current?: Selections // previously locked state; always wins over `suggested`
}
```
`suggested` is how the console opens on the material rather than on its defaults: pass
the ratio the frames already are, a hold that fits their count against a sensible
runtime, and an energy read from the design. A wrong prefill reads to the user as a
decision someone already made — so pass only what the material actually decides.
`current` (a re-open) always beats `suggested`.
## Selections
```ts
type Selections = {
ratio: '9:16' | '16:9' | '1:1' | '4:3' | '3:4', // the compositor/model-safe five
fps: 24 | 30 | 60,
hold: number, // default seconds each frame holds; per-frame override lives on the frame-board
transition: string, // default seam effect id (see references/TRANSITIONS.md); '' = hard cut
transitionDuration: number, // default seam window in seconds
energy: 'calm' | 'standard' | 'energetic', // global motion register: eases + transition intensity
duration: { mode: 'derived' | 'target', seconds?: number }
// 'derived' (DEFAULT, box checked): total = sum of holds, each ≥ its motion.
// 'target': a soft length; holds scale toward `seconds` but never below their motion-minimum.
}
```
- **`ratio` constrains more than the look** — it decides which frames get letterboxed
vs. filled, and (in Phase 2) which video models are usable. The five listed render
everywhere; keep scope crops (2.39, 21:9) out — shoot 16:9 and crop in the edit.
- **`energy` is an inferred register, never a question.** `calm` → softer eases; `energetic`
→ snappier eases and punchier default transitions. You **infer it from the design** (a
dense UI walkthrough vs. a calm hero reveal) and proceed; the designer adjusts it by
saying "punchier," not by being asked. It carries **no duration target** — a punchier
register means snappier *motion*, not a shorter *deadline*; total length still derives
from the animations. It sets defaults only; the editor overrides any one element.
- **`hold` / `transitionDuration` are defaults, not locks.** The frame-board sets
per-frame hold and per-seam transitions on top; this console just seeds them so a
first assembly is watchable without touching every seam.
## Layout
Full width, four stacked regions, minimum height 280px.
- **Strip** — the frames as a horizontal thumbnail row in order, so the ratio choice is
judged against the real design, not an abstract chip.
- **Format** — ratio tiles (each showing a frame letterboxed into it), an fps segmented
control, and a **Duration** control: a *"Derived from the motion"* checkbox **checked by
default**; unchecking it reveals a target-seconds field (the soft-budget path).
- **Motion** — the `energy` selector (three tiles, each a looping micro-preview of the
same two frames transitioning at that register), plus default-transition and
default-hold controls that update live from the energy pick but can be nudged.
- **Confirm** — a `summary` read-out sentence and one **Lock** button.
## Behavior
Picking `energy` **re-seeds** `transition`, `transitionDuration` and the eases, and the
micro-preview re-renders — but a value the user then nudges by hand is theirs and is
not re-seeded by a later energy change. Same "your edit is never overwritten" rule the
editor's drag follows.
The console **applies `suggested` silently** and validates every id against its own
options, dropping what it does not recognise — so a prefill the material did not really
decide never appears as a preselected answer.
## Feedback
`picsart_motion_setup` renders the console and returns only the input you passed. The
decision arrives later, on **Lock**, as one payload:
```json
{
"type": "motion_setup_feedback", "version": 1,
"selections": {
"ratio": "9:16", "fps": 30, "hold": 2.0,
"transition": "slide", "transitionDuration": 0.5, "energy": "standard",
"duration": { "mode": "derived" }
},
"summary": "Vertical 9:16 · 30 fps · Slide 0.5s · Standard energy · length from the motion.",
"verdict": "locked"
}
```
- Store `selections` into `motion.json.target` (ratio/width/height/fps) and as the seam
defaults; store `summary` verbatim to quote the decision back without reassembling it
from ids.
- `verdict` is always `locked` — the console has one button, and every control ships
answered.
- **Pass the format back in however it came up.** `suggested.selections.ratio` should
accept the id, the label, or the plain word (`vertical` / `9:16` / `portrait` /
`tiktok` all resolve to `9:16`) — the ids are opaque, and a suggestion sent as `9:16`
that gets validated-and-dropped is how "I asked for vertical" becomes a landscape
frame preselected as though someone chose it.
## Model protocol
Same as `scene-editor` and the film boards:
- **Opening the console is the whole turn.** No prose above or below it — it states its
own ask.
- **After opening it, stop and wait.** The decision arrives on Lock; do not speculate
about what they will pick or ask them to retype it in prose.
- **Read the console's app context on the return turn** — the `motion_setup_feedback`
payload is attached there, not in the visible sentence.
## Fallback
When the panel is not available, **do not interview the designer.** Infer the format from
the design (ratio from the frames' aspect, fps 30, energy as a register read from the
content), **state the inferred choices in one line they can override** — e.g. *"9:16,
30fps — say if you want it different"* — and proceed. Write the same `motion.json.target`
either way. **Never** turn setup into an the host's question interface menu: the design already answers
the ratio, and energy and duration come from the motion, not from a target the designer is
asked to pick.
SHA-256: 2702e81e11959a5ff0ecf80e4ae94b1a1cc876080b6b69cb301cb201dda4f50f