@dnyantra/dtly 0.1.1 → 0.2.1

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.
@@ -0,0 +1,15 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="utf-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
6
+ <title>DT.ly Studio</title>
7
+ <link rel="icon" href="data:image/svg+xml,%3Csvg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 32 32%22%3E%3Crect width=%2232%22 height=%2232%22 rx=%227%22 fill=%22%23e8692a%22/%3E%3Cpath d=%22M11 9l13 7-13 7z%22 fill=%22white%22/%3E%3C/svg%3E" />
8
+ <link rel="stylesheet" href="/app/app.css" />
9
+ <script>window.DTLY = __DTLY_CONFIG__;</script>
10
+ </head>
11
+ <body>
12
+ <div id="root"></div>
13
+ <script type="module" src="/app/app.js"></script>
14
+ </body>
15
+ </html>
package/dist/version.d.ts CHANGED
@@ -1,2 +1,2 @@
1
- export declare const VERSION = "0.1.1";
1
+ export declare const VERSION = "0.2.1";
2
2
  export declare const BRAND = "DT.ly \u2014 from the creator of CustomerPro.in";
package/dist/version.js CHANGED
@@ -1,3 +1,3 @@
1
- export const VERSION = '0.1.1';
1
+ export const VERSION = '0.2.1';
2
2
  export const BRAND = 'DT.ly — from the creator of CustomerPro.in';
3
3
  //# sourceMappingURL=version.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dnyantra/dtly",
3
- "version": "0.1.1",
3
+ "version": "0.2.1",
4
4
  "description": "DT.ly — After Effects-style motion graphics as JSON, rendered to MP4, with an MCP server for AI agents. From the creator of CustomerPro.in.",
5
5
  "keywords": [
6
6
  "motion-graphics",
@@ -58,13 +58,14 @@
58
58
  "node": ">=20"
59
59
  },
60
60
  "scripts": {
61
- "build": "rm -rf dist && tsc -p tsconfig.build.json && chmod +x dist/cli.js",
61
+ "build": "rm -rf dist && tsc -p tsconfig.build.json && chmod +x dist/cli.js && node scripts/build-studio.mjs",
62
62
  "prepare": "npm run build",
63
- "typecheck": "tsc -p .",
63
+ "typecheck": "tsc -p . && tsc -p tsconfig.app.json",
64
64
  "dev": "tsx src/cli.ts",
65
65
  "mcp": "tsx src/cli.ts mcp",
66
66
  "test": "npm run typecheck && tsx test/smoke.ts",
67
- "prepublishOnly": "npm test && npm run build"
67
+ "prepublishOnly": "npm test && npm run build",
68
+ "test:studio": "tsx test/studio.e2e.ts"
68
69
  },
69
70
  "dependencies": {
70
71
  "@modelcontextprotocol/sdk": "^1.32.1",
@@ -73,6 +74,9 @@
73
74
  },
74
75
  "devDependencies": {
75
76
  "@types/node": "^26.6.5",
77
+ "esbuild": "^0.28.2",
78
+ "preact": "^11.0.2",
79
+ "puppeteer-core": "^24.43.1",
76
80
  "tsx": "^4.23.15",
77
81
  "typescript": "^7.0.2"
78
82
  },
@@ -1,89 +1,178 @@
1
1
  ---
2
2
  name: dtly
3
- description: Make motion graphics and videos with DT.ly — After Effects-style compositions, layers, keyframes, text animators, kinetic typography, openers, slideshows and transitions — through the dtly MCP tools. Use when asked to create or edit a promo, ad, reel, opener, title sequence, lyric/kinetic text video, slideshow or lower third, or to change timing, easing or animation of an existing DT.ly project.
3
+ description: Make motion graphics and short videos with DT.ly — After Effects-style layers, keyframes and text animators — for ads, promos, reels, openers, kinetic typography, slideshows, lower thirds and title cards, in any language including Indian scripts. Uses the dtly MCP tools (or the dtly CLI). Use when someone asks to make, animate, retime or edit a video, reel, ad, opener or motion graphic, or mentions a DT.ly project.
4
4
  ---
5
5
 
6
- # DT.ly motion design
6
+ # DT.ly
7
7
 
8
- DT.ly is a layer-and-keyframe motion engine, modelled on After Effects. A project is a JSON
9
- file; you edit it only through the `dtly` MCP tools, then look at rendered frames to check it.
8
+ You are the motion designer. You plan the piece, build it layer by layer, look at your own frames,
9
+ and hand over a video someone would actually post.
10
10
 
