@bycrux/montaj-skills 0.3.0 → 0.3.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/package.json +1 -1
- package/skills/overlay.md +1 -1
- package/skills/write-overlay.md +20 -16
package/package.json
CHANGED
package/skills/overlay.md
CHANGED
|
@@ -45,7 +45,7 @@ If the prompt says "no overlays" — write nothing. Don't add an opening hook an
|
|
|
45
45
|
- **Go large** — 96–160px is a starting point, not a ceiling. If it looks a little too big, it's probably right. Small text gets scrolled past.
|
|
46
46
|
- **No backgrounds** — avoid dark cards, frosted panels, and semi-transparent boxes unless the prompt asks for them. A text shadow (`textShadow: '0 2px 16px rgba(0,0,0,0.9)'`) is enough to ensure legibility on any footage without boxing the text in.
|
|
47
47
|
- **Covering the face is fine** — text is more important than an unobstructed view of the speaker. Don't shrink or reposition text just to avoid the face.
|
|
48
|
-
- **Match the energy of the speech** — fast, punchy delivery gets tight entrance animations (
|
|
48
|
+
- **Match the energy of the speech** — fast, punchy delivery gets tight entrance animations (about 0.13–0.2s: `Math.round(fps * 0.13)` to `Math.round(fps * 0.2)`). Slower, deliberate speech gets a smoother slide or fade (about 0.33–0.5s). Never hardcode frame counts; projects can be 24, 30 or 60fps.
|
|
49
49
|
- **Use color sparingly** — one accent color maximum. White text with a colored word or icon reads better than multi-color text.
|
|
50
50
|
- **Avoid the bottom ~350px** — that's where captions render and where platform UI lives (TikTok progress bar, Instagram controls). Keep `bottom` values above 350px, or use `top`-anchored placement instead.
|
|
51
51
|
- **Avoid the right ~200px** — TikTok and Instagram stack action buttons (like, comment, share, follow) down the right edge. Don't push text or icons into that zone.
|
package/skills/write-overlay.md
CHANGED
|
@@ -34,18 +34,20 @@ Custom overlay JSX runs in a sandboxed evaluator. All identifiers below are inje
|
|
|
34
34
|
|
|
35
35
|
**No imports.** All `import` statements are stripped before evaluation. Do not import anything — use the globals above instead.
|
|
36
36
|
|
|
37
|
+
**Never hardcode frame counts; projects can be 24, 30 or 60fps.** A literal like `[0, 10]` means a different real-world duration on every project — 10 frames is 0.33s at 30fps but 0.17s at 60fps, so an overlay copied between projects (or a 30fps example copied into a 60fps project, which is the current default) plays at the wrong speed. Always derive the frame count from `fps`: `Math.round(fps * 0.33)` for "about a third of a second," not `10`.
|
|
38
|
+
|
|
37
39
|
### Top-level vs component-body
|
|
38
40
|
|
|
39
41
|
**All calls to `interpolate`, `spring`, and any read of `frame`, `fps`, `duration`, or `props` must be inside the component function body.** The module's top-level code runs before the render shim sets up these globals — calling them outside a function will throw `interpolate is not defined` and crash the entire render.
|
|
40
42
|
|
|
41
43
|
```jsx
|
|
42
44
|
// WRONG — crashes at render time
|
|
43
|
-
const opacity = interpolate(frame, [0,
|
|
45
|
+
const opacity = interpolate(frame, [0, Math.round(fps * 0.33)], [0, 1])
|
|
44
46
|
export default function Hook() { ... }
|
|
45
47
|
|
|
46
48
|
// CORRECT — inside the component, runs each frame
|
|
47
49
|
export default function Hook() {
|
|
48
|
-
const opacity = interpolate(frame, [0,
|
|
50
|
+
const opacity = interpolate(frame, [0, Math.round(fps * 0.33)], [0, 1])
|
|
49
51
|
return <div style={{ opacity }}>...</div>
|
|
50
52
|
}
|
|
51
53
|
```
|
|
@@ -77,8 +79,8 @@ The default aesthetic is **plain bold text directly on video** — no card, no b
|
|
|
77
79
|
// overlays/hook.jsx — plain text on video, no background
|
|
78
80
|
|
|
79
81
|
export default function Hook() {
|
|
80
|
-
const progress = interpolate(frame, [0,
|
|
81
|
-
const slideY = interpolate(frame, [0,
|
|
82
|
+
const progress = interpolate(frame, [0, Math.round(fps * 0.27)], [0, 1])
|
|
83
|
+
const slideY = interpolate(frame, [0, Math.round(fps * 0.33)], [40, 0])
|
|
82
84
|
|
|
83
85
|
return (
|
|
84
86
|
<div style={{
|
|
@@ -127,8 +129,8 @@ A control appears for each of those that is present (non-null) on `props`; anyth
|
|
|
127
129
|
```jsx
|
|
128
130
|
// Editable Hook — every text property is adjustable in the panel.
|
|
129
131
|
export default function Hook() {
|
|
130
|
-
const progress = interpolate(frame, [0,
|
|
131
|
-
const slideY = interpolate(frame, [0,
|
|
132
|
+
const progress = interpolate(frame, [0, Math.round(fps * 0.27)], [0, 1])
|
|
133
|
+
const slideY = interpolate(frame, [0, Math.round(fps * 0.33)], [40, 0])
|
|
132
134
|
|
|
133
135
|
return (
|
|
134
136
|
<div style={{ position: 'absolute', bottom: 180, left: 48, right: 48, opacity: progress, transform: `translateY(${slideY}px)` }}>
|
|
@@ -218,7 +220,7 @@ The most reliable way to use frosted-glass / blurred card backgrounds is to **pu
|
|
|
218
220
|
```jsx
|
|
219
221
|
// overlays/card-bg.jsx
|
|
220
222
|
// Just a frosted card that fades in. No children that animate opacity.
|
|
221
|
-
const opacity = interpolate(frame, [0,
|
|
223
|
+
const opacity = interpolate(frame, [0, Math.round(fps * 0.27)], [0, 1])
|
|
222
224
|
|
|
223
225
|
export default function CardBg() {
|
|
224
226
|
return (
|
|
@@ -273,16 +275,18 @@ background: 'rgba(10,10,10,0.88)' // solid dark — visually similar, no GPU la
|
|
|
273
275
|
Maps a frame number to any output value. Clamps at both ends by default.
|
|
274
276
|
|
|
275
277
|
```jsx
|
|
276
|
-
// Fade in over
|
|
277
|
-
const
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
const
|
|
278
|
+
// Fade in over the first ~0.5s
|
|
279
|
+
const holdIn = Math.round(fps * 0.5)
|
|
280
|
+
const opacity = interpolate(frame, [0, holdIn], [0, 1])
|
|
281
|
+
|
|
282
|
+
// Fade in then out, each over ~0.5s
|
|
283
|
+
const holdOut = Math.round(fps * 0.5)
|
|
284
|
+
const fadeIn = interpolate(frame, [0, holdIn], [0, 1])
|
|
285
|
+
const fadeOut = interpolate(frame, [duration - holdOut, duration], [1, 0])
|
|
282
286
|
const opacity = Math.min(fadeIn, fadeOut)
|
|
283
287
|
|
|
284
|
-
// Slide in from left
|
|
285
|
-
const x = interpolate(frame, [0,
|
|
288
|
+
// Slide in from left over ~0.67s
|
|
289
|
+
const x = interpolate(frame, [0, Math.round(fps * 0.67)], [-200, 0])
|
|
286
290
|
```
|
|
287
291
|
|
|
288
292
|
One option: `extrapolate` — `'clamp'` (default) or `'extend'`. That is the whole options object; the runtime destructures `{ extrapolate = 'clamp' }` and ignores everything else. Earlier versions of this file used `extrapolateRight`, which does not exist and was silently dropped — harmless only because the default was already `'clamp'`.
|
|
@@ -599,7 +603,7 @@ Assets (logos, images) are declared in `project.assets`. Reference them by passi
|
|
|
599
603
|
|
|
600
604
|
```jsx
|
|
601
605
|
// overlays/logo.jsx
|
|
602
|
-
const opacity = interpolate(frame, [0,
|
|
606
|
+
const opacity = interpolate(frame, [0, Math.round(fps * 0.2)], [0, 1])
|
|
603
607
|
|
|
604
608
|
export default function Logo() {
|
|
605
609
|
return (
|