@devinilabs/reelstack 1.3.0 → 1.3.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.
package/cli/lint.js CHANGED
@@ -2,14 +2,14 @@
2
2
  * `reelstack lint <file> [--critique]` — static-analysis pass over a reel composition.
3
3
  *
4
4
  * Reports (default mode — list of violations with line numbers):
5
- * - SAFE_ZONE_BREACH: element y-coords overlap reserved bands.
6
- * - MOTION_FLOOR_VIOLATION: scenes with too few simultaneous animation layers.
7
- * - HERO_TEXT_OVERFLOW: counter or hero text wider than canvas - 64px gutter.
5
+ * - SAFE_ZONE_BREACH: (warn) element y-coords overlap reserved bands — decorative elements OK with data-reelstack-safe-override.
6
+ * - MOTION_FLOOR_VIOLATION: (warn) scenes with too few simultaneous animation layers — nested-motion components produce false positives.
7
+ * - HERO_TEXT_OVERFLOW: (warn) counter or hero text wider than canvas - 64px gutter.
8
8
  * - AUDIO_NOT_LOCKED: file imports <Audio> but defines no BEAT const block.
9
- * - HAND_DRAWN_BRAND: inline <svg> longer than 800 chars in a logo-sized element.
9
+ * - HAND_DRAWN_BRAND: (warn) inline <svg> longer than 800 chars intentional brand SVGs are fine.
10
10
  * - ACCENT_ALLOWLIST_VIOLATION: (warn) hex color outside the resolved family's ALLOWED_ACCENTS list — intentional sponsor/custom accents are fine, the rule catches accidental token typos.
11
- * - SPACING_NON_GRID: padding/margin/gap not on the 4px grid.
12
- * - MISSING_REDUCE_MOTION: component imports useCurrentFrame but lacks a `reduceMotion` prop.
11
+ * - SPACING_NON_GRID: (warn) padding/margin/gap not on the 4px grid — optical-spacing exceptions OK.
12
+ * - MISSING_REDUCE_MOTION: (warn) component imports useCurrentFrame but lacks a `reduceMotion` prop — accessibility hygiene, not render-blocking.
13
13
  * - GENERIC_PLACEHOLDER: "Lorem ipsum", "John Doe", "Acme Corp" — leftover stub copy.
14
14
  * - HOOK_LATENCY: earliest <Sequence> starts after frame 30 — first second of reel is empty.
15
15
  * - CTA_MISSING: (warn) no CTA component or CTA keyword (follow/subscribe/etc.) found anywhere.
@@ -86,14 +86,14 @@ function lint(file) {
86
86
  if (topMatch) {
87
87
  const y = Number(topMatch[1]);
88
88
  if (y >= 0 && y < TOP_SAFE && !line.includes("data-reelstack-safe-override")) {
89
- violations.push({ line: i + 1, code: "SAFE_ZONE_BREACH", msg: `top: ${y} overlaps top reserved band (0..${TOP_SAFE}).` });
89
+ violations.push({ line: i + 1, code: "SAFE_ZONE_BREACH", severity: "warning", msg: `top: ${y} overlaps top reserved band (0..${TOP_SAFE}). Add data-reelstack-safe-override if decorative.` });
90
90
  }
91
91
  }
92
92
  const bottomMatch = line.match(/bottom:\s*(-?\d+)\s*,?/);
93
93
  if (bottomMatch) {
94
94
  const b = Number(bottomMatch[1]);
95
95
  if (b >= 0 && b < BOTTOM_SAFE && !line.includes("data-reelstack-safe-override")) {
96
- violations.push({ line: i + 1, code: "SAFE_ZONE_BREACH", msg: `bottom: ${b} overlaps bottom reserved band (0..${BOTTOM_SAFE}).` });
96
+ violations.push({ line: i + 1, code: "SAFE_ZONE_BREACH", severity: "warning", msg: `bottom: ${b} overlaps bottom reserved band (0..${BOTTOM_SAFE}). Add data-reelstack-safe-override if decorative.` });
97
97
  }
98
98
  }
99
99
  });
@@ -114,7 +114,8 @@ function lint(file) {
114
114
  violations.push({
115
115
  line: lineNum,
116
116
  code: "HAND_DRAWN_BRAND",
117
- msg: `Inline <svg> is ${svg.length} chars — likely hand-drawn. Use /reelstack-icons to pull a real brand SVG.`,
117
+ severity: "warning",
118
+ msg: `Inline <svg> is ${svg.length} chars — could be hand-drawn. For brand marks, use /reelstack-icons; intentional inline brand SVGs are fine.`,
118
119
  });
119
120
  }
