@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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bycrux/montaj-skills",
3
- "version": "0.3.0",
3
+ "version": "0.3.1",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "files": [
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 (4–6 frames). Slower, deliberate speech gets a smoother slide or fade (10–15 frames).
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.
@@ -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, 10], [0, 1])
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, 10], [0, 1])
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, 8], [0, 1])
81
- const slideY = interpolate(frame, [0, 10], [40, 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, 8], [0, 1])
131
- const slideY = interpolate(frame, [0, 10], [40, 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, 8], [0, 1])
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 frames 0–15
277
- const opacity = interpolate(frame, [0, 15], [0, 1])
278
-
279
- // Fade in then out
280
- const fadeIn = interpolate(frame, [0, 15], [0, 1])
281
- const fadeOut = interpolate(frame, [duration - 15, duration], [1, 0])
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, 20], [-200, 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, 6], [0, 1])
606
+ const opacity = interpolate(frame, [0, Math.round(fps * 0.2)], [0, 1])
603
607
 
604
608
  export default function Logo() {
605
609
  return (