@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 +20 -14
- package/package.json +1 -1
- package/reference/dark/claudedispatch.tsx +1 -1
- package/reference/dark/notebooklm.tsx +1 -1
- package/reference/dark/stitch.tsx +1 -1
- package/reference/forbidden/heretic.tsx +1 -2
- package/reference/glass/claudewatch.tsx +4 -4
- package/reference/glass/graphify.tsx +1 -2
- package/reference/glass/paperclip.tsx +1 -2
- package/reference/paper/designreel.tsx +1 -1
- package/reference/paper/justdrop.tsx +1 -1
- package/reference/paper/opus.tsx +1 -1
- package/reference/warm/huashu.tsx +1 -2
- package/reference/warm/mempalace.tsx +1 -2
- package/skill/SKILL.md +6 -0
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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
|
-
|
|
823
|
-
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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} />
|
package/reference/paper/opus.tsx
CHANGED
|
@@ -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:
|
|
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:
|
|
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:
|
|
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:
|