@norahe/remotion-workflow 0.1.0 → 0.1.2

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 (2) hide show
  1. package/package.json +1 -1
  2. package/src/cli.mjs +188 -2
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@norahe/remotion-workflow",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
4
4
  "type": "module",
5
5
  "description": "Workflow CLI for using Nora Remotion process in existing or new projects.",
6
6
  "bin": {
package/src/cli.mjs CHANGED
@@ -2,7 +2,7 @@
2
2
  /**
3
3
  * [INPUT]: process args, filesystem state, and optional create-video scaffolding.
4
4
  * [OUTPUT]: initialized workflow guide files in an existing or newly created project.
5
- * [POS]: CLI entrypoint for @nora/remotion-workflow package.
5
+ * [POS]: CLI entrypoint for @norahe/remotion-workflow package.
6
6
  * [PROTOCOL]: update this header when code changes, then check AGENTS.md
7
7
  */
8
8
 
@@ -41,6 +41,183 @@ const writeIfMissing = (filePath, content) => {
41
41
  return true;
42
42
  };
43
43
 
44
+ const upsertPackageJsonDependency = (projectDir, depName, version) => {
45
+ const packagePath = path.join(projectDir, 'package.json');
46
+ if (!fs.existsSync(packagePath)) return false;
47
+ const raw = fs.readFileSync(packagePath, 'utf8');
48
+ const pkg = JSON.parse(raw);
49
+ const inDeps = Boolean(pkg.dependencies?.[depName]);
50
+ const inDevDeps = Boolean(pkg.devDependencies?.[depName]);
51
+ if (inDeps || inDevDeps) return false;
52
+ pkg.dependencies = pkg.dependencies || {};
53
+ pkg.dependencies[depName] = version;
54
+ const sortedDeps = Object.keys(pkg.dependencies)
55
+ .sort((a, b) => a.localeCompare(b))
56
+ .reduce((acc, key) => {
57
+ acc[key] = pkg.dependencies[key];
58
+ return acc;
59
+ }, {});
60
+ pkg.dependencies = sortedDeps;
61
+ fs.writeFileSync(packagePath, `${JSON.stringify(pkg, null, 2)}\n`, 'utf8');
62
+ return true;
63
+ };
64
+
65
+ const buildStarterSchemaTs = () => {
66
+ return [
67
+ '/**',
68
+ ' * [INPUT]: zod runtime and Remotion props panel validation.',
69
+ ' * [OUTPUT]: starter schema for visual/audio/timing controls.',
70
+ ' * [POS]: Nora workflow starter schema for quick project bootstrapping.',
71
+ ' * [PROTOCOL]: update this header when code changes, then check AGENTS.md',
72
+ ' */',
73
+ '',
74
+ "import {z} from 'zod';",
75
+ '',
76
+ 'export const starterSchema = z.object({',
77
+ ' visual: z.object({',
78
+ ' headingScalePct: z.number().int().min(80).max(130),',
79
+ ' bodyScalePct: z.number().int().min(80).max(130),',
80
+ ' }),',
81
+ ' audio: z.object({',
82
+ ' enableVoiceover: z.boolean(),',
83
+ ' enableMusic: z.boolean(),',
84
+ ' musicVolumePct: z.number().int().min(0).max(100),',
85
+ ' voiceVolumePct: z.number().int().min(0).max(200),',
86
+ ' }),',
87
+ ' timing: z.object({',
88
+ ' totalFrames: z.number().int().min(300).max(3600),',
89
+ ' scene1Frames: z.number().int().min(60).max(1800),',
90
+ ' }),',
91
+ '});',
92
+ '',
93
+ 'export type StarterProps = z.infer<typeof starterSchema>;',
94
+ ].join('\n');
95
+ };
96
+
97
+ const buildStarterDefaultPropsTs = () => {
98
+ return [
99
+ '/**',
100
+ ' * [INPUT]: StarterProps type from starter schema.',
101
+ ' * [OUTPUT]: default props values for the starter composition.',
102
+ ' * [POS]: single source of truth for initial right-panel values.',
103
+ ' * [PROTOCOL]: update this header when code changes, then check AGENTS.md',
104
+ ' */',
105
+ '',
106
+ "import type {StarterProps} from './schema';",
107
+ '',
108
+ 'export const starterDefaultProps: StarterProps = {',
109
+ ' visual: {',
110
+ ' headingScalePct: 100,',
111
+ ' bodyScalePct: 100,',
112
+ ' },',
113
+ ' audio: {',
114
+ ' enableVoiceover: true,',
115
+ ' enableMusic: true,',
116
+ ' musicVolumePct: 20,',
117
+ ' voiceVolumePct: 100,',
118
+ ' },',
119
+ ' timing: {',
120
+ ' totalFrames: 1800,',
121
+ ' scene1Frames: 360,',
122
+ ' },',
123
+ '};',
124
+ ].join('\n');
125
+ };
126
+
127
+ const buildStarterCompositionTsx = () => {
128
+ return [
129
+ '/**',
130
+ ' * [INPUT]: starter props from schema/defaultProps.',
131
+ ' * [OUTPUT]: minimal, editable scene proving controls are wired.',
132
+ ' * [POS]: first-touch composition for non-technical users in Studio.',
133
+ ' * [PROTOCOL]: update this header when code changes, then check AGENTS.md',
134
+ ' */',
135
+ '',
136
+ "import React from 'react';",
137
+ "import {AbsoluteFill, useCurrentFrame, interpolate} from 'remotion';",
138
+ "import type {StarterProps} from './schema';",
139
+ '',
140
+ 'export const NoraWorkflowStarter: React.FC<StarterProps> = (props) => {',
141
+ ' const frame = useCurrentFrame();',
142
+ ' const fade = interpolate(frame, [0, 20], [0, 1], {extrapolateRight: "clamp"});',
143
+ ' const headingSize = 78 * (props.visual.headingScalePct / 100);',
144
+ ' const bodySize = 34 * (props.visual.bodyScalePct / 100);',
145
+ '',
146
+ ' return (',
147
+ ' <AbsoluteFill',
148
+ ' style={{',
149
+ " background: 'radial-gradient(circle at 25% 20%, rgba(32,114,255,0.20), transparent 35%), #040c1d',",
150
+ " color: '#f4f7ff',",
151
+ " fontFamily: 'Inter, system-ui, sans-serif',",
152
+ " opacity: fade,",
153
+ " justifyContent: 'center',",
154
+ " alignItems: 'center',",
155
+ " textAlign: 'center',",
156
+ ' }}',
157
+ ' >',
158
+ ' <div style={{padding: "0 120px"}}>',
159
+ ' <h1 style={{fontSize: headingSize, margin: 0, lineHeight: 1.1}}>Nora Workflow Starter</h1>',
160
+ ' <p style={{fontSize: bodySize, marginTop: 24, opacity: 0.88}}>',
161
+ ' Edit props on the right panel: visual / audio / timing. This is your default schema.',
162
+ ' </p>',
163
+ ' </div>',
164
+ ' </AbsoluteFill>',
165
+ ' );',
166
+ '};',
167
+ ].join('\n');
168
+ };
169
+
170
+ const ensureStarterFiles = (projectDir) => {
171
+ const srcDir = path.join(projectDir, 'src');
172
+ const starterDir = path.join(srcDir, 'NoraWorkflow');
173
+ ensureDir(starterDir);
174
+ writeIfMissing(path.join(starterDir, 'schema.ts'), `${buildStarterSchemaTs()}\n`);
175
+ writeIfMissing(path.join(starterDir, 'defaultProps.ts'), `${buildStarterDefaultPropsTs()}\n`);
176
+ writeIfMissing(path.join(starterDir, 'StarterComposition.tsx'), `${buildStarterCompositionTsx()}\n`);
177
+ };
178
+
179
+ const ensureStarterInRoot = (projectDir) => {
180
+ const rootTsxPath = path.join(projectDir, 'src', 'Root.tsx');
181
+ if (!fs.existsSync(rootTsxPath)) return false;
182
+ const raw = fs.readFileSync(rootTsxPath, 'utf8');
183
+ if (raw.includes('NoraWorkflowStarter')) return false;
184
+
185
+ const importLines = [
186
+ "import {NoraWorkflowStarter} from './NoraWorkflow/StarterComposition';",
187
+ "import {starterSchema} from './NoraWorkflow/schema';",
188
+ "import {starterDefaultProps} from './NoraWorkflow/defaultProps';",
189
+ ];
190
+ let next = raw;
191
+
192
+ for (const line of importLines) {
193
+ if (!next.includes(line)) {
194
+ next = `${line}\n${next}`;
195
+ }
196
+ }
197
+
198
+ const compositionBlock = [
199
+ ' <Composition',
200
+ ' id="NoraWorkflow-Starter"',
201
+ ' component={NoraWorkflowStarter}',
202
+ ' durationInFrames={starterDefaultProps.timing.totalFrames}',
203
+ ' fps={30}',
204
+ ' width={1920}',
205
+ ' height={1080}',
206
+ ' schema={starterSchema}',
207
+ ' defaultProps={starterDefaultProps}',
208
+ ' />',
209
+ ].join('\n');
210
+
211
+ if (next.includes('</>')) {
212
+ next = next.replace('</>', `${compositionBlock}\n </>`);
213
+ } else {
214
+ return false;
215
+ }
216
+
217
+ fs.writeFileSync(rootTsxPath, next, 'utf8');
218
+ return true;
219
+ };
220
+
44
221
  const isRemotionProject = (cwd) => {
45
222
  const packagePath = path.join(cwd, 'package.json');
46
223
  const remotionConfigPathTs = path.join(cwd, 'remotion.config.ts');
@@ -80,7 +257,7 @@ const buildGuide = (projectName) => {
80
257
  `Project: ${projectName}`,
81
258
  '',
82
259
  '1) Prompt selection',
83
- '- Open prompt browser: https://github.com/norahe0304-art/remotion-platform (see prompts app docs)',
260
+ '- Open prompt browser: https://prompts-mauve.vercel.app/app/',
84
261
  '- Copy either `Copy AI Template` (prompt-based) or `Copy Input Guide` (blank from scratch).',
85
262
  '',
86
263
  '2) Input location',
@@ -134,5 +311,14 @@ if (command !== 'init') {
134
311
  const projectName = getArgValue('--project-name');
135
312
  const cwd = process.cwd();
136
313
  const projectDir = createProjectIfNeeded(cwd, projectName);
314
+ ensureStarterFiles(projectDir);
315
+ const rootUpdated = ensureStarterInRoot(projectDir);
316
+ const zodAdded = upsertPackageJsonDependency(projectDir, 'zod', '^3.25.76');
137
317
  ensureWorkflowFiles(projectDir);
318
+ if (rootUpdated) {
319
+ console.log('[workflow] Added NoraWorkflow starter composition to src/Root.tsx');
320
+ }
321
+ if (zodAdded) {
322
+ console.log('[workflow] Added dependency: zod');
323
+ }
138
324
  console.log(`[workflow] Done. Use project: ${projectDir}`);