@studiomeyer/mcp-video 1.0.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 (184) hide show
  1. package/.github/ISSUE_TEMPLATE/bug_report.md +31 -0
  2. package/.github/ISSUE_TEMPLATE/feature_request.md +19 -0
  3. package/.github/workflows/ci.yml +34 -0
  4. package/CHANGELOG.md +24 -0
  5. package/CONTRIBUTING.md +75 -0
  6. package/LICENSE +21 -0
  7. package/README.md +198 -0
  8. package/USAGE.md +144 -0
  9. package/dist/handlers/capcut.d.ts +6 -0
  10. package/dist/handlers/capcut.js +229 -0
  11. package/dist/handlers/capcut.js.map +1 -0
  12. package/dist/handlers/editing.d.ts +6 -0
  13. package/dist/handlers/editing.js +242 -0
  14. package/dist/handlers/editing.js.map +1 -0
  15. package/dist/handlers/index.d.ts +2 -0
  16. package/dist/handlers/index.js +33 -0
  17. package/dist/handlers/index.js.map +1 -0
  18. package/dist/handlers/post-production.d.ts +5 -0
  19. package/dist/handlers/post-production.js +109 -0
  20. package/dist/handlers/post-production.js.map +1 -0
  21. package/dist/handlers/smart-screenshot.d.ts +5 -0
  22. package/dist/handlers/smart-screenshot.js +83 -0
  23. package/dist/handlers/smart-screenshot.js.map +1 -0
  24. package/dist/handlers/tts.d.ts +5 -0
  25. package/dist/handlers/tts.js +83 -0
  26. package/dist/handlers/tts.js.map +1 -0
  27. package/dist/handlers/video.d.ts +5 -0
  28. package/dist/handlers/video.js +127 -0
  29. package/dist/handlers/video.js.map +1 -0
  30. package/dist/lib/dual-transport.d.ts +42 -0
  31. package/dist/lib/dual-transport.js +208 -0
  32. package/dist/lib/dual-transport.js.map +1 -0
  33. package/dist/lib/logger.d.ts +12 -0
  34. package/dist/lib/logger.js +42 -0
  35. package/dist/lib/logger.js.map +1 -0
  36. package/dist/lib/types.d.ts +16 -0
  37. package/dist/lib/types.js +15 -0
  38. package/dist/lib/types.js.map +1 -0
  39. package/dist/schemas/capcut.d.ts +608 -0
  40. package/dist/schemas/capcut.js +411 -0
  41. package/dist/schemas/capcut.js.map +1 -0
  42. package/dist/schemas/editing.d.ts +822 -0
  43. package/dist/schemas/editing.js +466 -0
  44. package/dist/schemas/editing.js.map +1 -0
  45. package/dist/schemas/index.d.ts +2366 -0
  46. package/dist/schemas/index.js +15 -0
  47. package/dist/schemas/index.js.map +1 -0
  48. package/dist/schemas/post-production.d.ts +379 -0
  49. package/dist/schemas/post-production.js +268 -0
  50. package/dist/schemas/post-production.js.map +1 -0
  51. package/dist/schemas/smart-screenshot.d.ts +127 -0
  52. package/dist/schemas/smart-screenshot.js +122 -0
  53. package/dist/schemas/smart-screenshot.js.map +1 -0
  54. package/dist/schemas/tts.d.ts +220 -0
  55. package/dist/schemas/tts.js +194 -0
  56. package/dist/schemas/tts.js.map +1 -0
  57. package/dist/schemas/video.d.ts +236 -0
  58. package/dist/schemas/video.js +210 -0
  59. package/dist/schemas/video.js.map +1 -0
  60. package/dist/server.d.ts +11 -0
  61. package/dist/server.js +239 -0
  62. package/dist/server.js.map +1 -0
  63. package/dist/server.test.d.ts +1 -0
  64. package/dist/server.test.js +87 -0
  65. package/dist/server.test.js.map +1 -0
  66. package/dist/tools/engine/audio-mixer.d.ts +40 -0
  67. package/dist/tools/engine/audio-mixer.js +169 -0
  68. package/dist/tools/engine/audio-mixer.js.map +1 -0
  69. package/dist/tools/engine/audio.d.ts +22 -0
  70. package/dist/tools/engine/audio.js +73 -0
  71. package/dist/tools/engine/audio.js.map +1 -0
  72. package/dist/tools/engine/beat-sync.d.ts +31 -0
  73. package/dist/tools/engine/beat-sync.js +270 -0
  74. package/dist/tools/engine/beat-sync.js.map +1 -0
  75. package/dist/tools/engine/capture.d.ts +12 -0
  76. package/dist/tools/engine/capture.js +290 -0
  77. package/dist/tools/engine/capture.js.map +1 -0
  78. package/dist/tools/engine/chroma-key.d.ts +27 -0
  79. package/dist/tools/engine/chroma-key.js +154 -0
  80. package/dist/tools/engine/chroma-key.js.map +1 -0
  81. package/dist/tools/engine/concat.d.ts +49 -0
  82. package/dist/tools/engine/concat.js +149 -0
  83. package/dist/tools/engine/concat.js.map +1 -0
  84. package/dist/tools/engine/cursor.d.ts +26 -0
  85. package/dist/tools/engine/cursor.js +185 -0
  86. package/dist/tools/engine/cursor.js.map +1 -0
  87. package/dist/tools/engine/easing.d.ts +15 -0
  88. package/dist/tools/engine/easing.js +100 -0
  89. package/dist/tools/engine/easing.js.map +1 -0
  90. package/dist/tools/engine/editing.d.ts +158 -0
  91. package/dist/tools/engine/editing.js +541 -0
  92. package/dist/tools/engine/editing.js.map +1 -0
  93. package/dist/tools/engine/encoder.d.ts +31 -0
  94. package/dist/tools/engine/encoder.js +154 -0
  95. package/dist/tools/engine/encoder.js.map +1 -0
  96. package/dist/tools/engine/index.d.ts +30 -0
  97. package/dist/tools/engine/index.js +23 -0
  98. package/dist/tools/engine/index.js.map +1 -0
  99. package/dist/tools/engine/lut-presets.d.ts +25 -0
  100. package/dist/tools/engine/lut-presets.js +141 -0
  101. package/dist/tools/engine/lut-presets.js.map +1 -0
  102. package/dist/tools/engine/narrated-video.d.ts +63 -0
  103. package/dist/tools/engine/narrated-video.js +163 -0
  104. package/dist/tools/engine/narrated-video.js.map +1 -0
  105. package/dist/tools/engine/scenes.d.ts +17 -0
  106. package/dist/tools/engine/scenes.js +223 -0
  107. package/dist/tools/engine/scenes.js.map +1 -0
  108. package/dist/tools/engine/smart-screenshot.d.ts +80 -0
  109. package/dist/tools/engine/smart-screenshot.js +744 -0
  110. package/dist/tools/engine/smart-screenshot.js.map +1 -0
  111. package/dist/tools/engine/social-format.d.ts +66 -0
  112. package/dist/tools/engine/social-format.js +107 -0
  113. package/dist/tools/engine/social-format.js.map +1 -0
  114. package/dist/tools/engine/template-renderer.d.ts +45 -0
  115. package/dist/tools/engine/template-renderer.js +233 -0
  116. package/dist/tools/engine/template-renderer.js.map +1 -0
  117. package/dist/tools/engine/templates.d.ts +87 -0
  118. package/dist/tools/engine/templates.js +272 -0
  119. package/dist/tools/engine/templates.js.map +1 -0
  120. package/dist/tools/engine/text-animations.d.ts +33 -0
  121. package/dist/tools/engine/text-animations.js +192 -0
  122. package/dist/tools/engine/text-animations.js.map +1 -0
  123. package/dist/tools/engine/text-overlay.d.ts +27 -0
  124. package/dist/tools/engine/text-overlay.js +84 -0
  125. package/dist/tools/engine/text-overlay.js.map +1 -0
  126. package/dist/tools/engine/tts.d.ts +54 -0
  127. package/dist/tools/engine/tts.js +186 -0
  128. package/dist/tools/engine/tts.js.map +1 -0
  129. package/dist/tools/engine/types.d.ts +166 -0
  130. package/dist/tools/engine/types.js +13 -0
  131. package/dist/tools/engine/types.js.map +1 -0
  132. package/dist/tools/engine/voice-effects.d.ts +18 -0
  133. package/dist/tools/engine/voice-effects.js +215 -0
  134. package/dist/tools/engine/voice-effects.js.map +1 -0
  135. package/dist/tools/index.d.ts +32 -0
  136. package/dist/tools/index.js +23 -0
  137. package/dist/tools/index.js.map +1 -0
  138. package/package.json +56 -0
  139. package/scripts/check-deps.js +39 -0
  140. package/src/handlers/capcut.ts +245 -0
  141. package/src/handlers/editing.ts +260 -0
  142. package/src/handlers/index.ts +34 -0
  143. package/src/handlers/post-production.ts +136 -0
  144. package/src/handlers/smart-screenshot.ts +86 -0
  145. package/src/handlers/tts.ts +103 -0
  146. package/src/handlers/video.ts +137 -0
  147. package/src/lib/dual-transport.ts +272 -0
  148. package/src/lib/logger.ts +59 -0
  149. package/src/lib/types.ts +25 -0
  150. package/src/schemas/capcut.ts +418 -0
  151. package/src/schemas/editing.ts +476 -0
  152. package/src/schemas/index.ts +15 -0
  153. package/src/schemas/post-production.ts +273 -0
  154. package/src/schemas/smart-screenshot.ts +122 -0
  155. package/src/schemas/tts.ts +197 -0
  156. package/src/schemas/video.ts +211 -0
  157. package/src/server.test.ts +99 -0
  158. package/src/server.ts +289 -0
  159. package/src/tools/engine/audio-mixer.ts +244 -0
  160. package/src/tools/engine/audio.ts +115 -0
  161. package/src/tools/engine/beat-sync.ts +356 -0
  162. package/src/tools/engine/capture.ts +360 -0
  163. package/src/tools/engine/chroma-key.ts +202 -0
  164. package/src/tools/engine/concat.ts +242 -0
  165. package/src/tools/engine/cursor.ts +222 -0
  166. package/src/tools/engine/easing.ts +120 -0
  167. package/src/tools/engine/editing.ts +809 -0
  168. package/src/tools/engine/encoder.ts +208 -0
  169. package/src/tools/engine/index.ts +33 -0
  170. package/src/tools/engine/lut-presets.ts +235 -0
  171. package/src/tools/engine/narrated-video.ts +267 -0
  172. package/src/tools/engine/scenes.ts +309 -0
  173. package/src/tools/engine/smart-screenshot.ts +923 -0
  174. package/src/tools/engine/social-format.ts +146 -0
  175. package/src/tools/engine/template-renderer.ts +294 -0
  176. package/src/tools/engine/templates.ts +370 -0
  177. package/src/tools/engine/text-animations.ts +282 -0
  178. package/src/tools/engine/text-overlay.ts +143 -0
  179. package/src/tools/engine/tts.ts +284 -0
  180. package/src/tools/engine/types.ts +191 -0
  181. package/src/tools/engine/voice-effects.ts +258 -0
  182. package/src/tools/index.ts +67 -0
  183. package/tsconfig.json +19 -0
  184. package/vitest.config.ts +7 -0
