@a-company/atelier 0.36.0 → 0.38.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (99) hide show
  1. package/dist/{chunk-JPZ4F4PW.js → chunk-3ARBOSWY.js} +64 -5
  2. package/dist/chunk-3ARBOSWY.js.map +1 -0
  3. package/dist/cli.js +11469 -413
  4. package/dist/cli.js.map +1 -1
  5. package/dist/{dist-M67UZGFQ.js → dist-3YQK6PI6.js} +2 -2
  6. package/dist/index.cjs +3193 -227
  7. package/dist/index.cjs.map +1 -1
  8. package/dist/index.d.cts +701 -8
  9. package/dist/index.d.ts +701 -8
  10. package/dist/index.js +7237 -72
  11. package/dist/index.js.map +1 -1
  12. package/dist/mcp.js +2898 -507
  13. package/dist/mcp.js.map +1 -1
  14. package/package.json +14 -9
  15. package/src/web/inline-app.ts +55 -4
  16. package/src/web/timeline-state-types.ts +28 -0
  17. package/src/web/timeline-view.test.ts +99 -0
  18. package/src/web/timeline-view.ts +339 -0
  19. package/src/web/workspace-app.ts +3146 -0
  20. package/templates/workspace/.claude/agents/atelier-iris.md +75 -0
  21. package/templates/workspace/.claude/agents/atelier-lux.md +67 -0
  22. package/templates/workspace/.claude/agents/atelier-quill.md +61 -0
  23. package/templates/workspace/.gitignore +30 -0
  24. package/templates/workspace/.paradigm/personas/_shared/cascade-merge.md +172 -0
  25. package/templates/workspace/CLAUDE.md +93 -0
  26. package/templates/workspace/README.md +75 -0
  27. package/templates/workspace/SETUP.md +127 -0
  28. package/templates/workspace/_brand/.atelier-brand.yaml +34 -0
  29. package/templates/workspace/_brand/DESIGN.md +56 -0
  30. package/templates/workspace/_brand/SCRIPT.md +41 -0
  31. package/templates/workspace/_brand/STORYBOARD.md +33 -0
  32. package/templates/workspace/_packs/README.md +54 -0
  33. package/templates/workspace/projects/README.md +49 -0
  34. package/templates/workspace/workspace.atelier +22 -0
  35. package/university/content/notes/N-atel-001-first-render.md +114 -0
  36. package/university/content/notes/N-atel-001-install-and-launch.md +84 -0
  37. package/university/content/notes/N-atel-001-what-is-atelier.md +51 -0
  38. package/university/content/notes/N-atel-101-easings.md +97 -0
  39. package/university/content/notes/N-atel-101-layers.md +106 -0
  40. package/university/content/notes/N-atel-101-states-and-deltas.md +94 -0
  41. package/university/content/notes/N-atel-101-the-atelier-format.md +72 -0
  42. package/university/content/notes/N-atel-201-authoring-tools.md +141 -0
  43. package/university/content/notes/N-atel-201-mcp-overview.md +86 -0
  44. package/university/content/notes/N-atel-201-patterns.md +108 -0
  45. package/university/content/notes/N-atel-201-visual-and-effects.md +125 -0
  46. package/university/content/notes/N-atel-301-composition-and-overlays.md +141 -0
  47. package/university/content/notes/N-atel-301-effects.md +136 -0
  48. package/university/content/notes/N-atel-301-images-and-video.md +126 -0
  49. package/university/content/notes/N-atel-301-shapes-and-text.md +118 -0
  50. package/university/content/notes/N-atel-401-hierarchical-states.md +71 -0
  51. package/university/content/notes/N-atel-401-motion-deep-dive.md +106 -0
  52. package/university/content/notes/N-atel-401-presets-and-templates.md +98 -0
  53. package/university/content/notes/N-atel-401-transitions.md +94 -0
  54. package/university/content/notes/N-atel-501-detected-vs-user-edited.md +76 -0
  55. package/university/content/notes/N-atel-501-layer-tag-isolation.md +62 -0
  56. package/university/content/notes/N-atel-501-silence-trim.md +98 -0
  57. package/university/content/notes/N-atel-501-transcribe-and-captions.md +98 -0
  58. package/university/content/notes/N-atel-601-carousel.md +71 -0
  59. package/university/content/notes/N-atel-601-overlay-rules.md +96 -0
  60. package/university/content/notes/N-atel-601-recipe-tools-and-apply.md +84 -0
  61. package/university/content/notes/N-atel-601-studio-recipe.md +103 -0
  62. package/university/content/notes/N-atel-701-choosing-output.md +68 -0
  63. package/university/content/notes/N-atel-701-png-and-frames.md +84 -0
  64. package/university/content/notes/N-atel-701-vector.md +85 -0
  65. package/university/content/notes/N-atel-701-video.md +88 -0
  66. package/university/content/notes/N-atel-801-editing-surface.md +69 -0
  67. package/university/content/notes/N-atel-801-live-bridge.md +84 -0
  68. package/university/content/notes/N-atel-801-studio-app.md +72 -0
  69. package/university/content/notes/N-atel-801-symbiotic-loop.md +56 -0
  70. package/university/content/paths/LP-atel-001.yaml +21 -0
  71. package/university/content/paths/LP-atel-101.yaml +22 -0
  72. package/university/content/paths/LP-atel-201.yaml +23 -0
  73. package/university/content/paths/LP-atel-301.yaml +22 -0
  74. package/university/content/paths/LP-atel-401.yaml +22 -0
  75. package/university/content/paths/LP-atel-501.yaml +22 -0
  76. package/university/content/paths/LP-atel-601.yaml +22 -0
  77. package/university/content/paths/LP-atel-701.yaml +22 -0
  78. package/university/content/paths/LP-atel-801.yaml +22 -0
  79. package/university/content/quizzes/Q-atel-001-orientation.yaml +66 -0
  80. package/university/content/quizzes/Q-atel-101-document-model.yaml +66 -0
  81. package/university/content/quizzes/Q-atel-201-mcp-authoring.yaml +66 -0
  82. package/university/content/quizzes/Q-atel-301-visual-system.yaml +66 -0
  83. package/university/content/quizzes/Q-atel-401-state-machines.yaml +66 -0
  84. package/university/content/quizzes/Q-atel-501-video-pipeline.yaml +66 -0
  85. package/university/content/quizzes/Q-atel-601-recipes.yaml +66 -0
  86. package/university/content/quizzes/Q-atel-701-export.yaml +66 -0
  87. package/university/content/quizzes/Q-atel-801-studio-loop.yaml +66 -0
  88. package/university/index.yaml +720 -0
  89. package/university/pack.yaml +21 -0
  90. package/dist/chunk-5QQESXI6.js +0 -4432
  91. package/dist/chunk-5QQESXI6.js.map +0 -1
  92. package/dist/chunk-JPZ4F4PW.js.map +0 -1
  93. package/dist/cli.cjs +0 -6313
  94. package/dist/cli.cjs.map +0 -1
  95. package/dist/cli.d.cts +0 -1
  96. package/dist/cli.d.ts +0 -1
  97. package/dist/mcp.cjs +0 -5462
  98. package/dist/mcp.cjs.map +0 -1
  99. /package/dist/{dist-M67UZGFQ.js.map → dist-3YQK6PI6.js.map} +0 -0
