@devinilabs/reelstack 1.3.2 → 1.4.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.
package/cli/scaffold.js CHANGED
@@ -1,8 +1,20 @@
1
1
  /**
2
- * `reelstack scaffold` — copies a family template into the buyer's
3
- * src/<Name>Reel.tsx, substitutes preset metadata (palette, BEATs,
4
- * frame count, copy), and patches src/Root.tsx to register the new
5
- * composition.
2
+ * `reelstack scaffold` — produces a production-quality reel scaffold by
3
+ * preferring the reference reel (cloned from a real Devini Labs production
4
+ * reel) over the generic family template. Substitutes preset metadata,
5
+ * does a mechanical export rename, strips the reference header, and
6
+ * patches src/Root.tsx to register the new composition.
7
+ *
8
+ * v1.4 flow:
9
+ * 1. Resolve reference reel for family + preset.
10
+ * 2. If reference exists (and --minimal not passed):
11
+ * - Read reference verbatim (2000-3800 lines of production motion).
12
+ * - Strip the reference header JSDoc.
13
+ * - Rename `export const <PresetClass>Reel` → `export const <Name>Reel`.
14
+ * - Inject a /** SCAFFOLDED FROM: <ref> *\/ marker.
15
+ * 3. Else fall back to generic template + placeholder substitution.
16
+ * 4. Write to src/<Name>Reel.tsx.
17
+ * 5. Register in src/Root.tsx.
6
18
  */
7
19
  const fs = require("node:fs");
8
20
  const path = require("node:path");
@@ -127,6 +139,77 @@ function injectReferenceComment(tsx, refPath) {
127
139
  return tsx.slice(0, after) + block + tsx.slice(after);
128
140
  }
129
141
 
