@dnyantra/dtly 0.0.0-stage → 0.2.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 (76) hide show
  1. package/LICENSE +26 -0
  2. package/README.md +172 -2
  3. package/dist/cli.d.ts +2 -0
  4. package/dist/cli.js +95 -0
  5. package/dist/cli.js.map +1 -0
  6. package/dist/core/anim.d.ts +3 -0
  7. package/dist/core/anim.js +61 -0
  8. package/dist/core/anim.js.map +1 -0
  9. package/dist/core/color.d.ts +4 -0
  10. package/dist/core/color.js +28 -0
  11. package/dist/core/color.js.map +1 -0
  12. package/dist/core/ease.d.ts +8 -0
  13. package/dist/core/ease.js +90 -0
  14. package/dist/core/ease.js.map +1 -0
  15. package/dist/core/model.d.ts +229 -0
  16. package/dist/core/model.js +4 -0
  17. package/dist/core/model.js.map +1 -0
  18. package/dist/core/text.d.ts +41 -0
  19. package/dist/core/text.js +222 -0
  20. package/dist/core/text.js.map +1 -0
  21. package/dist/index.d.ts +10 -0
  22. package/dist/index.js +13 -0
  23. package/dist/index.js.map +1 -0
  24. package/dist/mcp/index.d.ts +1 -0
  25. package/dist/mcp/index.js +2 -0
  26. package/dist/mcp/index.js.map +1 -0
  27. package/dist/mcp/server.d.ts +3 -0
  28. package/dist/mcp/server.js +193 -0
  29. package/dist/mcp/server.js.map +1 -0
  30. package/dist/node.d.ts +4 -0
  31. package/dist/node.js +6 -0
  32. package/dist/node.js.map +1 -0
  33. package/dist/ops/ops.d.ts +39 -0
  34. package/dist/ops/ops.js +259 -0
  35. package/dist/ops/ops.js.map +1 -0
  36. package/dist/ops/store.d.ts +4 -0
  37. package/dist/ops/store.js +22 -0
  38. package/dist/ops/store.js.map +1 -0
  39. package/dist/presets/index.d.ts +14 -0
  40. package/dist/presets/index.js +235 -0
  41. package/dist/presets/index.js.map +1 -0
  42. package/dist/presets/text.d.ts +5 -0
  43. package/dist/presets/text.js +19 -0
  44. package/dist/presets/text.js.map +1 -0
  45. package/dist/render/export.d.ts +43 -0
  46. package/dist/render/export.js +215 -0
  47. package/dist/render/export.js.map +1 -0
  48. package/dist/render/ffmpeg.d.ts +1 -0
  49. package/dist/render/ffmpeg.js +16 -0
  50. package/dist/render/ffmpeg.js.map +1 -0
  51. package/dist/render/node-host.d.ts +18 -0
  52. package/dist/render/node-host.js +168 -0
  53. package/dist/render/node-host.js.map +1 -0
  54. package/dist/render/renderer.d.ts +36 -0
  55. package/dist/render/renderer.js +372 -0
  56. package/dist/render/renderer.js.map +1 -0
  57. package/dist/starter.d.ts +5 -0
  58. package/dist/starter.js +32 -0
  59. package/dist/starter.js.map +1 -0
  60. package/dist/studio/server.d.ts +12 -0
  61. package/dist/studio/server.js +190 -0
  62. package/dist/studio/server.js.map +1 -0
  63. package/dist/studio-app/app.css +2 -0
  64. package/dist/studio-app/app.css.map +7 -0
  65. package/dist/studio-app/app.js +5 -0
  66. package/dist/studio-app/app.js.map +7 -0
  67. package/dist/studio-app/index.html +15 -0
  68. package/dist/version.d.ts +2 -0
  69. package/dist/version.js +3 -0
  70. package/dist/version.js.map +1 -0
  71. package/fonts/Baloo2.ttf +0 -0
  72. package/fonts/Mukta-Bold.ttf +0 -0
  73. package/fonts/OFL-Baloo2.txt +93 -0
  74. package/fonts/OFL-Mukta.txt +93 -0
  75. package/package.json +84 -4
  76. package/plugin/skills/dtly/SKILL.md +94 -0
