@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.
- package/dist/{chunk-JPZ4F4PW.js → chunk-3ARBOSWY.js} +64 -5
- package/dist/chunk-3ARBOSWY.js.map +1 -0
- package/dist/cli.js +11469 -413
- package/dist/cli.js.map +1 -1
- package/dist/{dist-M67UZGFQ.js → dist-3YQK6PI6.js} +2 -2
- package/dist/index.cjs +3193 -227
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +701 -8
- package/dist/index.d.ts +701 -8
- package/dist/index.js +7237 -72
- package/dist/index.js.map +1 -1
- package/dist/mcp.js +2898 -507
- package/dist/mcp.js.map +1 -1
- package/package.json +14 -9
- package/src/web/inline-app.ts +55 -4
- package/src/web/timeline-state-types.ts +28 -0
- package/src/web/timeline-view.test.ts +99 -0
- package/src/web/timeline-view.ts +339 -0
- package/src/web/workspace-app.ts +3146 -0
- package/templates/workspace/.claude/agents/atelier-iris.md +75 -0
- package/templates/workspace/.claude/agents/atelier-lux.md +67 -0
- package/templates/workspace/.claude/agents/atelier-quill.md +61 -0
- package/templates/workspace/.gitignore +30 -0
- package/templates/workspace/.paradigm/personas/_shared/cascade-merge.md +172 -0
- package/templates/workspace/CLAUDE.md +93 -0
- package/templates/workspace/README.md +75 -0
- package/templates/workspace/SETUP.md +127 -0
- package/templates/workspace/_brand/.atelier-brand.yaml +34 -0
- package/templates/workspace/_brand/DESIGN.md +56 -0
- package/templates/workspace/_brand/SCRIPT.md +41 -0
- package/templates/workspace/_brand/STORYBOARD.md +33 -0
- package/templates/workspace/_packs/README.md +54 -0
- package/templates/workspace/projects/README.md +49 -0
- package/templates/workspace/workspace.atelier +22 -0
- package/university/content/notes/N-atel-001-first-render.md +114 -0
- package/university/content/notes/N-atel-001-install-and-launch.md +84 -0
- package/university/content/notes/N-atel-001-what-is-atelier.md +51 -0
- package/university/content/notes/N-atel-101-easings.md +97 -0
- package/university/content/notes/N-atel-101-layers.md +106 -0
- package/university/content/notes/N-atel-101-states-and-deltas.md +94 -0
- package/university/content/notes/N-atel-101-the-atelier-format.md +72 -0
- package/university/content/notes/N-atel-201-authoring-tools.md +141 -0
- package/university/content/notes/N-atel-201-mcp-overview.md +86 -0
- package/university/content/notes/N-atel-201-patterns.md +108 -0
- package/university/content/notes/N-atel-201-visual-and-effects.md +125 -0
- package/university/content/notes/N-atel-301-composition-and-overlays.md +141 -0
- package/university/content/notes/N-atel-301-effects.md +136 -0
- package/university/content/notes/N-atel-301-images-and-video.md +126 -0
- package/university/content/notes/N-atel-301-shapes-and-text.md +118 -0
- package/university/content/notes/N-atel-401-hierarchical-states.md +71 -0
- package/university/content/notes/N-atel-401-motion-deep-dive.md +106 -0
- package/university/content/notes/N-atel-401-presets-and-templates.md +98 -0
- package/university/content/notes/N-atel-401-transitions.md +94 -0
- package/university/content/notes/N-atel-501-detected-vs-user-edited.md +76 -0
- package/university/content/notes/N-atel-501-layer-tag-isolation.md +62 -0
- package/university/content/notes/N-atel-501-silence-trim.md +98 -0
- package/university/content/notes/N-atel-501-transcribe-and-captions.md +98 -0
- package/university/content/notes/N-atel-601-carousel.md +71 -0
- package/university/content/notes/N-atel-601-overlay-rules.md +96 -0
- package/university/content/notes/N-atel-601-recipe-tools-and-apply.md +84 -0
- package/university/content/notes/N-atel-601-studio-recipe.md +103 -0
- package/university/content/notes/N-atel-701-choosing-output.md +68 -0
- package/university/content/notes/N-atel-701-png-and-frames.md +84 -0
- package/university/content/notes/N-atel-701-vector.md +85 -0
- package/university/content/notes/N-atel-701-video.md +88 -0
- package/university/content/notes/N-atel-801-editing-surface.md +69 -0
- package/university/content/notes/N-atel-801-live-bridge.md +84 -0
- package/university/content/notes/N-atel-801-studio-app.md +72 -0
- package/university/content/notes/N-atel-801-symbiotic-loop.md +56 -0
- package/university/content/paths/LP-atel-001.yaml +21 -0
- package/university/content/paths/LP-atel-101.yaml +22 -0
- package/university/content/paths/LP-atel-201.yaml +23 -0
- package/university/content/paths/LP-atel-301.yaml +22 -0
- package/university/content/paths/LP-atel-401.yaml +22 -0
- package/university/content/paths/LP-atel-501.yaml +22 -0
- package/university/content/paths/LP-atel-601.yaml +22 -0
- package/university/content/paths/LP-atel-701.yaml +22 -0
- package/university/content/paths/LP-atel-801.yaml +22 -0
- package/university/content/quizzes/Q-atel-001-orientation.yaml +66 -0
- package/university/content/quizzes/Q-atel-101-document-model.yaml +66 -0
- package/university/content/quizzes/Q-atel-201-mcp-authoring.yaml +66 -0
- package/university/content/quizzes/Q-atel-301-visual-system.yaml +66 -0
- package/university/content/quizzes/Q-atel-401-state-machines.yaml +66 -0
- package/university/content/quizzes/Q-atel-501-video-pipeline.yaml +66 -0
- package/university/content/quizzes/Q-atel-601-recipes.yaml +66 -0
- package/university/content/quizzes/Q-atel-701-export.yaml +66 -0
- package/university/content/quizzes/Q-atel-801-studio-loop.yaml +66 -0
- package/university/index.yaml +720 -0
- package/university/pack.yaml +21 -0
- package/dist/chunk-5QQESXI6.js +0 -4432
- package/dist/chunk-5QQESXI6.js.map +0 -1
- package/dist/chunk-JPZ4F4PW.js.map +0 -1
- package/dist/cli.cjs +0 -6313
- package/dist/cli.cjs.map +0 -1
- package/dist/cli.d.cts +0 -1
- package/dist/cli.d.ts +0 -1
- package/dist/mcp.cjs +0 -5462
- package/dist/mcp.cjs.map +0 -1
- /package/dist/{dist-M67UZGFQ.js.map → dist-3YQK6PI6.js.map} +0 -0
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: N-atel-701-png-and-frames
|
|
3
|
+
title: PNG and frames — the raster export path
|
|
4
|
+
type: note
|
|
5
|
+
track: ATEL-701
|
|
6
|
+
author: atelier
|
|
7
|
+
created: '2026-05-20'
|
|
8
|
+
updated: '2026-05-20'
|
|
9
|
+
tags:
|
|
10
|
+
- course
|
|
11
|
+
- atel-701
|
|
12
|
+
- png
|
|
13
|
+
- raster
|
|
14
|
+
difficulty: intermediate
|
|
15
|
+
estimatedMinutes: 6
|
|
16
|
+
prerequisites:
|
|
17
|
+
- N-atel-101-the-atelier-format
|
|
18
|
+
summary: Single-frame raster output via `atelier export-image` and `atelier still`. The @napi-rs/canvas rasterizer ships prebuilt platform binaries — PNG export works on a plain `npm install`, no brew, no cairo, no node-gyp build.
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## One frame, one PNG
|
|
22
|
+
|
|
23
|
+
A whole animation is a sequence of resolved frames. To pull a single one out as an image:
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
atelier export-image hello.atelier --out frame.png
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
That renders the document's first state at frame `0` to `frame.png`. To pick a different moment, name the state and the frame:
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
atelier export-image hello.atelier --out mid-fade.png --frame 15 --state intro
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
The flags are exactly:
|
|
36
|
+
|
|
37
|
+
- `-o, --out <path>` — output PNG path (required)
|
|
38
|
+
- `-s, --state <name>` — state name (defaults to the first state)
|
|
39
|
+
- `-f, --frame <number>` — frame within the state (defaults to `0`)
|
|
40
|
+
- `--width <number>` / `--height <number>` — output dimension overrides
|
|
41
|
+
|
|
42
|
+
Pick a frame past the end of the state's `duration` and the command refuses it with `Frame N is out of range for state "X" (duration D)`. The renderer validates the selection before it touches a canvas.
|
|
43
|
+
|
|
44
|
+
## Resizing on export
|
|
45
|
+
|
|
46
|
+
Override one dimension and the other is derived to preserve the document's aspect ratio:
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
atelier export-image hero.atelier --out thumb.png --width 400
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
A 1920×1080 document exported with `--width 400` comes out 400×225. Override *both* and the renderer uses both verbatim — which can squash the image if the ratio doesn't match. That's deliberate: sometimes you genuinely want a non-uniform target size. Most of the time, set one and let the aspect ratio carry the other.
|
|
53
|
+
|
|
54
|
+
## `still` — the same frame, two formats
|
|
55
|
+
|
|
56
|
+
`atelier still` resolves a single frame too, but it can emit either the resolved-frame JSON or a PNG:
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
atelier still hello.atelier --frame 15 # JSON to stdout
|
|
60
|
+
atelier still hello.atelier --frame 15 --format png -o f.png
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
- `--format json` (default) prints the `resolveFrame` output — every layer's interpolated position, opacity, scale, and resolved visual at that frame. This is the inspection tool: see exactly what the renderer sees before it draws.
|
|
64
|
+
- `--format png` rasterizes that resolved frame. With no `-o, --output`, the PNG bytes go to stdout (pipe it somewhere); with `-o`, it writes a file.
|
|
65
|
+
|
|
66
|
+
So `export-image` is the dedicated "give me a PNG" command with resize flags; `still` is the "show me this frame" command that happens to also rasterize. When you want a sized image file, reach for `export-image`. When you're debugging interpolation, reach for `still --format json`.
|
|
67
|
+
|
|
68
|
+
## The rasterizer: @napi-rs/canvas
|
|
69
|
+
|
|
70
|
+
Both raster paths go through the same backend — `@napi-rs/canvas`, a Canvas2D implementation shipped as **prebuilt platform binaries**. This matters more than it sounds:
|
|
71
|
+
|
|
72
|
+
- No `node-gyp` compile step.
|
|
73
|
+
- No system libraries — no `brew install cairo pango libpng`, no `apt install libcairo2-dev`.
|
|
74
|
+
- It's a hard dependency, so PNG export works the moment `npm install` finishes, on any platform with a prebuilt binary.
|
|
75
|
+
|
|
76
|
+
The renderer pre-loads every `ImageVisual` source first (file path, `data:` URL, or buffer all work server-side), wires them into an `ImageCache`, then renders one resolved frame scaled to the requested output dimensions. If the binary somehow fails to load — a corrupt install, an unsupported platform — you get a typed `CanvasUnavailableError` telling you to reinstall, not a cryptic native crash.
|
|
77
|
+
|
|
78
|
+
> Hold onto this distinction: the **single-frame raster** path (`export-image`, `still --format png`, and `carousel`) needs nothing beyond `npm install`. The **video** path (`atelier render`, covered in N-atel-701-video) is a different backend with different requirements. PNG-clean install does not imply MP4-clean install.
|
|
79
|
+
|
|
80
|
+
## When raster is the right call
|
|
81
|
+
|
|
82
|
+
A PNG is the right output when the destination is a still image: a thumbnail, an Open Graph card, a poster frame, a single social image, a print proof. It's the wrong output for anything that needs to scale crisply at arbitrary sizes (a logo destined for a stylesheet) — that's the vector path in N-atel-701-vector — or anything that moves (the video path in N-atel-701-video).
|
|
83
|
+
|
|
84
|
+
For batch raster delivery — a whole folder of images composed into a uniform set of posts — don't loop `export-image` by hand. ATEL-601's `atelier carousel` drives the same `renderDocumentToPng` path across many inputs with recipe overlays applied. See that track; this note stays on the single-frame primitives.
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: N-atel-701-vector
|
|
3
|
+
title: Vector export — SVG per frame, Lottie per document
|
|
4
|
+
type: note
|
|
5
|
+
track: ATEL-701
|
|
6
|
+
author: atelier
|
|
7
|
+
created: '2026-05-20'
|
|
8
|
+
updated: '2026-05-20'
|
|
9
|
+
tags:
|
|
10
|
+
- course
|
|
11
|
+
- atel-701
|
|
12
|
+
- svg
|
|
13
|
+
- lottie
|
|
14
|
+
- vector
|
|
15
|
+
difficulty: intermediate
|
|
16
|
+
estimatedMinutes: 6
|
|
17
|
+
prerequisites:
|
|
18
|
+
- N-atel-101-the-atelier-format
|
|
19
|
+
summary: '`atelier export-svg` emits one frame as a resolution-independent SVG string; `atelier export-lottie` emits the whole animated document as Lottie JSON. When to choose vector over raster — web embeds and motion-design handoff.'
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
## Two vector exporters, two scopes
|
|
23
|
+
|
|
24
|
+
Raster gives you pixels at a fixed size. Vector gives you shapes that stay crisp at any size. Atelier ships two vector exporters, and the scope difference is the thing to internalize:
|
|
25
|
+
|
|
26
|
+
- `atelier export-svg` exports **one frame** as an SVG document.
|
|
27
|
+
- `atelier export-lottie` exports the **whole animated document** as Lottie JSON.
|
|
28
|
+
|
|
29
|
+
One is a still snapshot in vector form; the other is the animation itself, handed to a runtime that plays it.
|
|
30
|
+
|
|
31
|
+
## `export-svg` — a frame as SVG
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
atelier export-svg lockup.atelier --frame 0 --out logo.svg
|
|
35
|
+
atelier export-svg lockup.atelier --state hero --frame 12 # to stdout
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Flags:
|
|
39
|
+
|
|
40
|
+
- `-s, --state <name>` — state name (defaults to the first state)
|
|
41
|
+
- `-f, --frame <number>` — frame within the state (defaults to `0`)
|
|
42
|
+
- `-o, --output <path>` — output path (defaults to stdout)
|
|
43
|
+
- `--xml-declaration` — prepend the `<?xml ...?>` declaration
|
|
44
|
+
|
|
45
|
+
Like the raster path, it resolves a single frame — but instead of rasterizing it, it serializes the resolved layers as SVG elements. Shapes become `<path>` / `<rect>` / `<ellipse>`, text becomes `<text>`, fills and strokes and gradients map to SVG paint. The output scales to any size with no quality loss, which is the whole point: a logo lock-up exported as SVG drops straight into a stylesheet or a React component and renders sharp on a retina display and a billboard alike.
|
|
46
|
+
|
|
47
|
+
Use `--xml-declaration` when the SVG is a standalone file consumed by tooling that wants the prolog; omit it when you're inlining the markup into HTML (where the declaration is noise).
|
|
48
|
+
|
|
49
|
+
## `export-lottie` — the document as Lottie
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
atelier export-lottie intro.atelier --out intro.json
|
|
53
|
+
atelier export-lottie intro.atelier --state hero -o hero.json
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
Flags:
|
|
57
|
+
|
|
58
|
+
- `-s, --state <name>` — state name (defaults to the first state)
|
|
59
|
+
- `-o, --output <path>` — output path (defaults to stdout)
|
|
60
|
+
|
|
61
|
+
Notice what's **missing**: there's no `--frame`. Lottie is an animation format, so the exporter takes the whole timeline, not a single moment. It walks the document's deltas and writes them as Lottie keyframes — opacity tracks, transform tracks, the lot. The result is a JSON file a Lottie player (lottie-web, the iOS/Android runtimes, After Effects via Bodymovin) plays back as vector animation.
|
|
62
|
+
|
|
63
|
+
The exporter is honest about its limits. Anything it can't faithfully translate — an effect with no Lottie equivalent, a visual type outside Lottie's model — comes back as a **warning on stderr**, not a silent omission:
|
|
64
|
+
|
|
65
|
+
```
|
|
66
|
+
Warning: <unsupported feature> — skipped in Lottie output
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
Read those warnings. A clean export means the Lottie matches the document; a warning means a feature dropped on the way out and you should check the result in a player before shipping it.
|
|
70
|
+
|
|
71
|
+
## Vector vs raster — choosing
|
|
72
|
+
|
|
73
|
+
Reach for **vector** when:
|
|
74
|
+
|
|
75
|
+
- **The output embeds in the web and must scale.** An SVG logo or icon stays crisp at every density and size. No `@2x`/`@3x` asset matrix.
|
|
76
|
+
- **You're handing off to motion design.** Lottie is the lingua franca between Atelier and design/runtime teams — tiny files, vector-crisp, playable in every major runtime. Export the document as Lottie and a front-end engineer drops it into a screen with three lines of code.
|
|
77
|
+
- **File size and scalability beat pixel fidelity.** Vector files are small and resolution-independent.
|
|
78
|
+
|
|
79
|
+
Reach for **raster** (N-atel-701-png-and-frames) when:
|
|
80
|
+
|
|
81
|
+
- The artwork is photographic or has effects vector can't express cleanly (complex blurs, blends, image content).
|
|
82
|
+
- The destination wants a fixed-size pixel image (an OG card, a thumbnail).
|
|
83
|
+
- You need a single still and the consumer expects a PNG.
|
|
84
|
+
|
|
85
|
+
The rule of thumb: **SVG for a still that must scale, Lottie for motion handed to a player, raster for everything photographic or pixel-final.** When a Lottie export warns, treat that as a signal that the document leans on something raster (or video) would carry better.
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: N-atel-701-video
|
|
3
|
+
title: Video export — MP4 and GIF via FFmpeg, WebM in the studio
|
|
4
|
+
type: note
|
|
5
|
+
track: ATEL-701
|
|
6
|
+
author: atelier
|
|
7
|
+
created: '2026-05-20'
|
|
8
|
+
updated: '2026-05-20'
|
|
9
|
+
tags:
|
|
10
|
+
- course
|
|
11
|
+
- atel-701
|
|
12
|
+
- video
|
|
13
|
+
- ffmpeg
|
|
14
|
+
- webcodecs
|
|
15
|
+
difficulty: intermediate
|
|
16
|
+
estimatedMinutes: 7
|
|
17
|
+
prerequisites:
|
|
18
|
+
- N-atel-101-the-atelier-format
|
|
19
|
+
summary: '`atelier render` writes MP4 or GIF via FFmpeg from the CLI; the browser studio exports via WebCodecs with no FFmpeg dependency. The two paths use different backends with different install requirements — know which one you''re on.'
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
## `atelier render` — the CLI video path
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
atelier render animation.atelier # → animation.mp4
|
|
26
|
+
atelier render animation.atelier -f gif # → animation.gif
|
|
27
|
+
atelier render animation.atelier -o out/clip.mp4 # custom output path
|
|
28
|
+
atelier render animation.atelier -s intro outro # specific states, in order
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Flags:
|
|
32
|
+
|
|
33
|
+
- `-o, --output <path>` — output path (format inferred from the extension if `--format` is absent)
|
|
34
|
+
- `-f, --format <type>` — `mp4` or `gif`
|
|
35
|
+
- `-s, --state <names...>` — one or more states to render, **in order** (defaults to all states, in document order). This is variadic — `-s intro outro` renders intro then outro into one file.
|
|
36
|
+
|
|
37
|
+
With no flags it renders every state in order to `<name>.mp4`. With `-o foo.gif` and no `--format`, it infers GIF from the extension. The CLI renders **MP4 and GIF only** — there is no `webm` here. (WebM lives in the studio path, below.)
|
|
38
|
+
|
|
39
|
+
The render loop resolves each frame, draws it to a canvas, and pipes the raw frames straight into FFmpeg over stdin — MP4 via `libx264` (CRF 18, `+faststart` so it streams), GIF via single-pass palette generation. A live progress line on stderr reports `frame N/total`, percent, current state, and an ETA.
|
|
40
|
+
|
|
41
|
+
## Two requirements the CLI path enforces
|
|
42
|
+
|
|
43
|
+
**FFmpeg must be on `$PATH`.** Before rendering anything, `atelier render` shells out to `ffmpeg -version`. If it's missing you get an install hint and a non-zero exit, not a half-written file:
|
|
44
|
+
|
|
45
|
+
```
|
|
46
|
+
FFmpeg is not installed or not in PATH.
|
|
47
|
+
Install it:
|
|
48
|
+
macOS: brew install ffmpeg
|
|
49
|
+
Ubuntu: sudo apt install ffmpeg
|
|
50
|
+
Windows: https://ffmpeg.org/download.html
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
**H.264 requires even dimensions.** MP4 export refuses an odd-width or odd-height canvas with a message that tells you the next even size to try. GIF has no such constraint.
|
|
54
|
+
|
|
55
|
+
## The backend distinction — read this carefully
|
|
56
|
+
|
|
57
|
+
The single-frame raster path (N-atel-701-png-and-frames) uses `@napi-rs/canvas` — prebuilt binaries, no system libraries, works on a plain `npm install`. **The video path does not.**
|
|
58
|
+
|
|
59
|
+
`atelier render` rasterizes frames with the `canvas` (node-canvas) package, which is a separate, optional native dependency that **does** need a toolchain and system libraries:
|
|
60
|
+
|
|
61
|
+
```
|
|
62
|
+
The 'canvas' package is not installed.
|
|
63
|
+
Install it with:
|
|
64
|
+
npm install canvas
|
|
65
|
+
Prerequisites vary by OS:
|
|
66
|
+
macOS: brew install pkg-config cairo pango libpng jpeg giflib librsvg pixman
|
|
67
|
+
Ubuntu: sudo apt install build-essential libcairo2-dev libpango1.0-dev ...
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
So "PNG export works on a clean install" is true — and it does **not** imply "MP4 export works on a clean install." Video from the CLI asks for two things the raster path never does: FFmpeg on `$PATH` and the `canvas` package with its cairo/pango stack. If you only ever need stills, you never touch any of it.
|
|
71
|
+
|
|
72
|
+
## The studio path — WebCodecs, no FFmpeg
|
|
73
|
+
|
|
74
|
+
The browser studio doesn't shell out to FFmpeg and can't install cairo. Its Export button encodes video with **WebCodecs**, the browser's native video-encode API. That's the headline tradeoff:
|
|
75
|
+
|
|
76
|
+
- **No external dependency.** A user with a modern browser exports video with nothing installed — no FFmpeg, no node-canvas, no toolchain. The encoder is in the browser already.
|
|
77
|
+
- **Different format reach.** WebCodecs handles the codecs the browser ships (commonly MP4 and WebM). FFmpeg, on the CLI, reaches whatever your local FFmpeg build supports.
|
|
78
|
+
- **Different scale point.** The studio path is interactive and per-document — you hit Export and get a file. The CLI path is scriptable and batchable — you wire `atelier render` into a build, a Makefile, a CI job.
|
|
79
|
+
|
|
80
|
+
## Choosing a video target
|
|
81
|
+
|
|
82
|
+
- **Social / web video** → MP4 (H.264). Universal playback, good compression. `atelier render clip.atelier` or the studio Export button.
|
|
83
|
+
- **Short looping animation, chat/README embed** → GIF. Larger files, no audio, but plays everywhere with no `<video>` tag. `atelier render clip.atelier -f gif`.
|
|
84
|
+
- **Web video where size matters and the audience is modern browsers** → WebM, via the studio.
|
|
85
|
+
- **Automated/batch rendering** → the CLI (`atelier render`), so it scripts.
|
|
86
|
+
- **One-off export while editing** → the studio, so you skip the FFmpeg/canvas install entirely.
|
|
87
|
+
|
|
88
|
+
The destination decides the format; the *how* (CLI vs studio) decides the dependencies. N-atel-701-choosing-output ties this together with the still and vector paths into one decision guide.
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: N-atel-801-editing-surface
|
|
3
|
+
title: The editing surface — layers, image drop, and slider commit-gating
|
|
4
|
+
type: note
|
|
5
|
+
track: ATEL-801
|
|
6
|
+
author: atelier
|
|
7
|
+
created: '2026-05-20'
|
|
8
|
+
updated: '2026-05-20'
|
|
9
|
+
tags:
|
|
10
|
+
- course
|
|
11
|
+
- atel-801
|
|
12
|
+
- studio
|
|
13
|
+
- editing
|
|
14
|
+
- undo
|
|
15
|
+
difficulty: advanced
|
|
16
|
+
estimatedMinutes: 5
|
|
17
|
+
prerequisites:
|
|
18
|
+
- N-atel-201-mcp-overview
|
|
19
|
+
summary: The studio's hands-on surface — the Text/Shape/Image/Handle/Page Number layer-type picker, image drag-drop via createImageLayerFromFile (aspect-fit, centered, first drop replaces the `background` placeholder), the canvas/layer/property/yaml panels, and slider commit-gating (onInput live preview vs onChange commit) so one drag is one undo.
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
## The four panels
|
|
23
|
+
|
|
24
|
+
The studio is a two-column layout: the canvas on the left, and a right panel that hosts the **layer panel**, the **property panel**, and the **yaml panel** (the studio opens on the YAML tab by default). The canvas renders the resolved frame; the layer panel lists and reorders layers; the property panel edits the selected layer's properties; the yaml panel shows the live `.atelier` source. Every panel reads from one document — edit in any of them and the others refresh.
|
|
25
|
+
|
|
26
|
+
## The layer-type picker
|
|
27
|
+
|
|
28
|
+
The layer panel's Add menu offers five variants, each a one-glyph icon plus a label:
|
|
29
|
+
|
|
30
|
+
| Variant | Icon | What it adds |
|
|
31
|
+
|---|---|---|
|
|
32
|
+
| `text` | T | a TextVisual layer |
|
|
33
|
+
| `shape` | ■ | a ShapeVisual layer |
|
|
34
|
+
| `image` | ▣ | an ImageVisual layer (opens a file picker) |
|
|
35
|
+
| `handle` | @ | an overlay-tagged handle (`@username`, credit, watermark) |
|
|
36
|
+
| `page-number` | # | an overlay-tagged page-number (`{current}/{total}`) |
|
|
37
|
+
|
|
38
|
+
`Handle` and `Page Number` carry `tags: ["overlay"]` so the layer-tag isolation invariant (ATEL-301, ATEL-501) protects them across recipe re-runs — the studio's Add menu generates the same overlay shape the MCP convenience tools and recipes do. The other three are plain content layers.
|
|
39
|
+
|
|
40
|
+
## Image drag-drop
|
|
41
|
+
|
|
42
|
+
The headline image workflow is drag-and-drop. Drop one or more image files onto the canvas and each becomes an ImageVisual layer. The same path runs whether you drop on the canvas or pick a file via the layer panel's Add → Image menu; both call `createImageLayerFromFile`.
|
|
43
|
+
|
|
44
|
+
The geometry is fixed for you by `fitImageToCanvas`:
|
|
45
|
+
|
|
46
|
+
- **Aspect-fit.** A landscape-relative image fits to canvas width; a portrait-relative image fits to canvas height. Each dimension is capped at the canvas extent, so a square image into a square canvas fills exactly.
|
|
47
|
+
- **Centered.** The frame is `{ x: canvas.width / 2, y: canvas.height / 2 }`, combined with a `0.5 / 0.5` anchor, so the image's centerpoint sits at canvas center.
|
|
48
|
+
|
|
49
|
+
Two placement rules govern where the new layer lands in the z-stack:
|
|
50
|
+
|
|
51
|
+
1. **First-drop-replaces.** When the FIRST image of a drop batch lands and `doc.layers[0]?.id === "background"`, it swaps that placeholder layer **in place**. That's how the welcome scaffold's `background` placeholder gets filled by your first image. `createImageLayerFromFile` is called with `replaceBackground: true` only for the first file in the batch.
|
|
52
|
+
2. **Everything else unshifts.** Subsequent images are `unshift`-ed onto the layers array. The renderer paints index 0 first (bottom of the z-stack), so new images sit behind existing overlays like a handle or page-number — the desired composition order.
|
|
53
|
+
|
|
54
|
+
Each layer also gets a matching `doc.assets` entry registered alongside it, keyed by `${id}-asset`. Non-image files are silently filtered out before the helper runs.
|
|
55
|
+
|
|
56
|
+
## Slider commit-gating — one drag, one undo
|
|
57
|
+
|
|
58
|
+
The property panel's sliders distinguish two events, and the difference is what keeps undo sane:
|
|
59
|
+
|
|
60
|
+
- **`onInput`** (continuous, during a drag) → `onPropertyPreview`: a **cheap canvas re-render only**. No undo snapshot. No host notification. You get live feedback as you drag.
|
|
61
|
+
- **`onChange`** (once, on release) → `onPropertyChange`: the **commit**. Snapshot undo history, then notify the host (which triggers autosave).
|
|
62
|
+
|
|
63
|
+
So dragging a slider produces exactly **one** undo entry, not one per pixel of travel. If a control doesn't distinguish the two events, the preview hook degrades to a full commit, so behavior is never worse than committing on every tick — but the sliders that matter (opacity, transforms) split them.
|
|
64
|
+
|
|
65
|
+
This was the **slider-commit-flood** bug: in the old un-typechecked string form of the client, the commit fired on every `input` event, flooding the undo stack and re-saving on every frame of a drag. Like the autosave save-race, it only became fixable once the client moved into a real, compiler-visible module.
|
|
66
|
+
|
|
67
|
+
## Why this surface exists
|
|
68
|
+
|
|
69
|
+
Everything here is something a human does directly — pick a layer type, drop a photo, nudge a slider. The next note introduces the other editor at the same canvas: an agent, mutating the same document over a WebSocket while you watch.
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: N-atel-801-live-bridge
|
|
3
|
+
title: The live bridge — WebSocket, MCP-over-WS, and source-tagged mutations
|
|
4
|
+
type: note
|
|
5
|
+
track: ATEL-801
|
|
6
|
+
author: atelier
|
|
7
|
+
created: '2026-05-20'
|
|
8
|
+
updated: '2026-05-20'
|
|
9
|
+
tags:
|
|
10
|
+
- course
|
|
11
|
+
- atel-801
|
|
12
|
+
- bridge
|
|
13
|
+
- mcp
|
|
14
|
+
- aspects
|
|
15
|
+
difficulty: advanced
|
|
16
|
+
estimatedMinutes: 6
|
|
17
|
+
prerequisites:
|
|
18
|
+
- N-atel-201-mcp-overview
|
|
19
|
+
summary: How an agent mutates the open studio document in real time — the /bridge WebSocket and /mcp MCP-over-WS transport, DocumentStore.onChange → broadcast, AtelierStudio.applyMutation, and the ~source-tagged-mutation aspect (human|llm|system; only llm broadcasts) that prevents echo loops and false attribution. Plus toast attribution and Cmd-Z undo of an agent edit.
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
## Two endpoints on one server
|
|
23
|
+
|
|
24
|
+
`atelier studio` mounts two WebSocket endpoints on Vite's HTTP server:
|
|
25
|
+
|
|
26
|
+
- **`/mcp`** — an MCP-over-WebSocket transport. An external MCP client (e.g. Claude Desktop running `atelier-mcp`) connects here and drives the same 61 tools it would over stdio. Each connection gets its own `McpServer`, but they all share **one** `DocumentStore`. The transport (`WebSocketServerTransport`) wraps a single `ws` socket as an MCP `Transport`: inbound frames parse to JSON-RPC and surface via `onmessage`; outbound SDK messages stringify and `ws.send`. A malformed inbound frame surfaces via `onerror` but does NOT close the connection.
|
|
27
|
+
- **`/bridge`** — a thin broadcast channel to the browser. It carries typed envelopes (`hello`, `doc:loaded`, `doc:patch`, `llm:mutation`, `error`) at `BRIDGE_PROTOCOL_VERSION = 1`. v1 ships whole-document replacement so the envelope stays trivial; a JSON-patch op shape is v1.1, once the byte cost is measured on real docs.
|
|
28
|
+
|
|
29
|
+
## The Origin asymmetry
|
|
30
|
+
|
|
31
|
+
WS upgrades bypass Vite's connect middleware (Node fires `upgrade` before connect runs), so the bridge re-runs the Origin check itself on every upgrade — otherwise either endpoint would be cross-origin-reachable from any tab. The check is asymmetric:
|
|
32
|
+
|
|
33
|
+
- **`/bridge`** demands a strict same-origin match (`isAllowedOrigin`) — it's a real browser origin, your own studio tab.
|
|
34
|
+
- **`/mcp`** tolerates a **missing** Origin (`isAllowedMcpOrigin`) but still rejects a present-but-foreign one. Non-browser MCP clients send NO Origin header; under the strict check they'd all 403 and MCP-over-WS would be unreachable. A browser tab on evil.com still can't reach `/mcp` (it sends a foreign Origin). The loopback bind to `127.0.0.1` remains the primary protection.
|
|
35
|
+
|
|
36
|
+
## The mutation loop
|
|
37
|
+
|
|
38
|
+
The full agent→human loop:
|
|
39
|
+
|
|
40
|
+
```
|
|
41
|
+
LLM tool call ──► McpServer (per /mcp conn) ──► DocumentStore.set(source:"llm")
|
|
42
|
+
│
|
|
43
|
+
onChange fires (source = "llm")
|
|
44
|
+
▼
|
|
45
|
+
bridge broadcasts `llm:mutation` envelope
|
|
46
|
+
▼
|
|
47
|
+
browser studio.applyMutation({ type:"doc:replace", doc, source })
|
|
48
|
+
▼
|
|
49
|
+
canvas re-renders + toast attribution (Cmd-Z still works)
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
And the human→agent direction:
|
|
53
|
+
|
|
54
|
+
```
|
|
55
|
+
human edit ──► AtelierStudio.onDocumentChange ──► `doc:patch` envelope
|
|
56
|
+
▼
|
|
57
|
+
bridge writes DocumentStore.set(source:"human")
|
|
58
|
+
▼
|
|
59
|
+
onChange fires (source = "human") → NOT broadcast
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
`applyMutation` is whole-document replacement only in v1 — the op shape is exactly `{ type: "doc:replace", doc, source }`. It snapshots the current state into undo history first (so an agent edit is undoable from your seat), preserves the current state name and selected layer when they still exist in the new doc, and sets a one-tick `suppressNotify` flag so the resulting re-render doesn't echo the change back out as a "human" edit.
|
|
63
|
+
|
|
64
|
+
## ~source-tagged-mutation — the aspect that makes the loop safe
|
|
65
|
+
|
|
66
|
+
Every `DocumentStore` mutation carries a **source tag**: `"human" | "llm" | "system"`. This is the load-bearing convention behind the live bridge, and it answers one question: *which changes should the browser be told about?*
|
|
67
|
+
|
|
68
|
+
The rule, expressed in code as `shouldBroadcastMutation(source)`, is: **broadcast only when `source === "llm"`.**
|
|
69
|
+
|
|
70
|
+
| Source | Where it comes from | Broadcast? | Why |
|
|
71
|
+
|---|---|---|---|
|
|
72
|
+
| `llm` | An MCP tool call over `/mcp` | **Yes** | A genuine agent edit — show it, persist it, toast it |
|
|
73
|
+
| `human` | The browser's own edit via `doc:patch` (or `POST /api/file`) | No | The browser is already current — broadcasting would echo the edit back to the tab that made it |
|
|
74
|
+
| `system` | File-read hydration (a file the user just opened) | No | The bytes are already on disk and in the browser — broadcasting would fire a phantom "agent edited document" toast and a bogus undo entry on a plain file-open |
|
|
75
|
+
|
|
76
|
+
Without this filter you'd get two failure modes: an **echo loop** (the browser's edit bounces back and re-applies) and **false attribution** (opening a file looks like an agent edited it). The tag travels end-to-end — the MCP-side `MutationSource`, the bridge envelope, and the studio-side `MutationSource` are the same three strings, so no translation happens between processes.
|
|
77
|
+
|
|
78
|
+
It also pairs with **~copy-on-write-doc**: any code that mutates a document builds a NEW document object before calling `DocumentStore.set`, never mutating the stored object in place. That guarantees the bridge — a concurrent reader broadcasting to the browser — can never observe a half-mutated document mid-update.
|
|
79
|
+
|
|
80
|
+
## Toast attribution and Cmd-Z
|
|
81
|
+
|
|
82
|
+
When an `llm:mutation` lands and matches the file you're viewing, the studio applies it and shows a bottom-right toast: *"agent edited document (`<tool>`) — Cmd-Z to undo"*. Because `applyMutation` snapshotted before replacing, **Cmd-Z returns the prior state** — an agent's edit is just another entry in your undo history. The agent and you share one timeline.
|
|
83
|
+
|
|
84
|
+
That shared, attributable, undoable timeline is the whole point. The next note ties it into a complete workflow.
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: N-atel-801-studio-app
|
|
3
|
+
title: '`atelier studio` — the local-first editor'
|
|
4
|
+
type: note
|
|
5
|
+
track: ATEL-801
|
|
6
|
+
author: atelier
|
|
7
|
+
created: '2026-05-20'
|
|
8
|
+
updated: '2026-05-20'
|
|
9
|
+
tags:
|
|
10
|
+
- course
|
|
11
|
+
- atel-801
|
|
12
|
+
- studio
|
|
13
|
+
- cli
|
|
14
|
+
difficulty: advanced
|
|
15
|
+
estimatedMinutes: 5
|
|
16
|
+
prerequisites:
|
|
17
|
+
- N-atel-201-mcp-overview
|
|
18
|
+
summary: What `atelier studio` launches — a Vite-backed browser editor bound to 127.0.0.1, the welcome.atelier scaffold on an empty directory, the sidebar file list + "+ New", and the autosave coordinator whose flush-on-switch prevents .atelier corruption. Flags — --port, --no-open.
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## What the command does
|
|
22
|
+
|
|
23
|
+
`atelier studio [file]` spins up a local Vite dev server, writes a tiny entry shim into a temp directory that imports the browser client (`bootStudioApp`), and opens your browser. The client is a real, typechecked TypeScript module — `src/web/inline-app.ts` — not a string. (It used to be a ~700-LOC template-literal string with no typecheck, no lint, no tests; two real bugs hid there precisely because the compiler never saw it. More on both below.)
|
|
24
|
+
|
|
25
|
+
The server reads and writes `.atelier` files from your current working directory. There is no project file, no auth token, no cloud round-trip. You point it at a folder and edit.
|
|
26
|
+
|
|
27
|
+
```
|
|
28
|
+
atelier studio → browse all .atelier files in CWD
|
|
29
|
+
atelier studio my-animation.atelier → open a specific file
|
|
30
|
+
atelier studio --port 8080 → custom port (default 4321)
|
|
31
|
+
atelier studio --no-open → don't auto-open the browser
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
Those are the flags the command actually registers: `-p, --port <number>` and `--no-open`. Nothing else.
|
|
35
|
+
|
|
36
|
+
## ^localhost-only
|
|
37
|
+
|
|
38
|
+
The server binds explicitly to `127.0.0.1` — not the string `"localhost"`. Vite's default already resolves to loopback, but pinning the literal IP makes the invariant intentional and auditable, and it defeats any future change in Vite's defaults. The dev server cannot be reached from another machine on the network.
|
|
39
|
+
|
|
40
|
+
This matters because the studio exposes mutating endpoints (`POST /api/file` writes `.atelier` files into your CWD) and two WebSocket endpoints (`/bridge` and `/mcp`). Two layers protect them:
|
|
41
|
+
|
|
42
|
+
1. **The loopback bind** — the primary protection. Off-machine traffic never arrives.
|
|
43
|
+
2. **An Origin check** on every mutating REST request and on every WS upgrade. Without it, any browser tab on any site could `fetch('http://localhost:4321/api/file', { method: 'POST', ... })` and write files into your project. (The WS Origin check has a subtlety covered in the live-bridge note.)
|
|
44
|
+
|
|
45
|
+
## The welcome scaffold
|
|
46
|
+
|
|
47
|
+
Run `atelier studio` in an empty directory and you don't land on a dead-end empty-state. If CWD contains zero `.atelier` files, the command copies the bundled `welcome.atelier` template into the folder so you open onto something real. It skips files that already exist, so re-running never overwrites your work.
|
|
48
|
+
|
|
49
|
+
The welcome scaffold ships a placeholder layer whose **id is `background`** — a specific id contract, not a fuzzy description. The first image you drop replaces that layer in place (see the editing-surface note). The scaffold is a 1080×1080 social-square so you start at the most common authoring size.
|
|
50
|
+
|
|
51
|
+
## The sidebar and "+ New"
|
|
52
|
+
|
|
53
|
+
The left sidebar lists every `.atelier` file under CWD (recursively, grouped by folder), fetched from `/api/files`. Click an entry to load it. The `+ New` button prompts for a name, writes a minimal-but-non-empty starter document (one centered text layer on a 1080×1080 canvas so the canvas isn't a confusing void), refreshes the list, and selects the new file. A "new file" is just a `POST /api/file` of a fresh starter doc followed by a list refresh — there's no special server-side create path.
|
|
54
|
+
|
|
55
|
+
## Autosave and the save-race that corrupted files
|
|
56
|
+
|
|
57
|
+
The studio autosaves on every document change with an **800ms debounce**. The status bar flips to "saving…" the instant you edit, then "✓ saved" once the write lands (with a click-to-retry affordance if the write fails).
|
|
58
|
+
|
|
59
|
+
The debounce hides a bug that was real and nasty (`#cli-studio` autosave corruption):
|
|
60
|
+
|
|
61
|
+
> Edit file A → switch to file B within the 800ms window. The pending timer closed over A's *doc* but read the GLOBAL current file at fire time — which by then was B — so A's bytes were written to B's path. Real `.atelier` corruption.
|
|
62
|
+
|
|
63
|
+
The fix is twofold, and both halves matter:
|
|
64
|
+
|
|
65
|
+
1. **Capture the target path AND serialized bytes in the timer closure.** A fired timer always writes the bytes it was given to the file it was scheduled for — never whatever the "current" file later becomes.
|
|
66
|
+
2. **Flush the pending save before switching files.** `loadFile` calls `flushPendingSave()` at its very top, writing any in-flight edit to its OWN path, then clears the timer. No edit is dropped and no cross-file write can occur.
|
|
67
|
+
|
|
68
|
+
This logic is extracted into a real, unit-testable class — `SaveCoordinator` (`schedule`, `flushPending`, `cancel`) — so the save-race can be reasoned about in isolation. The inline browser app embeds the **same manual debounce pattern** (it does not import the class); the path/bytes capture and the flush-on-switch are preserved verbatim so there's exactly one behavior to trust.
|
|
69
|
+
|
|
70
|
+
## What you've got after boot
|
|
71
|
+
|
|
72
|
+
A browser window bound to loopback, a file sidebar, an editing canvas, autosave you can trust across file switches, and — quietly — a WebSocket bridge already connected and waiting for an agent. The next note covers what you can do on the canvas; the one after covers the agent.
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: N-atel-801-symbiotic-loop
|
|
3
|
+
title: The symbiotic loop — ingest, compose, co-edit, export
|
|
4
|
+
type: note
|
|
5
|
+
track: ATEL-801
|
|
6
|
+
author: atelier
|
|
7
|
+
created: '2026-05-20'
|
|
8
|
+
updated: '2026-05-20'
|
|
9
|
+
tags:
|
|
10
|
+
- course
|
|
11
|
+
- atel-801
|
|
12
|
+
- workflow
|
|
13
|
+
- import
|
|
14
|
+
- vision
|
|
15
|
+
difficulty: advanced
|
|
16
|
+
estimatedMinutes: 6
|
|
17
|
+
prerequisites:
|
|
18
|
+
- N-atel-201-mcp-overview
|
|
19
|
+
summary: The whole vision in one workflow — an agent runs an external generator in chat, atelier_import_images brings the results into the open doc, the human watches them appear live and keeps or undoes them, recipes and overlays compose the set, and atelier carousel exports it. Atelier owns no generation; it ingests, composes, and lets the human and agent co-edit one document.
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
## Atelier owns no generation
|
|
23
|
+
|
|
24
|
+
This is the thesis of the whole track: **Atelier does not generate images, video, audio, or text.** It has no diffusion model, no text-to-image tool, no in-process generator. What it owns is the part that's hard and durable — a declarative document model, a renderer, a recipe/overlay composition layer, and a live editing loop shared between a human and an agent.
|
|
25
|
+
|
|
26
|
+
Generation lives outside Atelier, in whatever tool the agent chooses to shell out to. Atelier's job begins when the bytes exist: it **ingests** them, **composes** them, and lets you and the agent **co-edit** the result.
|
|
27
|
+
|
|
28
|
+
## The loop, end to end
|
|
29
|
+
|
|
30
|
+
Picture a single chat session with the studio open in your browser:
|
|
31
|
+
|
|
32
|
+
1. **The agent generates — outside Atelier.** You ask for a set of images. The agent runs an external generator from chat — for example a Higgsfield CLI — and writes the results to a folder. None of this touches Atelier; it's the agent driving a separate tool.
|
|
33
|
+
|
|
34
|
+
2. **`atelier_import_images` ingests.** The agent calls the import tool. Each image becomes an ImageVisual layer with a matching `doc.assets` entry, added to the open document via a source-tagged-`llm` mutation. (Like every document-mutating tool, import builds a new document before calling `DocumentStore.set` — ~copy-on-write-doc — so the bridge never reads a half-built doc.)
|
|
35
|
+
|
|
36
|
+
3. **You watch them appear — live.** Because the mutation is `source: "llm"`, the bridge broadcasts it. The layers materialize on your canvas as the agent imports them, each with a toast: *"agent edited document — Cmd-Z to undo"*. You're not waiting for a render-and-refresh cycle; you're watching the document fill in real time.
|
|
37
|
+
|
|
38
|
+
4. **You keep or undo — per edit.** This is the symbiosis. A generated image that lands wrong? Cmd-Z, and it's gone — the agent's import was just an undo entry. The ones you like, you keep. You're curating the agent's output as it arrives, not after a batch finishes.
|
|
39
|
+
|
|
40
|
+
5. **Recipes and overlays compose.** Apply a recipe to lay out the kept images; add a handle and page-number via the overlay namespace. The layer-tag isolation invariant (ATEL-301) means re-running a recipe drops only its own tagged layers — your imported images and any manual tweaks survive. You and the agent can both restyle without erasing each other's work.
|
|
41
|
+
|
|
42
|
+
6. **`atelier carousel` exports the set.** The composed, multi-page result exports as a carousel — the page-number overlay threads `currentIndex` / `totalCount` per page during the batch.
|
|
43
|
+
|
|
44
|
+
## Why both editors at one canvas
|
|
45
|
+
|
|
46
|
+
The agent is good at the mechanical breadth — running the generator, importing a dozen results, applying a recipe across all of them, threading per-page overlays. You're good at the judgment — *that* crop, *not* that one, nudge the handle two pixels. The live bridge and the source-tagged-mutation aspect exist so neither steps on the other:
|
|
47
|
+
|
|
48
|
+
- Every change is **attributed** (`human` vs `llm`) so the UI never lies about who did what.
|
|
49
|
+
- Every change is **undoable** from your seat, so the agent never makes an edit you can't take back.
|
|
50
|
+
- Every change flows through **one document store**, so there's a single source of truth — no merge, no divergence, last-write-wins.
|
|
51
|
+
|
|
52
|
+
## The shape of the thing
|
|
53
|
+
|
|
54
|
+
Atelier is not a generator with an editor bolted on. It's a **composition surface** where a human and an agent edit the same document at the same time, with generation pushed to the edges. The agent brings breadth and the external tools; you bring taste and the veto; the document — declarative, rendered, source-tagged — is the contract between you.
|
|
55
|
+
|
|
56
|
+
That closes ATEL-801. You now understand `atelier studio` from the loopback bind up through the live bridge, and why the whole stack is built so an agent and a human can sit at one canvas without getting in each other's way.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
id: LP-atel-001
|
|
2
|
+
title: 'ATEL-001: Hello Atelier'
|
|
3
|
+
description: Orientation track. What Atelier is, how to install it, how to launch the studio, and how to render a first animation from a hand-written `.atelier` document. ~12 minutes.
|
|
4
|
+
author: atelier
|
|
5
|
+
created: '2026-05-18'
|
|
6
|
+
updated: '2026-05-18'
|
|
7
|
+
tags:
|
|
8
|
+
- course
|
|
9
|
+
- atel-001
|
|
10
|
+
- orientation
|
|
11
|
+
ordered: true
|
|
12
|
+
steps:
|
|
13
|
+
- content: N-atel-001-what-is-atelier
|
|
14
|
+
required: true
|
|
15
|
+
- content: N-atel-001-install-and-launch
|
|
16
|
+
required: true
|
|
17
|
+
- content: N-atel-001-first-render
|
|
18
|
+
required: true
|
|
19
|
+
- content: Q-atel-001-orientation
|
|
20
|
+
required: true
|
|
21
|
+
passRequired: true
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
id: LP-atel-101
|
|
2
|
+
title: 'ATEL-101: Document Model Fundamentals'
|
|
3
|
+
description: The vocabulary every Atelier user needs. The `.atelier` format, layer anatomy, states/deltas/resolveFrame, easings. After this track you can read any Atelier document and predict what it renders. ~18 minutes.
|
|
4
|
+
author: atelier
|
|
5
|
+
created: '2026-05-18'
|
|
6
|
+
updated: '2026-05-18'
|
|
7
|
+
tags:
|
|
8
|
+
- course
|
|
9
|
+
- atel-101
|
|
10
|
+
ordered: true
|
|
11
|
+
steps:
|
|
12
|
+
- content: N-atel-101-the-atelier-format
|
|
13
|
+
required: true
|
|
14
|
+
- content: N-atel-101-layers
|
|
15
|
+
required: true
|
|
16
|
+
- content: N-atel-101-states-and-deltas
|
|
17
|
+
required: true
|
|
18
|
+
- content: N-atel-101-easings
|
|
19
|
+
required: true
|
|
20
|
+
- content: Q-atel-101-document-model
|
|
21
|
+
required: true
|
|
22
|
+
passRequired: true
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
id: LP-atel-201
|
|
2
|
+
title: 'ATEL-201: Authoring via MCP'
|
|
3
|
+
description: How an AI agent (or any MCP scripter) builds, edits, and inspects Atelier documents through the 51-tool surface. Covers the single-store invariant, the 16 tool groups, common authoring patterns, and the four most common agent mistakes. ~20 minutes.
|
|
4
|
+
author: atelier
|
|
5
|
+
created: '2026-05-18'
|
|
6
|
+
updated: '2026-05-18'
|
|
7
|
+
tags:
|
|
8
|
+
- course
|
|
9
|
+
- atel-201
|
|
10
|
+
- mcp
|
|
11
|
+
ordered: true
|
|
12
|
+
steps:
|
|
13
|
+
- content: N-atel-201-mcp-overview
|
|
14
|
+
required: true
|
|
15
|
+
- content: N-atel-201-authoring-tools
|
|
16
|
+
required: true
|
|
17
|
+
- content: N-atel-201-visual-and-effects
|
|
18
|
+
required: true
|
|
19
|
+
- content: N-atel-201-patterns
|
|
20
|
+
required: true
|
|
21
|
+
- content: Q-atel-201-mcp-authoring
|
|
22
|
+
required: true
|
|
23
|
+
passRequired: true
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
id: LP-atel-301
|
|
2
|
+
title: 'ATEL-301: Visual System'
|
|
3
|
+
description: The non-motion visual surface — shapes, text, images, video, effects, composition, and the overlay namespace. After this track you can compose any static scene in Atelier and understand how recipes and the layer-tag isolation invariant make overlays safe across re-runs. ~20 minutes.
|
|
4
|
+
author: atelier
|
|
5
|
+
created: '2026-05-18'
|
|
6
|
+
updated: '2026-05-18'
|
|
7
|
+
tags:
|
|
8
|
+
- course
|
|
9
|
+
- atel-301
|
|
10
|
+
ordered: true
|
|
11
|
+
steps:
|
|
12
|
+
- content: N-atel-301-shapes-and-text
|
|
13
|
+
required: true
|
|
14
|
+
- content: N-atel-301-images-and-video
|
|
15
|
+
required: true
|
|
16
|
+
- content: N-atel-301-effects
|
|
17
|
+
required: true
|
|
18
|
+
- content: N-atel-301-composition-and-overlays
|
|
19
|
+
required: true
|
|
20
|
+
- content: Q-atel-301-visual-system
|
|
21
|
+
required: true
|
|
22
|
+
passRequired: true
|