@@ -0,0 +1,97 @@
1
+ ---
2
+ id: N-atel-101-easings
3
+ title: Easings — linear, cubic-bezier, presets, spring, step
4
+ type: note
5
+ track: ATEL-101
6
+ author: atelier
7
+ created: '2026-05-18'
8
+ updated: '2026-05-18'
9
+ tags:
10
+ - course
11
+ - atel-101
12
+ - easings
13
+ - motion
14
+ difficulty: beginner
15
+ estimatedMinutes: 4
16
+ prerequisites:
17
+ - N-atel-101-states-and-deltas
18
+ summary: Five easing kinds in Atelier. What each is, when to use it, and the one rule that prevents 90% of bad motion (don't use `linear` for organic movement).
19
+ ---
20
+
21
+ ## What an easing does
22
+
23
+ An easing is a function `f(t: 0..1) → 0..1` that maps "fraction of time elapsed" to "fraction of value progress." `f(0) = 0`, `f(1) = 1`. What happens in between is the personality of the motion.
24
+
25
+ Atelier supports five kinds:
26
+
27
+ ## 1. `linear`
28
+
29
+ `f(t) = t`. Constant rate. Use for: progress bars, scrubbing, opacity crossfades where the perceived speed matters. **Do not use** for object motion — it looks robotic. Most "feels wrong" motion in the wild is `linear` doing the wrong job.
30
+
31
+ ```yaml
32
+ easing: linear
33
+ ```
34
+
35
+ ## 2. Cubic-Bezier — `cubic-bezier(x1, y1, x2, y2)`
36
+
37
+ The CSS-style four-control-point bezier. Full control over the shape. This is what every preset below desugars to.
38
+
39
+ ```yaml
40
+ easing: cubic-bezier(0.4, 0.0, 0.2, 1.0) # Material "standard"
41
+ ```
42
+
43
+ If you've used CSS transitions, this is the same function.
44
+
45
+ ## 3. Ease presets
46
+
47
+ Named shortcuts to common bezier curves. Use these instead of memorizing coefficients.
48
+
49
+ | Preset | Bezier | Use for |
50
+ |---|---|---|
51
+ | `ease-in` | `(0.4, 0, 1, 1)` | Object accelerating off-screen |
52
+ | `ease-out` | `(0, 0, 0.2, 1)` | Object decelerating on entrance (most UI entrance animation) |
53
+ | `ease-in-out` | `(0.4, 0, 0.2, 1)` | Crossfades, value-to-value transitions where both endpoints are visible |
54
+ | `ease` | `(0.25, 0.1, 0.25, 1)` | The CSS default — generic "feels OK" |
55
+
56
+ Default for most UI motion: `ease-out` for entrances, `ease-in` for exits.
57
+
58
+ ## 4. Spring physics — `spring({ stiffness, damping, mass? })`
59
+
60
+ Physics-based. Produces overshoot and oscillation that no bezier can match. Use for: anything that should feel tactile — toggles, drag-release, dismissals.
61
+
62
+ ```yaml
63
+ easing:
64
+ type: spring
65
+ stiffness: 170
66
+ damping: 20
67
+ mass: 1
68
+ ```
69
+
70
+ Stiffness controls how aggressively the spring pulls toward the target. Damping controls how quickly oscillation dies out. Atelier solves the spring numerically per-frame; the same `startFrame`–`endFrame` window still applies (the spring is clipped at `endFrame` and held at the resolved value after).
71
+
72
+ When in doubt: `stiffness: 170, damping: 26` for a clean snap; `stiffness: 100, damping: 10` for visible oscillation.
73
+
74
+ ## 5. Step — `step(count)` or `step(count, position)`
75
+
76
+ Discrete jumps. `step(4)` produces four equal jumps from `from` to `to`. Use for: spritesheet frame indexing (jump through frames at 12fps inside a 60fps composition), digital-readout counters, deliberate stop-motion feel.
77
+
78
+ ```yaml
79
+ easing:
80
+ type: step
81
+ count: 4
82
+ position: start # 'start' | 'end' | 'jump-none' | 'jump-both'
83
+ ```
84
+
85
+ ## The rule that prevents most bad motion
86
+
87
+ > If a real physical object would do it, use spring or ease-out.
88
+ > If a UI element is reporting progress, use linear.
89
+ > Almost nothing else should be linear.
90
+
91
+ Easings are why motion feels "designed" or "wrong." The engine doesn't care which you pick — the renderer interpolates whatever `f(t)` returns. But your viewer does care, every time.
92
+
93
+ ## Inspecting an easing
94
+
95
+ The studio's easing-curve-editor (in PropertyPanel when you select a delta) shows the bezier curve, lets you drag control points, and previews the shape against the timeline. For springs, it samples the solver and plots the actual oscillation. There is no "easings preview file" — every delta you author can be inspected and tuned in place.
96
+
97
+ That closes ATEL-101. You now know enough to read any `.atelier` document and predict what it will render. ATEL-201 covers the MCP tool surface — how an agent builds documents like this without ever touching YAML.
@@ -0,0 +1,106 @@
1
+ ---
2
+ id: N-atel-101-layers
3
+ title: Layer anatomy
4
+ type: note
5
+ track: ATEL-101
6
+ author: atelier
7
+ created: '2026-05-18'
8
+ updated: '2026-05-18'
9
+ tags:
10
+ - course
11
+ - atel-101
12
+ - layers
13
+ - format
14
+ difficulty: beginner
15
+ estimatedMinutes: 5
16
+ prerequisites:
17
+ - N-atel-101-the-atelier-format
18
+ summary: Every field on the `Layer` type, what it does, what's required vs optional, and which fields are animatable through deltas.
19
+ ---
20
+
21
+ ## The minimum layer
22
+
23
+ ```yaml
24
+ - id: title
25
+ visual:
26
+ type: text
27
+ content: Hello
28
+ style:
29
+ fontFamily: Inter, system-ui, sans-serif
30
+ fontSize: 48
31
+ frame:
32
+ x: 400
33
+ y: 300
34
+ bounds:
35
+ width: 600
36
+ height: 80
37
+ ```
38
+
39
+ Four required fields: `id`, `visual`, `frame`, `bounds`. The rest are optional and default to sensible values.
40
+
41
+ ## The six visual types
42
+
43
+ The `visual` field is a discriminated union on `type`:
44
+
45
+ | `type` | Renders | Required sub-fields |
46
+ |---|---|---|
47
+ | `shape` | A geometric shape — rect, ellipse, polygon, path | `shape`, optional `fill` and `stroke` |
48
+ | `text` | A string with typography | `content`, `style` (fontFamily required) |
49
+ | `image` | A static image asset | `assetId` (and optional `src`, `sourceRect`, `spritesheet`) |
50
+ | `video` | A video clip | `assetId`, optional `sourceOffset`, `sourceEnd`, `playbackRate` |
51
+ | `group` | A container for child layers (via `parentId`) | none — just a container |
52
+ | `ref` | A reference to another `.atelier` file rendered as a sub-document | `src`, optional `state` and `frame` |
53
+
54
+ ## Position, size, and anchor
55
+
56
+ - **`frame: { x, y }`** — position of the anchor point in canvas pixels.
57
+ - **`bounds: { width, height }`** — the layer's box.
58
+ - **`anchorPoint: { x, y }`** — normalized 0–1 coordinate inside the bounds that `frame` positions. `{x: 0.5, y: 0.5}` centers the layer on `frame`. `{x: 0, y: 0}` puts the top-left at `frame`. Default is `{x: 0, y: 0}`.
59
+
60
+ The anchor also defines the pivot for `rotation` and `scale`.
61
+
62
+ ## Animatable layer properties
63
+
64
+ These can appear in deltas (`property: <name>`):
65
+
66
+ | Property | Type | Default |
67
+ |---|---|---|
68
+ | `opacity` | number 0–1 | 1 |
69
+ | `rotation` | degrees | 0 |
70
+ | `scale` | `{ x, y }` | `{1, 1}` |
71
+ | `frame.x`, `frame.y` | number | as authored |
72
+ | `bounds.width`, `bounds.height` | number | as authored |
73
+ | `tint.amount` | 0–1 (color tint strength) | 0 |
74
+ | Any color, gradient stop, stroke width, shadow blur on the visual or its style | various | — |
75
+
76
+ The full animatable list is 27 properties. The schema enforces that a delta's `property` path is reachable on the named layer's visual.
77
+
78
+ ## Non-animatable structural fields
79
+
80
+ | Field | Purpose |
81
+ |---|---|
82
+ | `id` | Unique identifier |
83
+ | `tags` | Namespace markers (used by pipelines for layer-tag isolation) |
84
+ | `description` | Human-readable note, kept out of render |
85
+ | `parentId` | Parents this layer to another for transform inheritance |
86
+ | `visible` | Hard on/off (rendered if `true` or absent) |
87
+ | `blendMode` | Canvas composite operation |
88
+ | `clipPath` | A Shape that restricts rendering to its interior |
89
+ | `motionPath` | A path the layer follows over time, with optional auto-rotate |
90
+ | `interactions` | Event-driven trigger-action bindings (e.g. hover → state transition) |
91
+
92
+ ## Parent-child and transform inheritance
93
+
94
+ Setting `parentId` on a layer makes its `frame`, `rotation`, and `scale` relative to the parent's transformed coordinate space. A `group` visual has no rendered output of its own — it exists purely to provide a parent transform. Use groups when you want to translate or rotate a cluster of layers as one unit.
95
+
96
+ ## Tags: the pipeline contract
97
+
98
+ Tags are how the silence-trim, caption, and overlay pipelines find layers they own. Each pipeline writes a tag namespace:
99
+
100
+ - `silence-trim` — clip layers produced by `atelier trim`
101
+ - `caption`, `caption-word` — text layers produced by `atelier captions regenerate`
102
+ - `overlay` — handle / page-number / recipe-driven decorative layers
103
+
104
+ The invariant: re-running a pipeline drops only its own tagged layers and replaces them. Layers without that tag (user-authored, or owned by another pipeline) are never touched. This is what makes the human↔agent symbiotic loop safe (ATEL-501 covers this in depth).
105
+
106
+ Next: states and deltas.
@@ -0,0 +1,94 @@
1
+ ---
2
+ id: N-atel-101-states-and-deltas
3
+ title: States, deltas, and `resolveFrame`
4
+ type: note
5
+ track: ATEL-101
6
+ author: atelier
7
+ created: '2026-05-18'
8
+ updated: '2026-05-18'
9
+ tags:
10
+ - course
11
+ - atel-101
12
+ - states
13
+ - deltas
14
+ - resolve-frame
15
+ difficulty: beginner
16
+ estimatedMinutes: 5
17
+ prerequisites:
18
+ - N-atel-101-layers
19
+ summary: A state is a named span of frames. A delta animates one property of one layer over a sub-range of that span. `resolveFrame(doc, stateName, frame)` is the function the renderer calls — knowing its shape demystifies everything else.
20
+ ---
21
+
22
+ ## A state is a named span
23
+
24
+ ```yaml
25
+ states:
26
+ default:
27
+ duration: 60
28
+ deltas:
29
+ - layer: title
30
+ property: opacity
31
+ from: 0
32
+ to: 1
33
+ startFrame: 0
34
+ endFrame: 30
35
+ easing: ease-out
36
+ ```
37
+
38
+ `default` is the state name. `duration: 60` means this state runs for 60 frames (one second at 60fps). Frames are 0-indexed, so this state spans `0..59` inclusive. The renderer plays through the state and either loops, transitions to another state, or stops, depending on configuration.
39
+
40
+ A document always has at least one state. Most documents have only one. Multi-state documents are the foundation of interactive UI motion (`hover`, `active`, `pressed`) and segmented narratives.
41
+
42
+ ## A delta animates one property
43
+
44
+ Every field on a delta exists for a reason:
45
+
46
+ | Field | Meaning |
47
+ |---|---|
48
+ | `layer` | Which layer (by id) this delta animates |
49
+ | `property` | Which animatable property (e.g. `opacity`, `frame.x`, `style.color`) |
50
+ | `from` | Starting value at `startFrame` |
51
+ | `to` | Ending value at `endFrame` |
52
+ | `startFrame` | Frame within the state where this delta begins |
53
+ | `endFrame` | Frame within the state where this delta ends |
54
+ | `easing` | How the value interpolates between `from` and `to` |
55
+ | `expression` | Optional override — a function of `t` (0..1) returning the interpolated value, for non-trivial animations |
56
+
57
+ Outside `[startFrame, endFrame]`, the property holds its boundary value (clamped). Multiple deltas on the same `layer` + `property` are rejected by the schema's no-overlap gate — overlapping motion is almost always a bug; if you need composition, use two layers or a `group`.
58
+
59
+ ## `resolveFrame` is the whole engine in one function
60
+
61
+ ```ts
62
+ const scene = resolveFrame(doc, stateName, frameNumber);
63
+ // scene is { layers: ResolvedLayer[] } — every property concrete, no deltas
64
+ ```
65
+
66
+ What it does, in order:
67
+
68
+ 1. Look up the state by name. If it doesn't exist, throw.
69
+ 2. For each layer in `doc.layers`, start with its authored values.
70
+ 3. For each delta in `state.deltas`:
71
+ - If `frameNumber < startFrame`, the layer's property stays at `from`.
72
+ - If `frameNumber > endFrame`, the layer's property stays at `to`.
73
+ - If between: compute `t = (frameNumber - startFrame) / (endFrame - startFrame)`, run `t` through the easing function (returning `t'`), then interpolate `from + (to - from) * t'`.
74
+ 4. Apply parent transforms (if a layer has `parentId`), motion paths, clip paths, tints.
75
+ 5. Return the resolved scene — every layer now has concrete property values ready for the renderer.
76
+
77
+ The renderer (canvas, SVG export, Lottie export, video exporter) all consume the same `ResolvedLayer[]` output. Adding a new renderer is "consume one frame at a time"; adding a new animatable property is "add to the interpolation switch."
78
+
79
+ ## Hierarchical states and transitions
80
+
81
+ States can declare a `parent` (extending another state's deltas) and a `transitions` block (named transitions from one state to another with their own duration, easing, and target deltas). This is the foundation of interactive UI motion — `hover` extends `default` with extra deltas, `pressed` transitions from `hover` with a 200ms ease-out. Full coverage in ATEL-401 (State Machines & Motion).
82
+
83
+ ## Inspecting a frame from the CLI
84
+
85
+ ```bash
86
+ atelier preview hello.atelier --frame 15
87
+ atelier preview hello.atelier --frame 15 --state hover
88
+ ```
89
+
90
+ The output is the same `ResolvedLayer[]` array `resolveFrame` produces — perfect for debugging "what does the engine actually think this scene looks like at frame N?"
91
+
92
+ For multi-frame inspection, `atelier_batch_preview` (MCP) or `atelier diff_frames` (CLI) compare two frames side by side.
93
+
94
+ Next: easings.
@@ -0,0 +1,72 @@
1
+ ---
2
+ id: N-atel-101-the-atelier-format
3
+ title: The `.atelier` document format
4
+ type: note
5
+ track: ATEL-101
6
+ author: atelier
7
+ created: '2026-05-18'
8
+ updated: '2026-05-18'
9
+ tags:
10
+ - course
11
+ - atel-101
12
+ - format
13
+ - schema
14
+ difficulty: beginner
15
+ estimatedMinutes: 4
16
+ prerequisites:
17
+ - N-atel-001-first-render
18
+ summary: The top-level shape of an `.atelier` YAML document — canvas, layers, states, assets, variables, presets, templates. Every field exists for one reason, named for what it means.
19
+ ---
20
+
21
+ ## The five required parts
22
+
23
+ Every `.atelier` document is YAML with five mandatory top-level keys:
24
+
25
+ ```yaml
26
+ name: My Animation # human-readable label
27
+ canvas: # global render context
28
+ width: 800
29
+ height: 600
30
+ fps: 60
31
+ background: "#0F1115" # optional, defaults to transparent
32
+ layers: [ ... ] # ordered array of visual elements
33
+ states: # named keyframes (always at least one)
34
+ default:
35
+ duration: 60 # in frames
36
+ deltas: [ ... ]
37
+ ```
38
+
39
+ That is the minimum. Validators accept it. The renderer can resolve any frame in the `default` state.
40
+
41
+ ## The optional parts
42
+
43
+ Any of these can be absent. Add them when you need them.
44
+
45
+ | Key | What it holds | When you need it |
46
+ |---|---|---|
47
+ | `assets` | External resources (images, videos, audio) referenced by `assetId` from layers | The moment you add an image or video layer |
48
+ | `variables` | Named values referenced by `${name}` in templates and expression-driven deltas | When you want to parametrize a document or template |
49
+ | `presets` | Reusable delta bundles applied by name | When the same animation pattern repeats across layers |
50
+ | `templates` | Reusable layer subtrees instantiated by name with variable substitution | Carousels, lower-thirds, repeating motifs |
51
+ | `audio` | Audio track + sync metadata | Video projects with sound |
52
+ | `meta` | Free-form metadata (author, tags, source notes) | Anything you want a downstream tool to read |
53
+
54
+ ## The order of keys is not meaningful (to the engine)
55
+
56
+ YAML is unordered for objects. The schema doesn't care whether `states` comes before `layers`. Conventionally we write them top-down — `name`, `canvas`, `assets`, `variables`, `presets`, `templates`, `layers`, `states`, `audio`, `meta` — because that order roughly matches "global setup → reusable bits → scene → motion → output."
57
+
58
+ ## The order of `layers` IS meaningful
59
+
60
+ Layers render back-to-front. Index 0 is drawn first (background); the last layer is on top. This is also the order the layer panel shows them in the studio (top of the list = top of the stack, matching most design-tool conventions — the array is rendered bottom-up for display).
61
+
62
+ ## Layer IDs and references
63
+
64
+ Every layer needs an `id` that is unique within the document. Deltas reference layers by id (`layer: title`). Parent-child relationships use `parentId`. Templates reference instance ids. The schema enforces uniqueness and rejects dangling references at validate time — you don't have to track them by hand.
65
+
66
+ Conventionally, layer ids are kebab-case (`title`, `subtitle`, `clip-trim-1`, `caption-word-7`). Pipeline-generated layers carry a tag namespace prefix in the id: `silence-trim-N`, `caption-N`, `caption-word-N`, `overlay-N`. The tags drive the layer-tag isolation invariant (ATEL-501).
67
+
68
+ ## Reading order for the rest of ATEL-101
69
+
70
+ 1. **Layers** — anatomy of a single layer: visual, frame, bounds, anchor, opacity, rotation, scale, parent, visible, motion path, clip path, blend mode, shadow, tint, interactions
71
+ 2. **States and deltas** — what a state is, what a delta is, how `resolveFrame(doc, stateName, frame)` produces a renderable scene
72
+ 3. **Easings** — linear / cubic-bezier / ease presets / spring / step; the math you don't have to remember but should recognize
@@ -0,0 +1,141 @@
1
+ ---
2
+ id: N-atel-201-authoring-tools
3
+ title: Authoring tools — document, layer, state, delta
4
+ type: note
5
+ track: ATEL-201
6
+ author: atelier
7
+ created: '2026-05-18'
8
+ updated: '2026-05-18'
9
+ tags:
10
+ - course
11
+ - atel-201
12
+ - mcp
13
+ - authoring
14
+ difficulty: intermediate
15
+ estimatedMinutes: 6
16
+ prerequisites:
17
+ - N-atel-201-mcp-overview
18
+ summary: The tools you reach for to build animations. Document lifecycle, layer composition, state machines, delta authoring. Annotated by which to call when.
19
+ ---
20
+
21
+ ## Document lifecycle
22
+
23
+ ```
24
+ atelier_create — make a new document with name + canvas
25
+ atelier_load — load a .atelier file from disk by path
26
+ atelier_list — list .atelier files in CWD
27
+ atelier_info — return name + canvas + layer count + state count
28
+ atelier_validate — schema-validate the current doc; returns errors with paths
29
+ atelier_preview — resolve one frame and return ResolvedLayer[]
30
+ atelier_batch_preview — resolve multiple frames in one call (for thumbnail rows)
31
+ atelier_diff_frames — compare two resolved frames; return property-level diffs
32
+ atelier_profile — measure resolveFrame latency per state
33
+ atelier_complexity — count layers, deltas, refs, max nesting; flag perf risks
34
+ ```
35
+
36
+ **When to use which:**
37
+
38
+ - Starting from scratch: `atelier_create({ name, canvas: { width, height, fps }})`. Returns a minimal doc with one empty `default` state.
39
+ - Resuming on an existing file: `atelier_load({ path })`. The store now holds that doc; subsequent calls operate on it.
40
+ - Sanity-checking before you ship: `atelier_validate` then `atelier_complexity` — the first catches schema issues, the second flags renderer perf risks (1000+ layers, 10+ levels of group nesting, etc.).
41
+ - Visual debugging: `atelier_preview({ frame: 30 })` returns the same `ResolvedLayer[]` the renderer consumes. Compare against `atelier_diff_frames({ from: 29, to: 31 })` to find what changes.
42
+
43
+ ## Layer composition
44
+
45
+ ```
46
+ atelier_add_layer — add a layer with visual + frame + bounds
47
+ atelier_edit_layer — edit non-visual layer properties (id, tags, parentId, etc.)
48
+ atelier_remove_layer — remove a layer by id (and any deltas referencing it)
49
+ atelier_reorder — change layer z-order
50
+ atelier_list_layers — list all layer ids + types
51
+ atelier_edit_visual — replace or patch the visual block (shape→text, fill, etc.)
52
+ ```
53
+
54
+ **Pattern:** build all layers first, then animate. Adding layers triggers immediate validation; adding deltas requires the target layer to exist. Reverse order works but emits more errors.
55
+
56
+ **`atelier_edit_visual` is the underrated one.** It's the single tool for "I want this rect to become a text layer" or "change this image's `assetId`." Most edit-flow agents use this far more than the dedicated `atelier_set_*` family.
57
+
58
+ ## State machines
59
+
60
+ ```
61
+ atelier_add_state — name a new state with duration (frames)
62
+ atelier_edit_state — change duration, parent state, transitions
63
+ atelier_remove_state — remove a state (rejects if it's the only one)
64
+ atelier_list_states — list state names + durations + parent chain
65
+ atelier_set_state_parent — make a state inherit deltas from another
66
+ atelier_configure_transition — define transition from state A→B with easing
67
+ ```
68
+
69
+ **State machines are the foundation of interactive motion.** A `default` state shows the resting layout. `hover` extends `default` (via `set_state_parent`) with extra deltas. `pressed` transitions from `hover` with a 200ms ease-out (via `configure_transition`). Three states, total complexity stays low, the interactive feel is right.
70
+
71
+ For non-interactive animations: one `default` state is fine. State-machine complexity is opt-in.
72
+
73
+ ## Delta authoring
74
+
75
+ ```
76
+ atelier_add_delta — animate one property of one layer
77
+ atelier_edit_delta — change from/to/easing/range of an existing delta
78
+ atelier_remove_delta — remove a delta
79
+ ```
80
+
81
+ `atelier_add_delta` takes:
82
+
83
+ ```ts
84
+ {
85
+ state: "default",
86
+ layer: "title",
87
+ property: "opacity", // or "frame.x", "style.color", "scale.x", ...
88
+ from: 0,
89
+ to: 1,
90
+ startFrame: 0,
91
+ endFrame: 30,
92
+ easing: "ease-out" // or cubic-bezier(...), spring({...}), step(...)
93
+ }
94
+ ```
95
+
96
+ The **no-overlap gate** rejects two deltas on the same `layer` + `property` whose `[startFrame, endFrame]` ranges overlap. If you need composed motion (e.g. wobble overlaid on a slide), use two layers or a `group`. The error message names both deltas; the fix is obvious.
97
+
98
+ **Easing as an object, not just a string.** The string form (`"ease-out"`) is the preset shortcut; the object form (`{ type: "spring", stiffness: 170, damping: 26 }`) is the full surface. Use the object form when you want spring physics or a custom cubic-bezier with specific control points.
99
+
100
+ ## A complete worked example
101
+
102
+ Building "fade in title, slide subtitle from below, hold for 2 seconds" from zero:
103
+
104
+ ```ts
105
+ const { documentId } = await atelier_create({
106
+ name: "Intro", canvas: { width: 1920, height: 1080, fps: 60 }
107
+ });
108
+
109
+ await atelier_add_layer({ documentId, layer: {
110
+ id: "title", visual: { type: "text", content: "Atelier",
111
+ style: { fontFamily: "Inter", fontSize: 120, color: "#F5F5F7" }},
112
+ frame: { x: 960, y: 480 }, bounds: { width: 1200, height: 160 },
113
+ anchorPoint: { x: 0.5, y: 0.5 }, opacity: 0
114
+ }});
115
+
116
+ await atelier_add_layer({ documentId, layer: {
117
+ id: "subtitle", visual: { type: "text", content: "AI-native animation",
118
+ style: { fontFamily: "Inter", fontSize: 40, color: "#9CA3AF" }},
119
+ frame: { x: 960, y: 580 }, bounds: { width: 1200, height: 60 },
120
+ anchorPoint: { x: 0.5, y: 0.5 }, opacity: 0
121
+ }});
122
+
123
+ await atelier_edit_state({ documentId, state: "default", duration: 180 });
124
+
125
+ await atelier_add_delta({ documentId, state: "default", layer: "title",
126
+ property: "opacity", from: 0, to: 1, startFrame: 0, endFrame: 30,
127
+ easing: "ease-out" });
128
+
129
+ await atelier_add_delta({ documentId, state: "default", layer: "subtitle",
130
+ property: "opacity", from: 0, to: 1, startFrame: 20, endFrame: 50,
131
+ easing: "ease-out" });
132
+
133
+ await atelier_add_delta({ documentId, state: "default", layer: "subtitle",
134
+ property: "frame.y", from: 680, to: 580, startFrame: 20, endFrame: 50,
135
+ easing: { type: "spring", stiffness: 120, damping: 22 } });
136
+
137
+ await atelier_validate({ documentId });
138
+ await atelier_preview({ documentId, frame: 40 }); // inspect mid-motion
139
+ ```
140
+
141
+ Eight calls. Two layers, three deltas, one validate, one preview. The agent never touched YAML.
@@ -0,0 +1,86 @@
1
+ ---
2
+ id: N-atel-201-mcp-overview
3
+ title: MCP in the Atelier context
4
+ type: note
5
+ track: ATEL-201
6
+ author: atelier
7
+ created: '2026-05-18'
8
+ updated: '2026-05-18'
9
+ tags:
10
+ - course
11
+ - atel-201
12
+ - mcp
13
+ - architecture
14
+ difficulty: intermediate
15
+ estimatedMinutes: 4
16
+ prerequisites:
17
+ - N-atel-101-easings
18
+ summary: What the `atelier-mcp` server actually is, why every authoring tool maps to a single mutation on a single document store, and how the LLM↔Studio live-mutation bridge works without conflict.
19
+ ---
20
+
21
+ ## What `atelier-mcp` is
22
+
23
+ `atelier-mcp` is a Model Context Protocol server. An AI agent connects to it (via stdio in Claude Desktop, or WebSocket when paired with `atelier studio`) and gains 61 tools across 15 groups. Each tool is a typed function the agent calls; each call is a single mutation against a shared in-memory `DocumentStore`. The server doesn't talk to the LLM; it just exposes the mutation surface and lets the LLM drive.
24
+
25
+ The boundary is intentional: the agent does the planning, the server does the validating. If the agent asks `atelier_add_delta` to animate a property that doesn't exist on a layer, the Zod schema rejects the call with an AI-readable error message. The agent reads the error, fixes the request, retries. No invalid document ever reaches the renderer.
26
+
27
+ ## The single-store invariant
28
+
29
+ There is **one** `DocumentStore` per project session. Every client — the MCP server's tool handlers, the `atelier studio` browser canvas, the WebSocket bridge between them — reads and writes the same store.
30
+
31
+ This is why the live LLM-mutation feature works at all. When an agent calls `atelier_add_layer`, the store updates, fires `onChange`, and the bridge broadcasts the mutation to any connected Studio. The Studio applies the mutation via `applyMutation(op)` (with a suppress-notify flag for one tick so the change doesn't echo back as a human edit), re-renders the canvas, and shows a toast: *"agent added layer `title`"* with an Undo affordance.
32
+
33
+ Conflict policy in v1: **last-write-wins**. Human edits during agent mutation overwrite the agent's pending change. No CRDT, no operational transform — Atelier is a single-author tool with an AI collaborator, not a real-time multiplayer canvas. Every op is tagged `source: "human" | "llm"` so the UI can attribute changes.
34
+
35
+ ## Tool conventions
36
+
37
+ Every MCP tool follows the same conventions. Recognize the pattern once and you can read any tool's signature:
38
+
39
+ | Convention | Example |
40
+ |---|---|
41
+ | `atelier_<verb>_<noun>` naming | `atelier_add_layer`, `atelier_edit_delta`, `atelier_remove_state` |
42
+ | First argument is always `documentId` (or implicit if the server has a current doc) | `atelier_add_layer({ documentId, ... })` |
43
+ | Mutations return the **updated document** (not just the changed fragment) | An agent doesn't have to re-fetch after every call |
44
+ | Inspect tools return only what was asked for | `atelier_preview` returns resolved layers, not the whole doc |
45
+ | Validation runs after every mutation; tool calls fail fast with field-level errors | Errors include the offending path: `layers[0].visual.style.fontSize: expected number` |
46
+ | Tools that need a frame number accept either an absolute frame or `{ state, frame }` | Clarity over cleverness |
47
+
48
+ ## The 15 groups
49
+
50
+ ```
51
+ document — create, load, list, info, validate, preview, batch_preview,
52
+ profile, complexity, diff_frames, export, export_svg, export_lottie
53
+ layer — add, edit, remove, reorder, list, edit_visual
54
+ state — add, edit, remove, list, set_parent, configure_transition
55
+ delta — add, edit, remove
56
+ visual — set_shape, set_fill, set_stroke, set_shadow, set_blend_mode,
57
+ set_clip_path, set_motion_path, set_tint
58
+ asset — add, list, remove
59
+ variable — add, list, remove, find
60
+ preset — define, list, apply
61
+ template — instantiate
62
+ interaction — add, list, remove
63
+ ref — set_ref, resolve_refs
64
+ audio — set_audio
65
+ overlay — add_handle, add_page_number (Phase 1.5)
66
+ recipe — recipe_list, recipe_get, recipe_validate, recipe_save, recipe_apply
67
+ import — import_images
68
+ ```
69
+
70
+ The next three notes cover the high-frequency tools — what to call and when. The full reference is `docs/mcp-reference.md` in the atelier repo (and at `https://atelier.dev/mcp`, when that lands).
71
+
72
+ ## Configuring the server
73
+
74
+ ```json
75
+ {
76
+ "mcpServers": {
77
+ "atelier": {
78
+ "command": "atelier-mcp"
79
+ }
80
+ }
81
+ }
82
+ ```
83
+
84
+ That's it. No auth tokens. No project path — the server scans CWD for `.atelier` files and loads them lazily on `atelier_load`. To bind the server to a specific project directory, set `ATELIER_PROJECT_ROOT=/path/to/project` in the env.
85
+
86
+ For the live Studio bridge, run `atelier studio` first; the MCP server detects the bridge's WebSocket endpoint and connects automatically. No config needed.