120
121
  }
@@ -197,7 +198,8 @@ function lint(file) {
197
198
  violations.push({
198
199
  line: lineOf(openIdx),
199
200
  code: "MOTION_FLOOR_VIOLATION",
200
- msg: `Sequence at frame ${fromValue} has ${motionCalls} motion calls, family rule is ≥3 (≥4 in opener).`,
201
+ severity: "warning",
202
+ msg: `Sequence at frame ${fromValue} has ${motionCalls} motion calls, family rule is ≥3 (≥4 in opener). If motion lives in a nested component, this warning is a false positive.`,
201
203
  });
202
204
  }
203
205
 
@@ -219,7 +221,8 @@ function lint(file) {
219
221
  violations.push({
220
222
  line: i + 1,
221
223
  code: "HERO_TEXT_OVERFLOW",
222
- msg: `fontSize ${px}px on apparent hero-counter; cap at 256px or wrap with maxWidth=${HERO_TEXT_MAX}.`,
224
+ severity: "warning",
225
+ msg: `fontSize ${px}px on apparent hero-counter; verify it fits within ${HERO_TEXT_MAX}px (canvas - 64px gutter) on real devices.`,
223
226
  });
224
227
  }
225
228
  });
@@ -265,7 +268,8 @@ function lint(file) {
265
268
  violations.push({
266
269
  line: lineOfIdx(idx),
267
270
  code: "SPACING_NON_GRID",
268
- msg: `${rawProp}: ${val} is off the 4px grid. Round to ${Math.round(val / 4) * 4}.`,
271
+ severity: "warning",
272
+ msg: `${rawProp}: ${val} is off the 4px grid. Round to ${Math.round(val / 4) * 4} — or accept the warning if this is intentional optical spacing.`,
269
273
  });
270
274
  }
271
275
  };
@@ -309,7 +313,8 @@ function lint(file) {
309
313
  violations.push({
310
314
  line: lineOfIdx(mFC.index),
311
315
  code: "MISSING_REDUCE_MOTION",
312
- msg: `React.FC component uses useCurrentFrame but prop type lacks "reduceMotion". Add reduceMotion?: boolean for accessibility.`,
316
+ severity: "warning",
317
+ msg: `React.FC component uses useCurrentFrame but prop type lacks "reduceMotion". Add reduceMotion?: boolean for accessibility (warning — not render-blocking).`,
313
318
  });
314
319
  }
315
320
  }
@@ -317,7 +322,8 @@ function lint(file) {
317
322
  violations.push({
318
323
  line: 1,
319
324
  code: "MISSING_REDUCE_MOTION",
320
- msg: `Component uses useCurrentFrame but no reduceMotion prop found anywhere. Add reduceMotion?: boolean.`,
325
+ severity: "warning",
326
+ msg: `Component uses useCurrentFrame but no reduceMotion prop found anywhere. Add reduceMotion?: boolean for accessibility (warning — not render-blocking).`,
321
327
  });
322
328
  }