package/LICENSE ADDED
@@ -0,0 +1,26 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 DT.ly — from the creator of CustomerPro.in
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
22
+
23
+ ---
24
+
25
+ Bundled fonts in fonts/ (Baloo 2, Mukta) are licensed under the SIL Open Font
26
+ License 1.1; see fonts/OFL-Baloo2.txt and fonts/OFL-Mukta.txt.
package/README.md CHANGED
@@ -1,3 +1,173 @@
1
- # Temporary Holding Version
1
+ # DT.ly
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ [![npm](https://img.shields.io/npm/v/@dnyantra/dtly)](https://www.npmjs.com/package/@dnyantra/dtly) [![license](https://img.shields.io/npm/l/@dnyantra/dtly)](LICENSE)
4
+
5
+ **After Effects-style motion graphics for code and AI agents.**
6
+ *From the creator of [CustomerPro.in](https://customerpro.in).*
7
+
8
+ DT.ly describes a video as plain JSON — compositions, layers, keyframes with easing curves,
9
+ text animators, masks, track mattes, trim paths, parenting, effects and motion blur — and renders
10
+ it to MP4. An MCP server lets AI agents edit projects keyframe by keyframe and look at the frames
11
+ they make.
12
+
13
+ ![Starter projects](docs/starters.png)
14
+
15
+ - **Fine-grained animation** — any property can be keyframed with AE-style eases, bezier curves or physical springs; `wiggle` included.
16
+ - **Text animators** — per grapheme, word or line, with stagger, order, blur, tracking, colour and masked line reveals. Indian scripts (Devanagari etc.) keep their conjuncts while animating.
17
+ - **Presets** — kinetic typography, logo openers, colour-bar openers, slideshows with Ken Burns, push/whip/wipe/zoom transitions, lower thirds, highlight plates. Presets write ordinary layers, so everything stays editable.
18
+ - **Media** — video, image and audio layers, volume keyframes, music mixing.
19
+ - **Studio** — a browser preview and After Effects-style timeline editor that stays in sync with agents.
20
+ - **Agent-native** — 23 MCP tools plus a skill that teaches motion-design rules.
21
+ - **No browser, no lock-in** — renders with skia-canvas and FFmpeg. MIT licensed.
22
+
23
+ ## Install
24
+
25
+ Requires **Node.js 20+** and **FFmpeg** on your PATH (`brew install ffmpeg`, `sudo apt install ffmpeg`, or `winget install ffmpeg`).
26
+
27
+ ```bash
28
+ npm install @dnyantra/dtly
29
+ ```
30
+
31
+ Try it without installing:
32
+
33
+ ```bash
34
+ npx @dnyantra/dtly init hello.json --starter kinetic --title "Hello"
35
+ npx @dnyantra/dtly render hello.json hello.mp4
36
+ ```
37
+
38
+ ### Use it from Claude Code
39
+
40
+ Install the plugin (MCP server + skill) in one step:
41
+
42
+ ```
43
+ /plugin install dtly --marketplace dnyantra/dtly
44
+ ```
45
+
46
+ Or add the pieces yourself:
47
+
48
+ ```bash
49
+ claude mcp add --scope user dtly -- npx -y @dnyantra/dtly mcp
50
+ npx @dnyantra/dtly skill install
51
+ ```
52
+
53
+ Then ask for things like *"make a 10 second kinetic typography opener for my bakery"* or
54
+ *"ease the call-to-action in with a spring and move it 0.3 s later"*.
55
+
56
+ ## Studio — browser preview and timeline editor
57
+
58
+ ```bash
59
+ npx @dnyantra/dtly studio project.json
60
+ ```
61
+
62
+ ![DT.ly Studio](docs/studio.png)
63
+
64
+ - **Live preview** with the same renderer as the MP4 export, playback with audio, frame stepping.
65
+ - **Timeline**: drag layer bars to move them, drag the ends to trim, twirl open a layer to see every
66
+ animated property, drag keyframes to retime them, double-click a property row to add a key.
67
+ Everything snaps to frames and to the playhead.
68
+ - **Inspector**: After Effects-style stopwatches (⏱ animates a property, ◆ adds/removes a key at the
69
+ playhead, ◀ ▶ jump between keys), scrubby numbers (drag sideways; Shift ×10, Alt ×0.1), colour
70
+ pickers, text, an ease editor with curve preview (named eases, bezier, springs), raw JSON for the rest.
71
+ - **Viewer**: click to select the layer under the cursor, drag to move it (keyframes it if animated).
72
+ - **Undo/redo**, autosave to the project file, **Render MP4** with progress.
73
+ - **Live with agents**: edits an agent makes through MCP appear instantly; the agent can open the
74
+ studio for you with the `studio_open` tool.
75
+
76
+ | Shortcut | Action |
77
+ |---|---|
78
+ | Space | Play / pause |
79
+ | ← / → (Shift: 10 frames) | Step one frame |
80
+ | Home / End | First / last frame |
81
+ | J / K | Previous / next keyframe of the selected layer |
82
+ | [ / ] | Move layer start to playhead / trim layer end at playhead |
83
+ | ⌘Z / ⇧⌘Z | Undo / redo |
84
+ | ⌘D | Duplicate layer |
85
+ | Delete | Delete selected keyframe (or layer) |
86
+ | ⌘/Ctrl + scroll, + / − | Zoom the timeline |
87
+
88
+ The studio listens on 127.0.0.1 only and its API needs a per-session token.
89
+
90
+ ## Command line
91
+
92
+ After `npm install -g @dnyantra/dtly` (or via `npx @dnyantra/dtly …`):
93
+
94
+ ```
95
+ dtly init <project.json> [--starter opener|kinetic|bars] [--title "Text"]
96
+ dtly render <project.json> <out.mp4> [--from s] [--to s] [--comp id] [--workers n]
97
+ dtly still <project.json> <time> <out.png> [--scale 0.5]
98
+ dtly sheet <project.json> <out.png> <t1,t2,...>
99
+ dtly studio <project.json> [--port 4400] [--no-open]
100
+ dtly presets
101
+ dtly mcp
102
+ dtly skill install [--dir path]
103
+ ```
104
+
105
+ ## Library
106
+
107
+ ```ts
108
+ import {newProject, addLayer, setKeyframe, PRESETS} from '@dnyantra/dtly';
109
+ import {writeProject, renderVideo} from '@dnyantra/dtly/node';
110
+
111
+ const p = newProject({name: 'Promo', duration: 5, background: '#111'});
112
+ addLayer(p, 'main', {
113
+ type: 'text', id: 'title', text: 'Fresh bread daily', size: 120, color: '#ffffff',
114
+ animators: [{unit: 'word', stagger: 0.1, duration: 0.6, ease: 'backOut', from: {opacity: 0, y: 60}}],
115
+ });
116
+ setKeyframe(p, 'title', 'transform.scale', 0, [90, 90], {spring: {stiffness: 180, damping: 12}});
117
+ setKeyframe(p, 'title', 'transform.scale', 1, [100, 100]);
118
+ PRESETS['element.highlightWord'].apply(p, 'main', {layer: 'title', at: 1});
119
+
120
+ writeProject('promo.json', p);
121
+ await renderVideo('promo.json', 'promo.mp4');
122
+ ```
123
+
124
+ Entry points: `@dnyantra/dtly` (model, edits, presets, renderer — browser-safe),
125
+ `@dnyantra/dtly/node` (MP4/PNG rendering, project files), `@dnyantra/dtly/mcp` (MCP server).
126
+ Installing the package also gives you the `dtly` command.
127
+
128
+ ## The model
129
+
130
+ | After Effects | DT.ly |
131
+ |---|---|
132
+ | Composition / precomp | `comps[]`, layer `type: "precomp"` |
133
+ | Layer in/out | `inPoint`, `outPoint` (seconds) |
134
+ | Transform | `transform.anchor/position/scale(%)/rotation(°)/opacity(%)/skew` |
135
+ | Keyframes + easing | `{keyframes: [{t, v, ease}]}` — `easyEase`, `expoOut`, `backOut`, `[x1,y1,x2,y2]`, `{spring: {...}}`, `hold` |
136
+ | `wiggle()` | `{keyframes, wiggle: {freq, amp}}` |
137
+ | Parenting / null | `parent: id`, `type: "null"` |
138
+ | Masks / track mattes | `masks: [...]`, `matte: {layer, mode}` |
139
+ | Trim paths | shape `trim: {start, end}` |
140
+ | Text animators | `animators: [{unit, stagger, duration, ease, from: {...}, clip, mode}]` |
141
+ | Effects | `blur`, `dropShadow`, `glow`, `fill`, `brightness`, `saturate`, `contrast` |
142
+ | Motion blur | `motionBlur: {samples, shutter}` |
143
+
144
+ Layer index 0 is the top of the stack, as in After Effects. See
145
+ [`plugin/skills/dtly/SKILL.md`](plugin/skills/dtly/SKILL.md) for the full guide.
146
+
147
+ ## Development
148
+
149
+ ```bash
150
+ git clone https://github.com/dnyantra/dtly && cd dtly
151
+ npm install # also builds dist/
152
+ npm test # typecheck + end-to-end smoke test (renders + MCP round trip)
153
+ npm run test:studio # drives the Studio in your installed Chrome (puppeteer-core)
154
+ npm run dev -- render examples/kinetic.json out.mp4
155
+ ```
156
+
157
+ Opening Claude Code in the repo loads the local MCP server (`.mcp.json`) and the skill.
158
+
159
+ ## Roadmap
160
+
161
+ - Studio: graph editor, drag-to-reorder layers, multi-select, media import by drag and drop
162
+ - Faster rendering: layer-bounds buffers, caching finished layers
163
+ - Templates with brand kits and media slots for fixed 30/60 s videos
164
+ - More AE features: 3D layers and camera, repeaters, path morphing, LUTs
165
+
166
+ ## License
167
+
168
+ MIT for the code. Bundled fonts (Baloo 2, Mukta) are under the SIL Open Font License 1.1.
169
+
170
+ ---
171
+
172
+ DT.ly is made by the creator of [CustomerPro.in](https://customerpro.in) — WhatsApp marketing,
173
+ campaigns and AI agents for small businesses.
package/dist/cli.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
package/dist/cli.js ADDED
@@ -0,0 +1,95 @@
1
+ #!/usr/bin/env node
2
+ import fs from 'node:fs';
3
+ import os from 'node:os';
4
+ import path from 'node:path';
5
+ import { fileURLToPath } from 'node:url';
6
+ import { listPresets } from './presets/index.js';
7
+ import { renderContactSheet, renderStill, renderVideo, renderVideoParallel } from './render/export.js';
8
+ import { writeProject } from './ops/store.js';
9
+ import { STARTERS } from './starter.js';
10
+ import { BRAND, VERSION } from './version.js';
11
+ const [cmd, ...rest] = process.argv.slice(2);
12
+ const usage = `${BRAND} v${VERSION}
13
+ After Effects-style motion graphics as JSON, rendered to MP4.
14
+
15
+ dtly init <project.json> [--starter opener|kinetic|bars] [--title "Text"]
16
+ dtly render <project.json> <out.mp4> [--from s] [--to s] [--comp id] [--workers n]
17
+ dtly still <project.json> <time> <out.png> [--scale 0.5] [--comp id]
18
+ dtly sheet <project.json> <out.png> <t1,t2,...> [--comp id]
19
+ dtly presets list text animators, openers, slides, transitions
20
+ dtly studio <project.json> [--port 4400] [--no-open] browser preview + timeline editor
21
+ dtly mcp run the MCP server (stdio) for AI agents
22
+ dtly skill install [--dir d] copy the agent skill into ~/.claude/skills
23
+
24
+ Docs: https://github.com/dnyantra/dtly`;
25
+ const flag = (name) => { const i = rest.indexOf(`--${name}`); return i >= 0 ? rest[i + 1] : undefined; };
26
+ const num = (s) => (s === undefined ? undefined : Number(s));
27
+ const pkgRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
28
+ try {
29
+ if (cmd === 'render' && rest[1]) {
30
+ const t0 = Date.now();
31
+ const r = await renderVideoParallel(rest[0], rest[1], {
32
+ from: num(flag('from')), to: num(flag('to')), comp: flag('comp'), workers: num(flag('workers')),
33
+ onProgress: (d, n) => { if (d % 30 === 0 || d === n)
34
+ process.stderr.write(`\r${d}/${n} frames`); },
35
+ });
36
+ console.error(`\nRendered ${r.out}: ${r.frames} frames, ${r.audioTracks} audio tracks in ${((Date.now() - t0) / 1000).toFixed(1)}s`);
37
+ }
38
+ else if (cmd === 'chunk') {
39
+ // Internal: one parallel-render worker. Reports progress to the parent over IPC.
40
+ const [project, out, from, to, comp, crf] = rest;
41
+ await renderVideo(project, out, { from: Number(from), to: Number(to), comp: comp || undefined, crf: Number(crf), audio: false, onProgress: (d) => process.send?.({ done: d }) });
42
+ }
43
+ else if (cmd === 'still' && rest[2]) {
44
+ console.log(await renderStill(rest[0], Number(rest[1]), rest[2], { scale: num(flag('scale')), comp: flag('comp') }));
45
+ }
46
+ else if (cmd === 'sheet' && rest[2]) {
47
+ console.log(await renderContactSheet(rest[0], rest[2].split(',').map(Number), rest[1], { comp: flag('comp') }));
48
+ }
49
+ else if (cmd === 'init' && rest[0]) {
50
+ const name = flag('starter') ?? 'opener';
51
+ const starter = STARTERS[name];
52
+ if (!starter)
53
+ throw new Error(`Unknown starter "${name}". Options: ${Object.keys(STARTERS).join(', ')}`);
54
+ if (fs.existsSync(rest[0]))
55
+ throw new Error(`${rest[0]} already exists`);
56
+ writeProject(rest[0], starter.build(flag('title') ?? 'DT.ly'));
57
+ console.log(`Created ${rest[0]} (${starter.description})\nNext: dtly render ${rest[0]} ${rest[0].replace(/\.json$/, '')}.mp4`);
58
+ }
59
+ else if (cmd === 'presets') {
60
+ console.log(listPresets());
61
+ }
62
+ else if (cmd === 'studio' && rest[0]) {
63
+ const { startStudio } = await import('./studio/server.js');
64
+ const { url } = await startStudio({ project: rest[0], port: num(flag('port')) });
65
+ console.log(`${BRAND}\nStudio for ${rest[0]} → ${url}\nEdits save to the file; changes made by agents or other tools appear live. Ctrl+C to stop.`);
66
+ if (!rest.includes('--no-open')) {
67
+ const { spawn } = await import('node:child_process');
68
+ const opener = process.platform === 'darwin' ? 'open' : process.platform === 'win32' ? 'explorer' : 'xdg-open';
69
+ spawn(opener, [url], { stdio: 'ignore', detached: true }).unref();
70
+ }
71
+ }
72
+ else if (cmd === 'mcp') {
73
+ const { startMcpServer } = await import('./mcp/server.js');
74
+ await startMcpServer();
75
+ }
76
+ else if (cmd === 'skill' && rest[0] === 'install') {
77
+ const base = flag('dir') ?? path.join(process.env.CLAUDE_CONFIG_DIR ?? path.join(os.homedir(), '.claude'), 'skills');
78
+ const dest = path.join(base, 'dtly');
79
+ fs.mkdirSync(dest, { recursive: true });
80
+ fs.cpSync(path.join(pkgRoot, 'plugin/skills/dtly'), dest, { recursive: true });
81
+ console.log(`Installed the DT.ly skill to ${dest}`);
82
+ }
83
+ else if (cmd === '--version' || cmd === '-v' || cmd === 'version') {
84
+ console.log(VERSION);
85
+ }
86
+ else {
87
+ console.log(usage);
88
+ process.exit(cmd && cmd !== 'help' && cmd !== '--help' ? 1 : 0);
89
+ }
90
+ }
91
+ catch (e) {
92
+ console.error(`dtly: ${e.message}`);
93
+ process.exit(1);
94
+ }
95
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import { type Keyframe, type Prop } from './model.js';
2
+ export declare const evalKeyframes: <T>(kfs: Keyframe<T>[], t: number) => T;
3
+ export declare function val<T>(p: Prop<T> | undefined, t: number, fallback: T): T;
@@ -0,0 +1,61 @@
1
+ import { applyEase } from './ease.js';
2
+ import { parseColor, rgbaString } from './color.js';
3
+ import { isAnimated } from './model.js';
4
+ const lerp = (a, b, p) => a + (b - a) * p;
5
+ const interpolate = (a, b, p) => {
6
+ if (typeof a === 'number' && typeof b === 'number')
7
+ return lerp(a, b, p);
8
+ if (Array.isArray(a) && Array.isArray(b))
9
+ return a.map((v, i) => lerp(v, b[i] ?? v, p));
10
+ if (typeof a === 'string' && typeof b === 'string') {
11
+ const ca = parseColor(a), cb = parseColor(b);
12
+ if (ca && cb)
13
+ return rgbaString(ca.map((v, i) => lerp(v, cb[i], p)));
14
+ }
15
+ return p < 1 ? a : b;
16
+ };
17
+ // Smooth 1D value noise for wiggle(); deterministic per seed so renders are repeatable.
18
+ const hash = (n) => {
19
+ const x = Math.sin(n * 127.1 + 311.7) * 43758.5453;
20
+ return x - Math.floor(x);
21
+ };
22
+ const noise = (x, seed) => {
23
+ const i = Math.floor(x), f = x - i;
24
+ const u = f * f * (3 - 2 * f);
25
+ return lerp(hash(i + seed * 1000) * 2 - 1, hash(i + 1 + seed * 1000) * 2 - 1, u);
26
+ };
27
+ const wiggle = (value, t, w) => {
28
+ const off = (dim) => noise(t * w.freq, (w.seed ?? 1) + dim * 17.3) * w.amp;
29
+ if (typeof value === 'number')
30
+ return value + off(0);
31
+ if (Array.isArray(value))
32
+ return value.map((v, i) => v + off(i));
33
+ return value;
34
+ };
35
+ export const evalKeyframes = (kfs, t) => {
36
+ if (kfs.length === 0)
37
+ throw new Error('empty keyframes');
38
+ if (t <= kfs[0].t)
39
+ return kfs[0].v;
40
+ const last = kfs[kfs.length - 1];
41
+ if (t >= last.t)
42
+ return last.v;
43
+ let i = 0;
44
+ while (i < kfs.length - 2 && t >= kfs[i + 1].t)
45
+ i++;
46
+ const a = kfs[i], b = kfs[i + 1];
47
+ const seg = b.t - a.t;
48
+ const p = applyEase(a.ease, seg <= 0 ? 1 : (t - a.t) / seg, seg);
49
+ return interpolate(a.v, b.v, p);
50
+ };
51
+ // Evaluate any property at time t (seconds, in the layer's composition time).
52
+ export function val(p, t, fallback) {
53
+ if (p === undefined || p === null)
54
+ return fallback;
55
+ if (!isAnimated(p))
56
+ return p;
57
+ const sorted = [...p.keyframes].sort((a, b) => a.t - b.t);
58
+ const v = evalKeyframes(sorted, t);
59
+ return (p.wiggle ? wiggle(v, t, p.wiggle) : v);
60
+ }
61
+ //# sourceMappingURL=anim.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"anim.js","sourceRoot":"","sources":["../../src/core/anim.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ export type RGBA = [number, number, number, number];
2
+ export declare const parseColor: (c: string) => RGBA | null;
3
+ export declare const rgbaString: ([r, g, b, a]: RGBA) => string;
4
+ export declare const withAlpha: (c: string, alpha: number) => string;
@@ -0,0 +1,28 @@
1
+ const NAMED = {
2
+ white: '#ffffff', black: '#000000', red: '#ff0000', green: '#008000', blue: '#0000ff',
3
+ yellow: '#ffff00', orange: '#ffa500', transparent: '#00000000', gray: '#808080', grey: '#808080',
4
+ };
5
+ export const parseColor = (c) => {
6
+ const s = (NAMED[c.trim().toLowerCase()] ?? c).trim();
7
+ if (s.startsWith('#')) {
8
+ let h = s.slice(1);
9
+ if (h.length === 3 || h.length === 4)
10
+ h = [...h].map((ch) => ch + ch).join('');
11
+ if (h.length !== 6 && h.length !== 8)
12
+ return null;
13
+ const n = (i) => parseInt(h.slice(i, i + 2), 16);
14
+ return [n(0), n(2), n(4), h.length === 8 ? n(6) / 255 : 1];
15
+ }
16
+ const m = s.match(/^rgba?\(([^)]+)\)$/i);
17
+ if (m) {
18
+ const p = m[1].split(/[\s,\/]+/).filter(Boolean).map(Number);
19
+ return [p[0], p[1], p[2], p[3] ?? 1];
20
+ }
21
+ return null;
22
+ };
23
+ export const rgbaString = ([r, g, b, a]) => `rgba(${Math.round(r)},${Math.round(g)},${Math.round(b)},${Math.max(0, Math.min(1, a)).toFixed(4)})`;
24
+ export const withAlpha = (c, alpha) => {
25
+ const p = parseColor(c);
26
+ return p ? rgbaString([p[0], p[1], p[2], p[3] * alpha]) : c;
27
+ };
28
+ //# sourceMappingURL=color.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"color.js","sourceRoot":"","sources":["../../src/core/color.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ import type { Ease } from './model.js';
2
+ export declare const spring: (elapsed: number, { stiffness, damping, mass }?: {
3
+ damping?: number | undefined;
4
+ mass?: number | undefined;
5
+ stiffness?: number | undefined;
6
+ }) => number;
7
+ export declare const applyEase: (ease: Ease | undefined, x: number, segSeconds?: number) => number;
8
+ export declare const EASE_NAMES: string[];
@@ -0,0 +1,90 @@
1
+ // Cubic-bezier solver (same curve definition as CSS / AE's speed graph handles).
2
+ const bezier = (x1, y1, x2, y2) => {
3
+ const cx = 3 * x1, bx = 3 * (x2 - x1) - cx, ax = 1 - cx - bx;
4
+ const cy = 3 * y1, by = 3 * (y2 - y1) - cy, ay = 1 - cy - by;
5
+ const sx = (t) => ((ax * t + bx) * t + cx) * t;
6
+ const sy = (t) => ((ay * t + by) * t + cy) * t;
7
+ const dx = (t) => (3 * ax * t + 2 * bx) * t + cx;
8
+ return (x) => {
9
+ let t = x;
10
+ for (let i = 0; i < 8; i++) {
11
+ const e = sx(t) - x;
12
+ if (Math.abs(e) < 1e-6)
13
+ return sy(t);
14
+ const d = dx(t);
15
+ if (Math.abs(d) < 1e-6)
16
+ break;
17
+ t -= e / d;
18
+ }
19
+ let lo = 0, hi = 1;
20
+ t = x;
21
+ for (let i = 0; i < 40; i++) {
22
+ const v = sx(t);
23
+ if (Math.abs(v - x) < 1e-6)
24
+ break;
25
+ if (v < x)
26
+ lo = t;
27
+ else
28
+ hi = t;
29
+ t = (lo + hi) / 2;
30
+ }
31
+ return sy(t);
32
+ };
33
+ };
34
+ const CURVES = {
35
+ easeIn: [0.42, 0, 1, 1],
36
+ easeOut: [0, 0, 0.58, 1],
37
+ easyEase: [0.333, 0, 0.667, 1], // AE "Easy Ease" (33% influence both sides)
38
+ quadOut: [0.25, 0.46, 0.45, 0.94],
39
+ cubicOut: [0.215, 0.61, 0.355, 1],
40
+ expoIn: [0.95, 0.05, 0.795, 0.035],
41
+ expoOut: [0.19, 1, 0.22, 1],
42
+ expoInOut: [1, 0, 0, 1],
43
+ backOut: [0.175, 0.885, 0.32, 1.275],
44
+ backIn: [0.6, -0.28, 0.735, 0.045],
45
+ };
46
+ const cache = new Map();
47
+ const elasticOut = (x) => x === 0 || x === 1 ? x : Math.pow(2, -10 * x) * Math.sin((x * 10 - 0.75) * ((2 * Math.PI) / 3)) + 1;
48
+ const bounceOut = (x) => {
49
+ const n = 7.5625, d = 2.75;
50
+ if (x < 1 / d)
51
+ return n * x * x;
52
+ if (x < 2 / d)
53
+ return n * (x -= 1.5 / d) * x + 0.75;
54
+ if (x < 2.5 / d)
55
+ return n * (x -= 2.25 / d) * x + 0.9375;
56
+ return n * (x -= 2.625 / d) * x + 0.984375;
57
+ };
58
+ // Damped spring from 0 to 1; `elapsed` in seconds so the motion is physical, not stretched.
59
+ export const spring = (elapsed, { stiffness = 170, damping = 18, mass = 1 } = {}) => {
60
+ const w0 = Math.sqrt(stiffness / mass);
61
+ const zeta = damping / (2 * Math.sqrt(stiffness * mass));
62
+ if (zeta < 1) {
63
+ const wd = w0 * Math.sqrt(1 - zeta * zeta);
64
+ return 1 - Math.exp(-zeta * w0 * elapsed) * (Math.cos(wd * elapsed) + ((zeta * w0) / wd) * Math.sin(wd * elapsed));
65
+ }
66
+ return 1 - Math.exp(-w0 * elapsed) * (1 + w0 * elapsed);
67
+ };
68
+ // Progress 0..1 (may overshoot) for a segment, given linear progress x and segment length in seconds.
69
+ export const applyEase = (ease, x, segSeconds = 1) => {
70
+ if (!ease || ease === 'linear')
71
+ return x;
72
+ if (ease === 'hold')
73
+ return x >= 1 ? 1 : 0;
74
+ if (ease === 'elasticOut')
75
+ return elasticOut(x);
76
+ if (ease === 'bounceOut')
77
+ return bounceOut(x);
78
+ if (typeof ease === 'object' && !Array.isArray(ease))
79
+ return x >= 1 ? 1 : spring(x * segSeconds, ease.spring);
80
+ const pts = Array.isArray(ease) ? ease : CURVES[ease];
81
+ if (!pts)
82
+ return x;
83
+ const key = pts.join(',');
84
+ let fn = cache.get(key);
85
+ if (!fn)
86
+ cache.set(key, (fn = bezier(...pts)));
87
+ return fn(x);
88
+ };
89
+ export const EASE_NAMES = ['linear', 'hold', ...Object.keys(CURVES), 'elasticOut', 'bounceOut'];
90
+ //# sourceMappingURL=ease.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ease.js","sourceRoot":"","sources":["../../src/core/ease.ts"],"names":[],"mappings":"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"}