142
+ /**
143
+ * v1.4: Strip the reference reel's leading JSDoc header block. References
144
+ * start with `/** REFERENCE — <ClassName> (Family: <fam>) ... *\/`.
145
+ * When the reference becomes a buyer's reel, that header doesn't apply.
146
+ */
147
+ function stripReferenceHeader(tsx) {
148
+ if (!tsx.startsWith("/**")) return tsx;
149
+ if (!/^\/\*\*\s*\n\s*\*\s*REFERENCE\b/.test(tsx)) return tsx;
150
+ const end = tsx.indexOf("*/");
151
+ if (end === -1) return tsx;
152
+ // Skip the closing `*/` plus any trailing newline.
153
+ let cut = end + 2;
154
+ while (tsx[cut] === "\n") cut++;
155
+ return tsx.slice(cut);
156
+ }
157
+
158
+ /**
159
+ * v1.4: Mechanical export rename — replace `export const <X>Reel` with
160
+ * `export const <NewName>Reel`. The reference reel has the production
161
+ * class name (e.g. GraphifyReel); we rewrite it to the buyer's chosen
162
+ * name (e.g. MyLaunchReel). Returns the rewritten source and the original
163
+ * class name (so we can announce what changed in the scaffold summary).
164
+ */
165
+ function renameReelExport(tsx, newName) {
166
+ const m = tsx.match(/export\s+const\s+(\w+Reel)\b/);
167
+ if (!m) return { tsx, originalClassName: null };
168
+ const originalClassName = m[1];
169
+ const targetClassName = `${newName}Reel`;
170
+ if (originalClassName === targetClassName) {
171
+ return { tsx, originalClassName };
172
+ }
173
+ // Replace ALL occurrences of the original class name with the new one.
174
+ // This catches the export, any internal references (helper components
175
+ // named after the reel, JSDoc, comments), and keeps the file coherent.
176
+ const re = new RegExp(`\\b${originalClassName}\\b`, "g");
177
+ return {
178
+ tsx: tsx.replace(re, targetClassName),
179
+ originalClassName,
180
+ };
181
+ }
182
+
183
+ /**
184
+ * v1.4: Inject a top-of-file marker so the buyer (and Claude on subsequent
185
+ * iteration) knows this file was scaffolded from a reference reel and the
186
+ * canonical hooks/CTAs are Devini Labs strings that should be replaced.
187
+ */
188
+ function injectScaffoldHeader({ name, family, preset, refPath, originalClassName }) {
189
+ const rel = path.relative(REELSTACK_PKG, refPath) || refPath;
190
+ return `/**
191
+ * ${name}Reel — scaffolded by ReelStack v1.4
192
+ *
193
+ * Family: ${family}
194
+ * Preset: ${preset}
195
+ * Source: ${rel}
196
+ * Cloned at: ${new Date().toISOString()}
197
+ *
198
+ * This file is a faithful clone of the production ${originalClassName || preset + "Reel"}
199
+ * — same motion vocabulary, palette, and scene structure. Canonical Devini
200
+ * Labs hook / sub / CTA strings are still in place. REPLACE them with your
201
+ * own content before publishing. The motion floor (≥4 layers in opener,
202
+ * ≥3 in anchor scenes) is preserved.
203
+ *
204
+ * Next steps:
205
+ * 1. Replace the hero hook, sub, and CTA copy with your reel's narrative.
206
+ * 2. Run \`/reelstack-beats <vo.wav>\` to lock motion to your voiceover.
207
+ * 3. Look for REFERENCE-STRIP markers and swap in your assets.
208
+ * 4. Run \`/reelstack-lint src/${name}Reel.tsx\` and address any errors.
209
+ */
210
+ `;
211
+ }
212
+
130
213
  function patchRoot(rootPath, name, durationFrames) {
131
214
  if (!fs.existsSync(rootPath)) {
132
215
  fail(`src/Root.tsx not found at ${rootPath}. Skipping registration.`);
@@ -193,15 +276,35 @@ async function run(argv, opts = {}) {
193
276
  }
194
277
 
195
278
  const presetMeta = loadPreset(family, preset);
196
- const template = loadTemplate(family);
197
- let tsx = substitute(template, presetMeta, name);
198
-
199
- // v1.1.1+ — resolve the reference reel for this preset (or buyer's override
200
- // via --reference=<preset>) so a `/** REFERENCE: <path> */` comment can be
201
- // injected into the scaffold, and a hint surfaced after Studio.
202
279
  const ref = resolveReference(family, preset, args.reference);
203
- if (ref) {
204
- tsx = injectReferenceComment(tsx, ref.path);
280
+
281
+ // v1.4 flow: prefer reference reel (production-grade DNA) over the generic
282
+ // template. Buyer can opt into the generic template via --minimal.
283
+ let tsx;
284
+ let scaffoldedFromReference = false;
285
+ let originalClassName = null;
286
+
287
+ if (ref && !args.minimal) {
288
+ let refSrc = fs.readFileSync(ref.path, "utf8");
289
+ refSrc = stripReferenceHeader(refSrc);
290
+ const renamed = renameReelExport(refSrc, name);
291
+ refSrc = renamed.tsx;
292
+ originalClassName = renamed.originalClassName;
293
+ const header = injectScaffoldHeader({
294
+ name,
295
+ family,
296
+ preset,
297
+ refPath: ref.path,
298
+ originalClassName,
299
+ });
300
+ tsx = header + refSrc;
301
+ scaffoldedFromReference = true;
302
+ } else {
303
+ const template = loadTemplate(family);
304
+ tsx = substitute(template, presetMeta, name);
305
+ if (ref) {
306
+ tsx = injectReferenceComment(tsx, ref.path);
307
+ }
205
308
  }
206
309
 
207
310
  const outFile = path.join(cwd, "src", `${name}Reel.tsx`);
@@ -225,13 +328,18 @@ async function run(argv, opts = {}) {
225
328
  console.log(c.gray(` Family: `) + family);
226
329
  console.log(c.gray(` Preset: `) + `${preset} (from ${presetMeta.source})`);
227
330
  console.log(c.gray(` Frame count: `) + `${presetMeta.durationFrames} (${(presetMeta.durationFrames / 30).toFixed(1)}s @ 30fps)`);
331
+ console.log(c.gray(` Source: `) + (scaffoldedFromReference
332
+ ? `reference reel (${path.basename(ref.path)})${originalClassName ? ` — renamed ${originalClassName} → ${name}Reel` : ""}`
333
+ : `generic template`));
228
334
  console.log(c.gray(` Output: `) + path.relative(cwd, outFile));
229
335
  console.log("");
230
336
  console.log(c.cyan("→ ") + `Open Remotion Studio with ${c.bold("npm run dev")} and select "${name}Reel" from the comp list.`);
231
- if (ref) {
232
- console.log(c.cyan("ℹ ") + `Reference reel available: ${ref.path}`);
233
- console.log(c.gray(" Claude will study this reel when iterating on your scaffold. Open it"));
234
- console.log(c.gray(" yourself to learn the family's motion vocabulary."));
337
+ if (scaffoldedFromReference) {
338
+ console.log(c.cyan("ℹ ") + `Scaffolded from production reference. Canonical Devini Labs hook/sub/CTA copy is in the file —`);
339
+ console.log(c.gray(" replace those strings with your own narrative before publishing. The motion, palette, and scene"));
340
+ console.log(c.gray(" structure are production-grade and preserved verbatim."));
341
+ } else if (ref) {
342
+ console.log(c.cyan("ℹ ") + `Reference reel available for study: ${ref.path}`);
235
343
  }
236
344
  console.log("");
237
345
  }
@@ -145,7 +145,7 @@ Common moves:
145
145
  - **Pull a brand logo.** Run `/reelstack-icons logos:react` (or any Iconify ID).
146
146
  - **Capture a product UI.** Run `/reelstack-capture https://example.com`.
147
147
 
148
- **Reference reels.** ReelStack ships 12 reference reels at `~/.reelstack/reference/<family>/<preset>.tsx`. After scaffolding, your AI agent (Claude Code, etc.) can study these to give better iteration advice. To force a specific reference, pass `--reference=<preset>` to the scaffold command. See `~/.reelstack/docs/design-discipline.md` for the full catalog.
148
+ **Reference reels.** As of v1.4, ReelStack ships **22 reference reels** at `~/.reelstack/reference/<family>/<preset>.tsx` one per preset. The scaffolder copies the matching reference directly into your `src/<Name>Reel.tsx`, so every scaffold is production-grade by default. To force a specific reference (e.g., use ClaudeWatchReel's structure even when scaffolding Graphify), pass `--reference=<preset>`. To opt into the legacy thin-template flow, pass `--minimal`. See `~/.reelstack/docs/design-discipline.md` for the full catalog.
149
149
 
150
150
  ## 7. Validate before posting
151
151
 
@@ -84,7 +84,7 @@ Dark Cinematic inherits from **leonxlnx/taste-skill-brutalist** (Tactical Teleme
84
84
  - **WCAG AA+ contrast** documented inline in `palette.ts` (fg: 18.7:1, fgSoft: 13.4:1, fgMuted: 6.4:1).
85
85
  - **Optional hazard-red `#E61919` accent** — pull from the Brutalist tactical-telemetry palette for "hold / abort / kill switch" callouts.
86
86
 
87
- ## Reference reels (v1.1.1+)
87
+ ## Reference reels (v1.4+ — every preset has one)
88
88
 
89
89
  3 reference reels ship for this family at `~/.reelstack/reference/dark/<preset>.tsx`:
90
90
 
@@ -69,7 +69,7 @@ Forbidden inherits from **leonxlnx/taste-skill-brutalist** (Swiss Industrial Pri
69
69
  - **WCAG AA+ contrast** documented inline in `palette.ts` (ink: 17.6:1, inkSoft: 13.0:1, inkMuted: 6.8:1).
70
70
  - **No-border-radius variant** — pass `radius={0}` to `<GlassCard />` for the Brutalist Swiss "rigid grid" look.
71
71
 
72
- ## Reference reels (v1.1.1+)
72
+ ## Reference reels (v1.4+ — every preset has one)
73
73
 
74
74
  1 reference reel ships for this family at `~/.reelstack/reference/forbidden/<preset>.tsx`:
75
75
 
@@ -87,7 +87,7 @@ Glass Iridescent inherits from **leonxlnx/taste-skill-soft** + **leonxlnx/taste-
87
87
  - **`ALLOWED_ACCENTS`** export — lint flags any color outside `[iriCyan, iriViolet, iriRose, iriGold, violetPill, tealPill, ink]` as a hue bleed.
88
88
  - **WCAG AA+ contrast** documented inline in `palette.ts` (ink: 16.8:1, inkSoft: 12.4:1, etc.).
89
89
 
90
- ## Reference reels (v1.1.1+)
90
+ ## Reference reels (v1.4+ — every preset has one)
91
91
 
92
92
  3 reference reels ship for this family at `~/.reelstack/reference/glass/<preset>.tsx`:
93
93
 
@@ -71,7 +71,7 @@ Cream Paper inherits from **leonxlnx/taste-skill-minimalist** + **leonxlnx/taste
71
71
  - **WCAG AA+ contrast** documented inline in `palette.ts` (heading: 15.6:1, body: 11.6:1, muted: 4.8:1).
72
72
  - **4-px grid constants** re-exported from the family entrypoint: `import { palette, GRID, GRID_2, GRID_4, … } from "@devinilabs/reelstack/families/paper"`.
73
73
 
74
- ## Reference reels (v1.1.1+)
74
+ ## Reference reels (v1.4+ — every preset has one)
75
75
 
76
76
  3 reference reels ship for this family at `~/.reelstack/reference/paper/<preset>.tsx`:
77
77
 
@@ -76,7 +76,7 @@ Warm Signature inherits from **leonxlnx/taste-skill-minimalist** + **leonxlnx/ta
76
76
  - **WCAG AA+ contrast** documented inline in `palette.ts` (fg: 19.6:1, fgSoft: 13.9:1, fgMuted: 7.7:1).
77
77
  - **Diffused warm shadow stack** — Soft variant's multi-layer `paperShadow`-style stacks adapted to warm-zinc backgrounds for premium card depth.
78
78
 
79
- ## Reference reels (v1.1.1+)
79
+ ## Reference reels (v1.4+ — every preset has one)
80
80
 
81
81
  2 reference reels ship for this family at `~/.reelstack/reference/warm/<preset>.tsx`:
82
82
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@devinilabs/reelstack",
3
- "version": "1.3.2",
3
+ "version": "1.4.1",
4
4
  "description": "Premium 9:16 Reel OS for Remotion. 5 cinematic style families, 22 production-tested presets, audio-locked motion, IG-safe by default. v1.1+ bakes in leonxlnx/taste-skill design discipline + huashu-design productivity patterns.",
5
5
  "keywords": [
6
6
  "remotion",