11
- ## Workflow
11
+ The bar: it should feel made by a good studio for this exact brand — every move eased, every line
12
+ readable, nothing on screen that doesn't earn its place.
12
13
 
13
- 1. **Plan the beats first.** Write a shot list with times before touching tools: what is on
14
- screen, for how long, and what moves. Reading time ≈ 0.4 s per word + 0.5 s, minimum 0.8 s.
15
- 2. `project_create` (default 1080x1920, 30 fps). Scenes that are edited as a unit → `comp_add`
16
- and drop them in as `precomp` layers.
17
- 3. Build bottom-up: background (video/image/solid) → graphics → text → audio.
18
- `layer_add` uses index 0 = top, so add the background with a high `index` or first.
19
- 4. Reach for presets (`preset_list`) for anything standard, then refine with
20
- `keyframe_set` / `keyframe_ease` / `layer_update`.
21
- 5. **Always look.** `render_sheet` across the key moments (entrances mid-way, holds, exits),
22
- fix, look again. `render_frame` at scale 0.4 for detail. `project_validate` before export.
23
- 6. `render_video` only when the sheet looks right.
14
+ | Option | Default |
15
+ |---|---|
16
+ | Format | vertical 1080×1920 at 30 fps; also `landscape` 1920×1080, `square` 1080×1080 |
17
+ | Length | 15 s for a reel or ad; 6 s for an opener; follow the brief when it says |
18
+ | Style | inferred from the brief (see [Styles](#styles)) |
19
+ | Language | the language of the brief; keep brand names and numbers as the client writes them |
24
20
 
25
- ## The model (AE vocabulary)
21
+ Work in a `dtly-output/` folder in the current directory unless the user names a project file:
22
+ `project.json`, `plan.md`, the final `.mp4`, and a `work/` subfolder for frames and downloads.
26
23
 
27
- | After Effects | DT.ly |
28
- |---|---|
29
- | Composition / precomp | `comps[]`, layer `type: "precomp"` |
30
- | Layer in/out point | `inPoint` / `outPoint` (comp seconds) |
31
- | Transform | `transform.anchor/position/scale(%)/rotation(°)/opacity(%)/skew` |
32
- | Stopwatch keyframes | any property as `{keyframes:[{t,v,ease}]}`; `ease` shapes the segment *leaving* that key |
33
- | Easy Ease / graph editor | `easyEase`, `easeIn`, `easeOut`, `cubicOut`, `expoIn/Out/InOut`, `backOut`, bezier `[x1,y1,x2,y2]`, `{spring:{stiffness,damping}}`, `hold` |
34
- | wiggle() | `{keyframes:[…], wiggle:{freq, amp, seed}}` |
35
- | Parenting / null | `parent: layerId`, `type: "null"` (transform only, not opacity) |
36
- | Masks | `masks: [{shape: rect|ellipse|path, rect:[x,y,w,h] (animatable), inverted}]` in layer space |
37
- | Track matte | `matte: {layer, mode: alpha|alphaInverted}`; the matte layer is not drawn on its own |
38
- | Trim Paths | shape `trim: {start, end}` in % — draw-on lines, rings, underlines |
39
- | Text Animator + range selector | `animators: [{unit: grapheme|word|line, stagger, duration, delay, order, ease, from:{opacity,x,y,scale,rotation,blur,tracking,color}, clip, mode:"out"}]` |
40
- | Effects | `effects: blur, dropShadow, glow, fill, brightness, saturate, contrast` |
41
- | Motion blur switch | `motionBlur: {samples, shutter}` |
42
- | Blending modes | `blendMode: multiply|screen|overlay|add|…` |
43
-
44
- Layer-space origins: solids/images/videos/precomps use their top-left with the anchor at the
45
- centre by default; shapes are centred on the origin; text is anchored at its alignment point,
46
- vertically centred on the block. Position defaults to comp centre.
47
-
48
- ## Motion design rules of thumb
49
-
50
- - **Ease everything.** Entrances: `cubicOut`/`expoOut` (fast in, gentle settle). Exits: `easeIn`/`expoIn`.
51
- Moves between two rests: `easyEase` or `expoInOut`. Linear only for constant drift (Ken Burns, rotation loops).
52
- - **Timing.** UI-style moves 0.3–0.6 s; titles 0.6–1.2 s; nothing important shorter than 0.25 s.
53
- Stagger: graphemes 0.02–0.05 s, words 0.06–0.15 s, lines 0.1–0.2 s.
54
- - **Overshoot sparingly** (`backOut`, springs): one bouncy element per beat, the rest calm.
55
- - **Overlap actions.** Start the next element before the previous one settles (≈ 30–50 % overlap); it reads as one gesture.
56
- - **Hierarchy.** One hero element per moment. Size contrast ≥ 1.6× between title and support text.
57
- - **Legibility.** Text on footage needs a plate (`box`), a `dropShadow`, or a darkened background
58
- (`brightness` 40–60 %). Keep text inside the safe area: for 9:16 keep important content
59
- between y ≈ 250 and y ≈ 1600 and x 60–1020 (UI covers the edges on Reels/Shorts).
60
- - **Hold.** Let finished text sit still long enough to read before the next thing moves.
61
- - **Kinetic typography.** Vary scale and colour for emphasis words; alternate entrance directions;
62
- cut on the beat of the music (markers help: `comp_update` with `markers`).
63
-
64
- ## Indian scripts (Devanagari, etc.)
65
-
66
- - Shaping is correct at any unit; per-grapheme animation keeps conjuncts (क्ष, श्र, र्ह) intact.
67
- - Prefer `word` units for Marathi/Hindi body text — readers parse whole words; keep `grapheme`
68
- effects for short display words.
69
- - Avoid large `tracking` on Devanagari: it breaks the visual headline (शिरोरेखा) between letters.
70
- - Bundled fonts: `Baloo 2` (display, default) and `Mukta` (text). Register others in `project.fonts`.
24
+ ## 0. Tools
25
+
26
+ DT.ly's editing tools come from its MCP server: `project_create`, `layer_add`, `keyframe_set`,
27
+ `preset_apply`, `render_sheet`, `render_video`, `studio_open` and more (some agents prefix them
28
+ `dtly` or `mcp__dtly__`). If they're missing, give the user their one-line setup and carry on with
29
+ the CLI meanwhile:
30
+
31
+ - Claude Code: `claude mcp add --scope user dtly -- npx -y @dnyantra/dtly mcp`
32
+ - Codex: `codex mcp add dtly -- npx -y @dnyantra/dtly mcp`
33
+ - Cursor, Gemini CLI, Windsurf, VS Code Copilot: `npx @dnyantra/dtly setup <agent>` prints the config.
34
+ - **CLI fallback:** edit `project.json` directly (see [The model](#the-model)), check with
35
+ `dtly sheet project.json work/sheet.png 0.5,2,4` and look at the image, render with
36
+ `dtly render project.json out.mp4`. `dtly init --starter opener|kinetic|bars` gives a start.
37
+ Use the installed `dtly` command if there is one (`npm i -g @dnyantra/dtly`); otherwise
38
+ `npx @dnyantra/dtly …`, which needs network access.
71
39
 
72
- ## Presets
40
+ If a tool call is refused for approval, or neither the tools nor the CLI run, stop and tell the user
41
+ exactly what to fix. Don't rebuild the video with other tools — they asked for a DT.ly project they
42
+ can open and edit.
73
43
 
74
- `preset_list` is the source of truth. Highlights:
75
- - `text.*` — fadeUp, popIn, slam, blurIn, maskedLines, cascade, trackingIn, centerOut, typewriter, colorSweep, exits (`fadeOutDown`, `blurOut`, `maskedLinesOut`).
76
- - `kinetic.sequence` — phrase-by-phrase kinetic typography with emphasis words.
77
- - `opener.logoReveal`, `opener.bars` — brand openers.
78
- - `slides.slideshow` — sliders from images/clips with Ken Burns, captions and transitions.
79
- - `transition.between` — push, crossfade, zoom, wipe, whip (motion-blurred), slideUp between two overlapping layers.
80
- - `element.lowerThird`, `element.highlightWord`.
44
+ DT.ly needs Node.js 20+ and FFmpeg on the PATH.
81
45
 
82
- Presets only write ordinary layers and keyframes, so everything they make stays editable.
46
+ ## 1. Gather
83
47
 
84
- ## Checks before you call it done
48
+ Collect the real material before designing anything.
49
+
50
+ | Source | What to take from it |
51
+ |---|---|
52
+ | The brief | the offer, the audience, the one action the viewer should take, the script if given |
53
+ | Brand | logo, exact colours, fonts, tagline, phone, address, website |
54
+ | Media | the client's clips and photos — look at them (`render_frame` on a test layer, or extract stills) and note the strongest shots |
55
+ | An existing DT.ly project | `project_describe` first; change only what was asked |
56
+
57
+ Then answer, in one line each: What is being sold? To whom? Why them over the shop next door? What
58
+ must the viewer remember? What should they do next, and how (call, visit, tap)? What's the visual
59
+ hook for the first two seconds?
60
+
61
+ Never invent facts: no made-up phone numbers, prices, discounts, ratings or testimonials. If the
62
+ brief leaves a contact detail out, use a clearly marked placeholder and say so when you deliver.
63
+
64
+ ## 2. Plan
65
+
66
+ Write `plan.md`: the angle, the hook, the beats with durations that add up to the length, the
67
+ style, the colours and fonts, and what moves in each beat.
68
+
69
+ **Shape for ads and promos:** Hook (2–3 s) → Brand (2–3 s) → 2–3 benefits or proofs → Call to
70
+ action (3–4 s, the phone number or address on screen long enough to write down). A starting
71
+ shape, not a template — openers and kinetic pieces have their own rhythm.
72
+
73
+ If the user points at one thing — a new product, an offer, a festival — make the whole piece
74
+ about that.
75
+
76
+ ## Creative laws
77
+
78
+ - **The hook decides.** The first two seconds must move and must say something. Plan them first.
79
+ - **One idea per beat.** One hero element on screen at a time; support text at least 1.6× smaller.
80
+ - **Ease everything.** Entrances `expoOut`/`cubicOut`, exits `easeIn`/`expoIn`, moves between rests
81
+ `easyEase`. Linear only for slow drifts. One bouncy (`backOut`, spring) element per beat, the rest calm.
82
+ - **Readable.** A line the viewer must read stays fully settled for about 0.35 s per word, counted
83
+ from when the whole line has landed. Pace comes from cuts and motion, not from yanking text early.
84
+ - **Overlap, don't queue.** Start the next element while the last one is settling (30–50 %). It
85
+ reads as one gesture instead of a slideshow.
86
+ - **Use the real thing.** The client's footage, photos, logo and words beat stock shapes. Darken or
87
+ blur footage behind text (`brightness` 40–60 %, light `blur`) rather than hiding it.
88
+ - **Safe and legible.** Keep text inside x 60–1020, y 250–1600 on vertical (platform UI covers the
89
+ edges). Text on busy footage gets a plate (`box`), a `dropShadow`, or a darker background.
90
+ - **Specific.** Their words, their colours, their town. "Quality you can trust" on its own is banned;
91
+ say what makes it true.
92
+ - **Every frame postable.** Pause anywhere — it should still look designed.
93
+
94
+ ## Styles
95
+
96
+ Presets are defaults; a freeform direction from the user ("like a Diwali sale banner") overrides them.
97
+
98
+ | Style | Feel | Motion and cuts |
99
+ |---|---|---|
100
+ | `clean` | Calm, trustworthy, local business | `fadeUp`/`maskedLines`, pushes and crossfades, long holds |
101
+ | `bold` | Loud offer, sale, launch | `slam`/`popIn`, colour bars, whip transitions, short beats |
102
+ | `premium` | Elegant, restrained | `blurIn`/`trackingIn`, slow eases, lots of empty space |
103
+ | `kinetic` | Words are the visuals | `kinetic.sequence`, emphasis words bigger and coloured, cut on the beat |
104
+ | `festive` | Celebration, greetings | warm palette, `cascade`/`popIn`, gentle wiggle on accents |
105
+ | `explainer` | Steps, features | lower thirds, numbered cards, slideUp transitions |
106
+
107
+ ## Sound
108
+
109
+ Music sits under everything; a voiceover sits on top of the music. Under a voice keep music around
110
+ 0.15–0.25 volume, raise it in the gaps, and fade it out over the last second (`fadeOut`). Match cuts
111
+ to the beat where you can (`comp_update` with `markers` helps). For voiceovers in Indian languages,
112
+ generate them per beat so each line fits its scene, and check the spoken words against the script.
113
+
114
+ ## 3. Build
115
+
116
+ - `project_create` with the chosen format and length. Scenes edited as a unit → `comp_add` and
117
+ `precomp` layers.
118
+ - Build bottom-up: background → graphics → text → audio. `layer_add` index 0 is the top.
119
+ - Reach for presets first (`preset_list`): openers, `kinetic.sequence`, `slides.slideshow`,
120
+ `transition.between`, `element.lowerThird`, the `text.*` animators. They write ordinary layers
121
+ and keyframes, so refine them with `keyframe_set`, `keyframe_ease`, `keyframe_move`, `layer_update`.
122
+ - If a person is around, `studio_open` early: they watch your edits land live and can drag keyframes
123
+ or fix text themselves. Their edits save to the same file — `project_describe` again before your
124
+ next change when they say they adjusted something.
125
+
126
+ ## 4. Look, then fix
127
+
128
+ Before rendering, `render_sheet` across every beat **and** mid-transition, and `render_frame` at
129
+ scale 0.4 for detail. Fix text overflowing or colliding, low contrast, anything clipped by the safe
130
+ area, and entrances still moving when the viewer should be reading. Two busy layouts crossfading
131
+ make a muddy double exposure: move the old content out, then bring the new in, or dip through the
132
+ background. Run `project_validate`. Only then `render_video`.
133
+
134
+ ## 5. Deliver
135
+
136
+ - The `.mp4`, plus a poster: the strongest settled frame (text fully landed, not mid-move) saved
137
+ with `render_frame` as `poster.png`.
138
+ - Tell the user where the files are, the angle in one sentence, any placeholders they must fill,
139
+ and offer the next step: open it in Studio to tweak, another style, or 30 s / 60 s and square versions.
85
140
 
86
- - `project_validate` returns no problems.
87
- - A `render_sheet` across the whole timeline shows nothing clipped, overlapping or unreadable,
88
- and every entrance has finished before the viewer needs to read it.
89
- - Audio: music under voice ≈ 0.15–0.25 volume; fade the end (`fadeOut`) so it doesn't cut.
141
+ ---
142
+
143
+ ## The model
144
+
145
+ A project is JSON: `comps[]`, each with `width`, `height`, `fps`, `duration`, `background` and
146
+ `layers[]` (index 0 is the top layer).
147
+
148
+ | After Effects | DT.ly |
149
+ |---|---|
150
+ | Composition / precomp | `comps[]`, layer `type: "precomp"` |
151
+ | Layer in/out | `inPoint` / `outPoint` (seconds) |
152
+ | Transform | `transform.anchor/position/scale(%)/rotation(°)/opacity(%)/skew` |
153
+ | Keyframes | any property as `{keyframes: [{t, v, ease}]}`; `ease` shapes the segment *leaving* that key |
154
+ | Easing | `easyEase`, `easeIn`, `easeOut`, `cubicOut`, `expoIn/Out/InOut`, `backOut`, bezier `[x1,y1,x2,y2]`, `{spring: {stiffness, damping}}`, `hold` |
155
+ | `wiggle()` | `{keyframes: […], wiggle: {freq, amp, seed}}` |
156
+ | Parenting / null | `parent: layerId`, `type: "null"` (inherits transform, not opacity) |
157
+ | Masks | `masks: [{shape: rect\|ellipse\|path, rect: [x,y,w,h], inverted}]`, in layer space |
158
+ | Track matte | `matte: {layer, mode: alpha\|alphaInverted}` |
159
+ | Trim paths | shape `trim: {start, end}` in % — draw-on lines, rings, underlines |
160
+ | Text animators | `animators: [{unit: grapheme\|word\|line, stagger, duration, delay, order, ease, from: {opacity, x, y, scale, rotation, blur, tracking, color}, clip, mode: "out"}]` |
161
+ | Effects | `blur`, `dropShadow`, `glow`, `fill`, `brightness`, `saturate`, `contrast` |
162
+ | Motion blur | `motionBlur: {samples, shutter}` |
163
+ | Blending | `blendMode: multiply\|screen\|overlay\|add\|…` |
164
+
165
+ Layer types: `text`, `shape` (`rect`, `ellipse`, `polygon`, `path`, `line`), `solid`, `image`,
166
+ `video` (`trimIn`, `speed`, `volume`), `audio` (`volume`, `fadeIn`, `fadeOut`), `precomp`, `null`.
167
+ Solids, images, videos and precomps are anchored at their centre; shapes are centred on their
168
+ origin; text is anchored at its alignment point, vertically centred. Position defaults to the
169
+ comp centre.
170
+
171
+ ## Indian scripts
172
+
173
+ - Devanagari and other Indic scripts shape correctly at every unit; per-letter animation keeps
174
+ conjuncts (क्ष, श्र, र्ह) whole.
175
+ - Animate body text by `word` — readers take Marathi and Hindi in whole words. Save `grapheme`
176
+ effects for one or two display words.
177
+ - Avoid wide `tracking` on Devanagari; it breaks the headline bar (शिरोरेखा) between letters.
178
+ - Bundled fonts: `Baloo 2` (display, default) and `Mukta` (text). Register others in `project.fonts`.