@@ -0,0 +1,149 @@
1
+ /**
2
+ * Video concatenation engine — merge multiple clips with cinematic transitions
3
+ */
4
+ import { execFile } from 'child_process';
5
+ import * as fs from 'fs';
6
+ import * as path from 'path';
7
+ import { logger } from '../../lib/logger.js';
8
+ import { getMediaDuration } from './audio.js';
9
+ // ─── Available Transitions ──────────────────────────────────────────
10
+ export const TRANSITIONS = [
11
+ 'fade', 'fadeblack', 'fadewhite', 'dissolve',
12
+ 'wipeleft', 'wiperight', 'wipeup', 'wipedown',
13
+ 'slideleft', 'slideright', 'slideup', 'slidedown',
14
+ 'smoothleft', 'smoothright', 'smoothup', 'smoothdown',
15
+ 'circlecrop', 'circleopen', 'circleclose',
16
+ 'rectcrop', 'vertopen', 'vertclose', 'horzopen', 'horzclose',
17
+ 'diagtl', 'diagtr', 'diagbl', 'diagbr',
18
+ 'hlslice', 'hrslice', 'vuslice', 'vdslice',
19
+ 'radial', 'pixelize',
20
+ ];
21
+ export async function concatenateVideos(config) {
22
+ const { clips, outputPath, transition = 'fade', transitionDuration = 1, targetWidth = 1920, targetHeight = 1080, targetFps = 60, } = config;
23
+ if (clips.length === 0)
24
+ throw new Error('No clips provided');
25
+ if (clips.length === 1) {
26
+ fs.copyFileSync(clips[0].path, outputPath);
27
+ return outputPath;
28
+ }
29
+ // Verify all clips exist
30
+ for (const clip of clips) {
31
+ if (!fs.existsSync(clip.path))
32
+ throw new Error(`Clip not found: ${clip.path}`);
33
+ }
34
+ // Get durations for each clip
35
+ const durations = [];
36
+ for (const clip of clips) {
37
+ let dur = await getMediaDuration(clip.path);
38
+ if (clip.trimStart)
39
+ dur -= clip.trimStart;
40
+ if (clip.trimEnd)
41
+ dur = Math.min(dur, clip.trimEnd - (clip.trimStart ?? 0));
42
+ durations.push(dur);
43
+ }
44
+ logger.info(`Concatenating ${clips.length} clips (${durations.map(d => d.toFixed(1) + 's').join(' + ')}) with ${transition} transition`);
45
+ const inputs = [];
46
+ const filterParts = [];
47
+ // Add inputs
48
+ for (const clip of clips) {
49
+ if (clip.trimStart !== undefined || clip.trimEnd !== undefined) {
50
+ if (clip.trimStart)
51
+ inputs.push('-ss', String(clip.trimStart));
52
+ if (clip.trimEnd)
53
+ inputs.push('-to', String(clip.trimEnd));
54
+ }
55
+ inputs.push('-i', clip.path);
56
+ }
57
+ // Normalize all inputs to same resolution and fps
58
+ for (let i = 0; i < clips.length; i++) {
59
+ filterParts.push(`[${i}:v]scale=${targetWidth}:${targetHeight}:force_original_aspect_ratio=decrease,` +
60
+ `pad=${targetWidth}:${targetHeight}:(ow-iw)/2:(oh-ih)/2:color=black,` +
61
+ `setsar=1,fps=${targetFps}[v${i}]`);
62
+ }
63
+ // Chain xfade filters with correct offset calculation
64
+ let cumulativeDuration = durations[0];
65
+ let prevLabel = 'v0';
66
+ for (let i = 1; i < clips.length; i++) {
67
+ const offset = Math.max(0, cumulativeDuration - transitionDuration);
68
+ const outLabel = i === clips.length - 1 ? 'vout' : `xf${i}`;
69
+ filterParts.push(`[${prevLabel}][v${i}]xfade=transition=${transition}:duration=${transitionDuration}:offset=${offset.toFixed(3)}[${outLabel}]`);
70
+ cumulativeDuration += durations[i] - transitionDuration;
71
+ prevLabel = outLabel;
72
+ }
73
+ // Ensure output dir exists
74
+ const outDir = path.dirname(outputPath);
75
+ if (!fs.existsSync(outDir))
76
+ fs.mkdirSync(outDir, { recursive: true });
77
+ const args = [
78
+ '-y',
79
+ ...inputs,
80
+ '-filter_complex', filterParts.join(';'),
81
+ '-map', '[vout]',
82
+ '-c:v', 'libx264',
83
+ '-crf', '18',
84
+ '-preset', 'medium',
85
+ '-pix_fmt', 'yuv420p',
86
+ '-movflags', '+faststart',
87
+ outputPath,
88
+ ];
89
+ await runFfmpeg(args);
90
+ const stats = fs.statSync(outputPath);
91
+ logger.info(`Concatenated: ${outputPath} (${(stats.size / 1024 / 1024).toFixed(2)} MB)`);
92
+ return outputPath;
93
+ }
94
+ export async function generateIntro(config) {
95
+ const { text, subtitle, duration = 3, backgroundColor = '#0a0a0a', textColor = 'white', width = 1920, height = 1080, fps = 60, outputPath, } = config;
96
+ const outDir = path.dirname(outputPath);
97
+ if (!fs.existsSync(outDir))
98
+ fs.mkdirSync(outDir, { recursive: true });
99
+ // Find a usable font
100
+ const fontPath = await findFont();
101
+ // Build drawtext filter with fade animation
102
+ const escapedText = text.replace(/'/g, "\\\\'").replace(/:/g, '\\:');
103
+ let vf = `drawtext=text='${escapedText}':fontfile='${fontPath}':fontsize=72:fontcolor=${textColor}:x=(w-text_w)/2:y=(h-text_h)/2-40:alpha='if(lt(t\\,0.8)\\,t/0.8\\,if(gt(t\\,${duration - 0.8})\\,(${duration}-t)/0.8\\,1))'`;
104
+ if (subtitle) {
105
+ const escapedSub = subtitle.replace(/'/g, "\\\\'").replace(/:/g, '\\:');
106
+ vf += `,drawtext=text='${escapedSub}':fontfile='${fontPath}':fontsize=36:fontcolor=${textColor}@0.7:x=(w-text_w)/2:y=(h-text_h)/2+40:alpha='if(lt(t\\,1.2)\\,0\\,if(lt(t\\,2)\\,(t-1.2)/0.8\\,if(gt(t\\,${duration - 0.8})\\,(${duration}-t)/0.8\\,1)))'`;
107
+ }
108
+ const args = [
109
+ '-y',
110
+ '-f', 'lavfi',
111
+ '-i', `color=c=${backgroundColor}:s=${width}x${height}:d=${duration}:r=${fps}`,
112
+ '-vf', vf,
113
+ '-c:v', 'libx264',
114
+ '-crf', '18',
115
+ '-pix_fmt', 'yuv420p',
116
+ '-movflags', '+faststart',
117
+ outputPath,
118
+ ];
119
+ await runFfmpeg(args);
120
+ logger.info(`Intro generated: ${outputPath}`);
121
+ return outputPath;
122
+ }
123
+ // ─── Helpers ────────────────────────────────────────────────────────
124
+ async function findFont() {
125
+ const candidates = [
126
+ '/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf',
127
+ '/usr/share/fonts/truetype/liberation/LiberationSans-Bold.ttf',
128
+ '/usr/share/fonts/truetype/ubuntu/Ubuntu-Bold.ttf',
129
+ '/usr/share/fonts/truetype/freefont/FreeSansBold.ttf',
130
+ ];
131
+ for (const f of candidates) {
132
+ if (fs.existsSync(f))
133
+ return f;
134
+ }
135
+ return '/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf';
136
+ }
137
+ function runFfmpeg(args) {
138
+ return new Promise((resolve, reject) => {
139
+ execFile('ffmpeg', args, { maxBuffer: 50 * 1024 * 1024 }, (error, stdout, stderr) => {
140
+ if (error) {
141
+ logger.error(`ffmpeg failed: ${stderr}`);
142
+ reject(new Error(`ffmpeg failed: ${stderr || error.message}`));
143
+ return;
144
+ }
145
+ resolve(stdout);
146
+ });
147
+ });
148
+ }
149
+ //# sourceMappingURL=concat.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"concat.js","sourceRoot":"","sources":["../../../src/tools/engine/concat.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Cursor simulation — injects a visible, smooth cursor into the page
3
+ * Uses Bézier curves for natural-looking mouse movement
4
+ */
5
+ import type { Page } from 'playwright';
6
+ import type { CursorConfig } from './types.js';
7
+ /**
8
+ * Inject a visible cursor overlay into the page
9
+ */
10
+ export declare function injectCursor(page: Page, config?: Partial<CursorConfig>): Promise<void>;
11
+ /**
12
+ * Move cursor to a specific position with smooth Bézier interpolation
13
+ */
14
+ export declare function moveCursor(page: Page, targetX: number, targetY: number, duration?: number, fps?: number): Promise<void>;
15
+ /**
16
+ * Move cursor to a CSS selector's center
17
+ */
18
+ export declare function moveCursorToElement(page: Page, selector: string, duration?: number, fps?: number): Promise<void>;
19
+ /**
20
+ * Animate a click effect at current cursor position
21
+ */
22
+ export declare function animateClick(page: Page): Promise<void>;
23
+ /**
24
+ * Hide cursor (move off-screen)
25
+ */
26
+ export declare function hideCursor(page: Page): Promise<void>;
@@ -0,0 +1,185 @@
1
+ /**
2
+ * Cursor simulation — injects a visible, smooth cursor into the page
3
+ * Uses Bézier curves for natural-looking mouse movement
4
+ */
5
+ const DEFAULT_CURSOR = {
6
+ enabled: true,
7
+ style: 'dot',
8
+ color: 'rgba(255, 255, 255, 0.9)',
9
+ size: 20,
10
+ clickAnimation: true,
11
+ };
12
+ /**
13
+ * Inject a visible cursor overlay into the page
14
+ */
15
+ export async function injectCursor(page, config = {}) {
16
+ const opts = { ...DEFAULT_CURSOR, ...config };
17
+ if (!opts.enabled)
18
+ return;
19
+ await page.evaluate(({ style, color, size, clickAnimation }) => {
20
+ // Create cursor element
21
+ const cursor = document.createElement('div');
22
+ cursor.id = '__cinema-cursor';
23
+ const isArrow = style === 'arrow' || style === 'pointer';
24
+ if (isArrow) {
25
+ // SVG arrow cursor
26
+ cursor.innerHTML = `<svg width="${size}" height="${size}" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
27
+ <path d="M5 3L19 12L12 13L9 20L5 3Z" fill="${color}" stroke="rgba(0,0,0,0.4)" stroke-width="1.5"/>
28
+ </svg>`;
29
+ Object.assign(cursor.style, {
30
+ position: 'fixed',
31
+ zIndex: '2147483647',
32
+ pointerEvents: 'none',
33
+ left: '-100px',
34
+ top: '-100px',
35
+ transition: 'none',
36
+ filter: 'drop-shadow(0 2px 4px rgba(0,0,0,0.3))',
37
+ });
38
+ }
39
+ else {
40
+ // Dot cursor
41
+ Object.assign(cursor.style, {
42
+ position: 'fixed',
43
+ zIndex: '2147483647',
44
+ pointerEvents: 'none',
45
+ width: `${size}px`,
46
+ height: `${size}px`,
47
+ borderRadius: '50%',
48
+ backgroundColor: color,
49
+ border: '2px solid rgba(0,0,0,0.2)',
50
+ boxShadow: '0 0 12px rgba(0,0,0,0.15), 0 2px 8px rgba(0,0,0,0.1)',
51
+ left: '-100px',
52
+ top: '-100px',
53
+ transform: 'translate(-50%, -50%)',
54
+ transition: 'none',
55
+ });
56
+ }
57
+ document.body.appendChild(cursor);
58
+ // Click animation ring
59
+ if (clickAnimation) {
60
+ const ring = document.createElement('div');
61
+ ring.id = '__cinema-cursor-ring';
62
+ Object.assign(ring.style, {
63
+ position: 'fixed',
64
+ zIndex: '2147483646',
65
+ pointerEvents: 'none',
66
+ width: `${size * 2.5}px`,
67
+ height: `${size * 2.5}px`,
68
+ borderRadius: '50%',
69
+ border: `2px solid ${color}`,
70
+ left: '-100px',
71
+ top: '-100px',
72
+ transform: 'translate(-50%, -50%) scale(0)',
73
+ opacity: '0',
74
+ transition: 'none',
75
+ });
76
+ document.body.appendChild(ring);
77
+ }
78
+ // Store config on window for later use
79
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
80
+ window.__cinemaCursorConfig = { style, color, size, clickAnimation };
81
+ }, opts);
82
+ }
83
+ /**
84
+ * Move cursor to a specific position with smooth Bézier interpolation
85
+ */
86
+ export async function moveCursor(page, targetX, targetY, duration = 800, fps = 60) {
87
+ const frames = Math.max(1, Math.ceil((duration / 1000) * fps));
88
+ // Get current cursor position
89
+ const startPos = await page.evaluate(() => {
90
+ const cursor = document.getElementById('__cinema-cursor');
91
+ if (!cursor)
92
+ return { x: 0, y: 0 };
93
+ return {
94
+ x: parseFloat(cursor.style.left) || 0,
95
+ y: parseFloat(cursor.style.top) || 0,
96
+ };
97
+ });
98
+ // Generate Bézier control points for natural movement
99
+ const dx = targetX - startPos.x;
100
+ const dy = targetY - startPos.y;
101
+ const cp1x = startPos.x + dx * 0.3 + (Math.random() - 0.5) * Math.abs(dx) * 0.2;
102
+ const cp1y = startPos.y + dy * 0.1 + (Math.random() - 0.5) * Math.abs(dy) * 0.3;
103
+ const cp2x = startPos.x + dx * 0.7 + (Math.random() - 0.5) * Math.abs(dx) * 0.15;
104
+ const cp2y = startPos.y + dy * 0.9 + (Math.random() - 0.5) * Math.abs(dy) * 0.2;
105
+ // Animate through Bézier curve
106
+ for (let i = 0; i <= frames; i++) {
107
+ const t = i / frames;
108
+ // Ease the t parameter for acceleration/deceleration
109
+ const et = t < 0.5 ? 2 * t * t : -1 + (4 - 2 * t) * t;
110
+ // Cubic Bézier
111
+ const mt = 1 - et;
112
+ const x = mt * mt * mt * startPos.x +
113
+ 3 * mt * mt * et * cp1x +
114
+ 3 * mt * et * et * cp2x +
115
+ et * et * et * targetX;
116
+ const y = mt * mt * mt * startPos.y +
117
+ 3 * mt * mt * et * cp1y +
118
+ 3 * mt * et * et * cp2y +
119
+ et * et * et * targetY;
120
+ await page.evaluate(({ x, y }) => {
121
+ const cursor = document.getElementById('__cinema-cursor');
122
+ if (cursor) {
123
+ cursor.style.left = `${x}px`;
124
+ cursor.style.top = `${y}px`;
125
+ }
126
+ }, { x: Math.round(x), y: Math.round(y) });
127
+ // Also move the actual mouse for hover effects
128
+ await page.mouse.move(Math.round(x), Math.round(y));
129
+ }
130
+ }
131
+ /**
132
+ * Move cursor to a CSS selector's center
133
+ */
134
+ export async function moveCursorToElement(page, selector, duration = 800, fps = 60) {
135
+ const pos = await page.evaluate((sel) => {
136
+ const el = document.querySelector(sel);
137
+ if (!el)
138
+ return null;
139
+ const rect = el.getBoundingClientRect();
140
+ return {
141
+ x: rect.left + rect.width / 2,
142
+ y: rect.top + rect.height / 2,
143
+ };
144
+ }, selector);
145
+ if (pos) {
146
+ await moveCursor(page, pos.x, pos.y, duration, fps);
147
+ }
148
+ }
149
+ /**
150
+ * Animate a click effect at current cursor position
151
+ */
152
+ export async function animateClick(page) {
153
+ await page.evaluate(() => {
154
+ const ring = document.getElementById('__cinema-cursor-ring');
155
+ const cursor = document.getElementById('__cinema-cursor');
156
+ if (!ring || !cursor)
157
+ return;
158
+ ring.style.left = cursor.style.left;
159
+ ring.style.top = cursor.style.top;
160
+ ring.style.transition = 'transform 0.4s ease-out, opacity 0.4s ease-out';
161
+ ring.style.transform = 'translate(-50%, -50%) scale(0)';
162
+ ring.style.opacity = '1';
163
+ // Trigger animation
164
+ requestAnimationFrame(() => {
165
+ ring.style.transform = 'translate(-50%, -50%) scale(1)';
166
+ ring.style.opacity = '0';
167
+ });
168
+ });
169
+ // Wait for animation
170
+ await new Promise((r) => setTimeout(r, 450));
171
+ }
172
+ /**
173
+ * Hide cursor (move off-screen)
174
+ */
175
+ export async function hideCursor(page) {
176
+ await page.evaluate(() => {
177
+ const cursor = document.getElementById('__cinema-cursor');
178
+ const ring = document.getElementById('__cinema-cursor-ring');
179
+ if (cursor)
180
+ cursor.style.left = '-100px';
181
+ if (ring)
182
+ ring.style.left = '-100px';
183
+ });
184
+ }
185
+ //# sourceMappingURL=cursor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cursor.js","sourceRoot":"","sources":["../../../src/tools/engine/cursor.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Cinema-grade easing functions for smooth scroll animations
3
+ * All functions: t ∈ [0,1] → output ∈ [0,1]
4
+ */
5
+ import type { EasingName } from './types.js';
6
+ declare const EASINGS: Record<EasingName, (t: number) => number>;
7
+ /**
8
+ * Get an easing function by name
9
+ */
10
+ export declare function getEasing(name: EasingName): (t: number) => number;
11
+ /**
12
+ * Apply easing to a progress value and map to a range
13
+ */
14
+ export declare function applyEasing(progress: number, totalDistance: number, easingName?: EasingName): number;
15
+ export { EASINGS };
@@ -0,0 +1,100 @@
1
+ /**
2
+ * Cinema-grade easing functions for smooth scroll animations
3
+ * All functions: t ∈ [0,1] → output ∈ [0,1]
4
+ */
5
+ // ─── Core Easing Functions ──────────────────────────────────────────
6
+ const linear = (t) => t;
7
+ const easeInQuad = (t) => t * t;
8
+ const easeOutQuad = (t) => t * (2 - t);
9
+ const easeInOutQuad = (t) => t < 0.5 ? 2 * t * t : -1 + (4 - 2 * t) * t;
10
+ const easeInCubic = (t) => t * t * t;
11
+ const easeOutCubic = (t) => (--t) * t * t + 1;
12
+ const easeInOutCubic = (t) => t < 0.5 ? 4 * t * t * t : 1 - Math.pow(-2 * t + 2, 3) / 2;
13
+ const easeInQuart = (t) => t * t * t * t;
14
+ const easeOutQuart = (t) => 1 - (--t) * t * t * t;
15
+ const easeInOutQuart = (t) => t < 0.5 ? 8 * t * t * t * t : 1 - Math.pow(-2 * t + 2, 4) / 2;
16
+ const easeInQuint = (t) => t * t * t * t * t;
17
+ const easeOutQuint = (t) => 1 + (--t) * t * t * t * t;
18
+ const easeInOutQuint = (t) => t < 0.5 ? 16 * t * t * t * t * t : 1 - Math.pow(-2 * t + 2, 5) / 2;
19
+ const easeInOutSine = (t) => -(Math.cos(Math.PI * t) - 1) / 2;
20
+ /**
21
+ * Cinematic easing: slow start (15%), smooth cruise (70%), slow end (15%)
22
+ * Uses quintic in/out for dramatic deceleration at edges
23
+ */
24
+ const cinematic = (t) => {
25
+ if (t < 0.15) {
26
+ // Slow ease-in (quintic)
27
+ const local = t / 0.15;
28
+ return 0.15 * (local * local * local);
29
+ }
30
+ else if (t > 0.85) {
31
+ // Slow ease-out (quintic)
32
+ const local = (t - 0.85) / 0.15;
33
+ return 0.85 + 0.15 * (1 - Math.pow(1 - local, 3));
34
+ }
35
+ else {
36
+ // Linear cruise in the middle
37
+ const local = (t - 0.15) / 0.70;
38
+ return 0.15 + 0.70 * local;
39
+ }
40
+ };
41
+ /**
42
+ * Showcase easing: dramatic slow start, buttery cruise, elegant deceleration
43
+ * Designed specifically for portfolio showcase videos
44
+ *
45
+ * Distribution: 25% slow start → 50% smooth cruise → 25% slow end
46
+ * The cruise section uses easeInOutSine for a gentle wave-like motion
47
+ * that never feels rushed — perfect for long pages
48
+ */
49
+ const showcase = (t) => {
50
+ if (t < 0.25) {
51
+ // Very slow ease-in (quintic for dramatic slowness)
52
+ const local = t / 0.25;
53
+ return 0.08 * easeInQuint(local);
54
+ }
55
+ else if (t > 0.75) {
56
+ // Very slow ease-out (quintic for elegant stop)
57
+ const local = (t - 0.75) / 0.25;
58
+ return 0.92 + 0.08 * easeOutQuint(local);
59
+ }
60
+ else {
61
+ // Smooth cruise in the middle — easeInOutSine for wave-like feel
62
+ const local = (t - 0.25) / 0.50;
63
+ return 0.08 + 0.84 * easeInOutSine(local);
64
+ }
65
+ };
66
+ // ─── Easing Registry ────────────────────────────────────────────────
67
+ const EASINGS = {
68
+ linear,
69
+ easeInQuad,
70
+ easeOutQuad,
71
+ easeInOutQuad,
72
+ easeInCubic,
73
+ easeOutCubic,
74
+ easeInOutCubic,
75
+ easeInQuart,
76
+ easeOutQuart,
77
+ easeInOutQuart,
78
+ easeInQuint,
79
+ easeOutQuint,
80
+ easeInOutQuint,
81
+ easeInOutSine,
82
+ cinematic,
83
+ showcase,
84
+ };
85
+ /**
86
+ * Get an easing function by name
87
+ */
88
+ export function getEasing(name) {
89
+ return EASINGS[name] ?? EASINGS.easeInOutCubic;
90
+ }
91
+ /**
92
+ * Apply easing to a progress value and map to a range
93
+ */
94
+ export function applyEasing(progress, totalDistance, easingName = 'easeInOutCubic') {
95
+ const easingFn = getEasing(easingName);
96
+ const clamped = Math.max(0, Math.min(1, progress));
97
+ return Math.round(easingFn(clamped) * totalDistance);
98
+ }
99
+ export { EASINGS };
100
+ //# sourceMappingURL=easing.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"easing.js","sourceRoot":"","sources":["../../../src/tools/engine/easing.ts"],"names":[],"mappings":"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"}