323
329
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@devinilabs/reelstack",
3
- "version": "1.3.0",
3
+ "version": "1.3.2",
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",
@@ -2424,7 +2424,7 @@ export const ClaudeDispatchReel: React.FC = () => {
2424
2424
 
2425
2425
  return (
2426
2426
  <AbsoluteFill style={{ backgroundColor: C.bg, fontFamily: FONT }}>
2427
- {/* REFERENCE-STRIP: <Audio> tag removed — bring your own voiceover */}
2427
+ {/* REFERENCE-STRIP: voiceover tag removed — bring your own voiceover */}
2428
2428
 
2429
2429
  <Background frame={frame} fps={fps} />
2430
2430
 
@@ -2309,7 +2309,7 @@ const SceneRouter: React.FC = () => {
2309
2309
  export const NotebookLMReel: React.FC = () => {
2310
2310
  return (
2311
2311
  <AbsoluteFill style={{ background: C.bg }}>
2312
- {/* REFERENCE-STRIP: <Audio> tag removed — bring your own voiceover */}
2312
+ {/* REFERENCE-STRIP: voiceover tag removed — bring your own voiceover */}
2313
2313
  <SceneRouter />
2314
2314
  </AbsoluteFill>
2315
2315
  );
@@ -3023,7 +3023,7 @@ export const StitchReel: React.FC = () => {
3023
3023
 
3024
3024
  return (
3025
3025
  <AbsoluteFill style={{ backgroundColor: C.bg, fontFamily: FONT }}>
3026
- {/* REFERENCE-STRIP: <Audio> tag removed — bring your own voiceover */}
3026
+ {/* REFERENCE-STRIP: voiceover tag removed — bring your own voiceover */}
3027
3027
 
3028
3028
  <Background frame={frame} fps={fps} />
3029
3029
 
@@ -17,7 +17,6 @@
17
17
  import React from "react";
18
18
  import {
19
19
  AbsoluteFill,
20
- Audio,
21
20
  Easing,
22
21
  Img,
23
22
  OffthreadVideo,
@@ -2630,7 +2629,7 @@ export const HereticReel: React.FC = () => {
2630
2629
  {frame >= HBEAT.stars && frame < HBEAT.cta && <StarsScene />}
2631
2630
  {frame >= HBEAT.cta && frame < HBEAT.end && <CtaScene />}
2632
2631
 
2633
- {/* REFERENCE-STRIP: <Audio> tag removed — bring your own voiceover */}
2632
+ {/* REFERENCE-STRIP: voiceover tag removed — bring your own voiceover */}
2634
2633
  </AbsoluteFill>
2635
2634
  );
2636
2635
  };
@@ -17,7 +17,6 @@
17
17
  import React from "react";
18
18
  import {
19
19
  AbsoluteFill,
20
- Audio,
21
20
  Easing,
22
21
  Img,
23
22
  OffthreadVideo,
@@ -818,9 +817,10 @@ const HookScene: React.FC = () => {
818
817
  top: "55%",
819
818
  left: -2,
820
819
  height: 4,
820
+ width: "calc(100% + 4px)",
821
821
  background: C.crimson,
822
- width: `calc(${strikeWidth}% + 4px)`,
823
- transform: "translateY(-50%) rotate(-2deg)",
822
+ transform: `translateY(-50%) rotate(-2deg) scaleX(${strikeWidth / 100})`,
823
+ transformOrigin: "left center",
824
824
  boxShadow: `0 0 12px ${C.crimson}`,
825
825
  borderRadius: 2,
826
826
  }}
@@ -3777,7 +3777,7 @@ export const ClaudeWatchReel: React.FC<{ embedded?: boolean }> = ({
3777
3777
  fontFamily: FONT,
3778
3778
  }}
3779
3779
  >
3780
- {!embedded && {/* REFERENCE-STRIP: <Audio> tag removed — bring your own voiceover */}}
3780
+ {!embedded && {/* REFERENCE-STRIP: voiceover tag removed — bring your own voiceover */}}
3781
3781
 
3782
3782
  {/* Perpetual atmosphere — runs all scenes (skipped when embedded; host provides bg+caustics) */}
3783
3783
  {!embedded && <CausticBlobs />}
@@ -17,7 +17,6 @@
17
17
  import React from "react";
18
18
  import {
19
19
  AbsoluteFill,
20
- Audio,
21
20
  Easing,
22
21
  Img,
23
22
  OffthreadVideo,
@@ -2412,7 +2411,7 @@ export const GraphifyReel: React.FC = () => {
2412
2411
  </div>
2413
2412
 
2414
2413
  {/* Audio */}
2415
- {/* REFERENCE-STRIP: <Audio> tag removed — bring your own voiceover */}
2414
+ {/* REFERENCE-STRIP: voiceover tag removed — bring your own voiceover */}
2416
2415
  </AbsoluteFill>
2417
2416
  );
2418
2417
  };
@@ -17,7 +17,6 @@
17
17
  import React from "react";
18
18
  import {
19
19
  AbsoluteFill,
20
- Audio,
21
20
  Img,
22
21
  OffthreadVideo,
23
22
  Sequence,
@@ -2184,7 +2183,7 @@ export const PaperclipReel: React.FC = () => {
2184
2183
  }}
2185
2184
  >
2186
2185
  {/* Voiceover — single Audio at root, no Sequence wrap */}
2187
- {/* REFERENCE-STRIP: <Audio> tag removed — bring your own voiceover */}
2186
+ {/* REFERENCE-STRIP: voiceover tag removed — bring your own voiceover */}
2188
2187
 
2189
2188
  {/* Perpetual layers */}
2190
2189
  <CausticBlobs />
@@ -873,7 +873,7 @@ export const DesignReel: React.FC = () => {
873
873
 
874
874
  return (
875
875
  <AbsoluteFill style={{ backgroundColor: "#e8e4de", fontFamily: FONT }}>
876
- {/* REFERENCE-STRIP: <Audio> tag removed — bring your own voiceover */}
876
+ {/* REFERENCE-STRIP: voiceover tag removed — bring your own voiceover */}
877
877
  <Background frame={frame} />
878
878
  <HookScene frame={frame} fps={fps} />
879
879
  <TaglineScene frame={frame} fps={fps} />
@@ -1884,7 +1884,7 @@ export const JustDropReel: React.FC = () => {
1884
1884
 
1885
1885
  return (
1886
1886
  <AbsoluteFill style={{ backgroundColor: "#e8e4de", fontFamily: FONT }}>
1887
- {/* REFERENCE-STRIP: <Audio> tag removed — bring your own voiceover */}
1887
+ {/* REFERENCE-STRIP: voiceover tag removed — bring your own voiceover */}
1888
1888
  <Background frame={frame} />
1889
1889
  <HookScene frame={frame} fps={fps} />
1890
1890
  <PowersScene frame={frame} fps={fps} />
@@ -1751,7 +1751,7 @@ export const OpusReel: React.FC = () => {
1751
1751
  return (
1752
1752
  <AbsoluteFill style={{ backgroundColor: "#e8e4de", fontFamily: FONT }}>
1753
1753
  {/* Voiceover audio */}
1754
- {/* REFERENCE-STRIP: <Audio> tag removed — bring your own voiceover */}
1754
+ {/* REFERENCE-STRIP: voiceover tag removed — bring your own voiceover */}
1755
1755
 
1756
1756
  <Background frame={frame} />
1757
1757
  <HookScene frame={frame} fps={fps} />
@@ -17,7 +17,6 @@
17
17
  import React from "react";
18
18
  import {
19
19
  AbsoluteFill,
20
- Audio,
21
20
  Easing,
22
21
  OffthreadVideo,
23
22
  Sequence,
@@ -3367,7 +3366,7 @@ export const HuashuReel: React.FC = () => {
3367
3366
  }}
3368
3367
  >
3369
3368
  {/* VOICEOVER — runs the entire composition */}
3370
- {/* REFERENCE-STRIP: <Audio> tag removed — bring your own voiceover */}
3369
+ {/* REFERENCE-STRIP: voiceover tag removed — bring your own voiceover */}
3371
3370
 
3372
3371
  <Sequence from={SCENES.S1.from} durationInFrames={SCENES.S1.dur}>
3373
3372
  <Scene1 />
@@ -17,7 +17,6 @@
17
17
  import React from "react";
18
18
  import {
19
19
  AbsoluteFill,
20
- Audio,
21
20
  Easing,
22
21
  Img,
23
22
  OffthreadVideo,
@@ -2878,7 +2877,7 @@ const Scene8: React.FC = () => {
2878
2877
  export const MemPalaceReel: React.FC = () => {
2879
2878
  return (
2880
2879
  <AbsoluteFill style={{ background: C.bg }}>
2881
- {/* REFERENCE-STRIP: <Audio> tag removed — bring your own voiceover */}
2880
+ {/* REFERENCE-STRIP: voiceover tag removed — bring your own voiceover */}
2882
2881
 
2883
2882
  <Sequence from={SCENES.S1.from} durationInFrames={SCENES.S1.dur}>
2884
2883
  <Scene1 />
package/skill/SKILL.md CHANGED
@@ -19,6 +19,12 @@ When a `/reelstack-*` command fires, you scaffold a complete `<Name>Reel.tsx` Re
19
19
 
20
20
  ---
21
21
 
22
+ ## Craft posture
23
+
24
+ Approach each reel as a senior motion designer would: motion-heavy openers where the lint's ≥4-layer floor is the floor, not the ceiling; every anchor scene earns its dwell with kinetic typography or perpetual primitives (`SonarRings`, `ParticleBurst`, `DriftingSpotlights`, `IridescentRing`, `CausticBlobs`, etc.); GSAP-flavored easings (`power4Out`, `expoOut`, `backOut`, `bouncy`, `gentle` — exposed via `utils/easing.ts` and reinforced by the bundled `gsap-core` / `gsap-timeline` companion skills) throughout. Before iterating, READ the matching reference reel at `~/.reelstack/reference/<family>/<preset>.tsx` for cadence and scene composition. The lint enforces the floor — you're aiming above it.
25
+
26
+ ---
27
+
22
28
  ## Design discipline & productivity influences
23
29
 
24
30
  ReelStack v1.1+ bakes in two outside influences so buyers don't think about taste at all — they just trigger a slash command and inherit curator-grade design + huashu-style productivity patterns: