@lalalic/markcut 2.2.3 → 2.2.5

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": "@lalalic/markcut",
3
- "version": "2.2.3",
3
+ "version": "2.2.5",
4
4
  "description": "Markdown-to-video engine. Describe scenes in markdown, get a rendered video.",
5
5
  "bin": {
6
6
  "markcut": "bin/markcut"
@@ -28,6 +28,14 @@ see [docs/markdown-descriptive.md](docs/markdown-descriptive.md) for full detail
28
28
  - Templates in ``docs/templates/`` provide ready-to-use video structures for `orchestrator` agents, use them as starting point to create your own videos.
29
29
 
30
30
 
31
+ ### Viral Story requires
32
+ - **Hooks** : why should the viewer watch this?
33
+ - **Conflict** : what challenges or obstacles do the characters face?
34
+ - **Resolution** : how are the conflicts resolved?
35
+ - **Emotion** : what feelings are evoked in the viewer?
36
+ - **Call to action** : what should the viewer do next?
37
+ - **Open ending** : does the story leave room for interpretation or continuation?
38
+
31
39
  ## 3. CLI
32
40
 
33
41
  ```bash
@@ -66,4 +74,11 @@ some common issues (photo or video can't be displayed, audio missing), take belo
66
74
  - `rehype-katex` — render math formulas with KaTeX
67
75
 
68
76
  - `@remotion/shapes` — render shapes like arrows, circles, rectangles, etc
69
- - `@remotion/starburst` — render starburst animations
77
+ - `@remotion/starburst` — render starburst animations
78
+
79
+ ## Golden rule
80
+ - always check stream start and duration to avoid
81
+ - audio cut off
82
+ - video cut off
83
+ - subtitle mismatch
84
+ - sync issues between audio, video, and subtitles
@@ -35,7 +35,11 @@ The line after `# video` contains all video configuration as space-separated
35
35
 
36
36
  ```markdown
37
37
  # video
38
- width:1920 height:1080 fps:30 layout:series tts:"edge-tts --voice 'en-US-GuyNeural' --text '{input}' --write-media '{output}'" stylesheet:".bg { background: #000; }" subtitle:captions.vtt
38
+ width:1920 height:1080 fps:30 layout:series
39
+ ~~~css stylesheet
40
+ .bg { background: #000; }
41
+ ~~~
42
+ subtitle:captions.vtt
39
43
  ```
40
44
 
41
45
  Supported keys: `width`, `height`, `fps`, `layout`, `tts`, `stt`, `tti`, `ttv`,
@@ -145,8 +149,10 @@ For compatibility, `import { Name } from "spec"` also works and produces the sam
145
149
  | `height` | canvas height | root |
146
150
  | `fps` | frame rate | root |
147
151
  | `theme` | *removed — use `style` on root* | root |
148
- | `tts` | CLI template string (e.g. `edge-tts --voice "en-US-GuyNeural" --text "{input}" --write-media "{output}"`) | root |
149
- | `stt` | CLI template string (e.g. `whisper "{input}" --output_format vtt --output_dir "{output}"`) | root |
152
+ | `tts` | text-to-speech CLI template string | root |
153
+ | `stt` | speech-to-text CLI template string | root |
154
+ | `tti` | text-to-image CLI template string | root |
155
+ | `ttv` | text-to-video CLI template string | root |
150
156
  | `layout` | `series\|parallel\|transitionSeries` | root, scene |
151
157
  | `transition` | `fade\|slide\|wipe\|flip\|clockWipe` | transitionSeries |
152
158
  | `transitionTime` | seconds | transitionSeries |
@@ -176,7 +182,6 @@ For compatibility, `import { Name } from "spec"` also works and produces the sam
176
182
  | `script` | narration/dialogue text; TTS source; NOT rendered directly | audio | Only on audio nodes — see Narration section below |
177
183
  | `speaker` | speaker name for multi-turn dialogue; set automatically by dialogue expansion | audio | Used for per-speaker voice lookup and subtitle prefix |
178
184
  | `voices` | JSON object mapping speaker names to extra TTS CLI flags e.g. `{"Ray":"--voice en-US-GuyNeural"}` | root | Flags are appended to the resolved TTS CLI template |
179
- | `tts` | CLI template string e.g. `"edge-tts --voice en-US-GuyNeural --text {input} --write-media {output}"` | root | Set once at the root level; all TTS uses this template |
180
185
  | `metadata` | arbitrary metadata string | root |
181
186
  | `stylesheet` | global CSS string; selectors use `.className` on elements | root |
182
187
  | `style` | inline CSS applied to the node's container div e.g. `"border-radius:12px"` | any |
@@ -752,45 +757,49 @@ markcut verify courseware.md
752
757
  - `@remotion/shapes` — render shapes like arrows, circles, rectangles, etc
753
758
  - `@remotion/starburst` — render starburst animations
754
759
 
755
- ## Example
756
-
757
- ```md
758
- ---
759
- width: 1080
760
- height: 1920
761
- fps: 30
762
- ---
763
- # video
764
- layout:series
765
- ~~~js imports
766
- export { StatCounter } from "stat-counter"
767
- export { Logo } from "github:myorg/design-system#Logo"
768
-
769
- export function Greeting({ name }) {
770
- return <div style={{color: '#fff', fontSize: 28, textAlign: 'center'}}>Hello {name}!</div>
771
- }
760
+ # best practices
761
+ ~~~ example - avoid audio cut
762
+ - parallel
763
+ - image|video background:true
764
+ - audio|script
772
765
  ~~~
773
766
 
774
- ## Hook
775
- layout:parallel
776
- - script "Set location and emotional tone"
777
- - image src:cover.jpg duration:2
767
+ ~~~ example - global streams
768
+ #video
769
+ - audio background:true loop:true src:bgm.mp3
770
+ - component background:true loop:true jsx:"<Logo />" style:"position:fixed;top:10px;left:10px;width:100px;height:100px;"
771
+ - parallel title:"lip sync" style:"position:fixed;bottom:100px;right:100px;width:100px;height:100px;"
772
+ - video src:background.mp4 loop:true background:true
773
+ - video src:lip_sync.mp4
774
+ ~~~
778
775
 
779
- ## Journey
780
- layout:transitionSeries transition:fade transitionTime:0.4
781
- - script "Move through moments"
782
- - video src:clips/arrival.mp4 startFrom:0 endAt:3.5
783
- - video src:clips/fire.mp4 startFrom:1 endAt:4
776
+ ~~~ example - styling VTT captions
777
+ ```vtt
778
+ 000:00:00.000 --> 00:00:05.000
779
+ It's a <span style="color:#ff6b6b;font-weight:bold">Bear</span>.
784
780
 
785
- ## Stat
786
- layout:parallel
787
- - component duration:2 jsx:"<StatCounter value={42} label='S-mores' />"
781
+ 000:00:05.000 --> 00:00:10.000
782
+ And now it's <span class="emoji-dead">dead</span>.
788
783
 
789
- ## Logo
790
- layout:parallel
791
- - component duration:1 jsx:"<Logo />"
784
+ 000:00:10.000 --> 00:00:15.000
785
+ <span class="bong">Bong!</span>
786
+ ```
792
787
 
793
- ## Route
794
- layout:parallel
795
- - map duration:3 travelMode:DRIVING waypoints:[37.7749,-122.4194,"SF";34.0522,-118.2437,"LA"]
788
+ #root
789
+ ```css stylesheet
790
+ .emoji-dead {
791
+ font-family: "Funny Emoji", sans-serif;
792
+ font-size: 48px;
793
+ }
794
+ .bong {
795
+ color: #ff6b6b;
796
+ font-weight: bold;
797
+ position: absolute;
798
+ top: 50%;
799
+ left: 50%;
800
+ transform: translate(-50%, -50%);
801
+ animation: bong 1s ease-in-out infinite;
802
+ }
796
803
  ```
804
+ ~~~
805
+
@@ -59,7 +59,6 @@ What tools/skills the agent needs before starting. Always include:
59
59
 
60
60
  - The engine itself: `npx @lalalic/markcut` runnable
61
61
  - Required markcut skill (`SKILL.md` + `docs/markdown-descriptive.md`) — the agent must read these
62
- - Required CLIs: `edge-tts`, `ffmpeg`/`ffprobe`, whisper, image-understanding (for reviewer)
63
62
  - Any domain-specific tools
64
63
 
65
64
  ### §1 Inputs — the contract with the user
@@ -9,26 +9,6 @@ engine: "@lalalic/markcut — run via `npx @lalalic/markcut`"
9
9
 
10
10
  Follow this file top to bottom. Read the markcut skill (`SKILL.md` → `docs/markdown-descriptive.md`) first if you have not.
11
11
 
12
- ```mermaid
13
- flowchart LR
14
- Profile[👤 user.md<br/>profile] --> Writer
15
- History[📜 vlog_history.json<br/>past entries] --> Writer
16
- Media[📁 Media folder] --> Vision[🧠 markcut vision --label]
17
- Vision --> Labels[📝 metadata.json]
18
- Labels --> Group[🔍 group-clips.md<br/>filter + group]
19
- Group --> Groups[📋 grouped scenes]
20
- Locale[🌍 local-context.md<br/>weather, events, news] --> Groups
21
- Groups --> Writer[✍️ story-writer agent<br/>narration + title]
22
- Writer --> Storyboard[📄 course.md]
23
- BGM[🎵 bgm-select.md<br/>mandatory BGM] --> Assemble[📦 assemble + render]
24
- Storyboard --> Assemble
25
- Assemble --> MP4[📺 final.mp4]
26
- MP4 --> Reviewer[🔍 reviewer agent]
27
- Reviewer -->|FAIL| Fix[🔧 fix]
28
- Fix --> Assemble
29
- Reviewer -->|PASS| HistoryUpdate[📝 append to<br/>vlog_history.json]
30
- ```
31
-
32
12
  | Path | Runs in | Purpose |
33
13
  |---|---|---|
34
14
  | `TEMPLATE.md` | your context | everything |
@@ -38,10 +18,7 @@ flowchart LR
38
18
  ## 0. Prerequisites
39
19
 
40
20
  - `npx @lalalic/markcut` runnable
41
- - TTS CLI (default: `edge-tts`)
42
- - `ffmpeg`/`ffprobe` on PATH
43
- - For vision pipeline: ITT/VTT/STT CLIs configured (see `docs/markdown-descriptive.md` for env vars)
44
- - For reviewer: image-understanding capability, STT CLI
21
+ - `ffmpeg`/`ffprobe`/`exiftool` on PATH
45
22
 
46
23
  ## 1. Inputs — collect before starting
47
24
 
@@ -50,13 +27,13 @@ flowchart LR
50
27
  | Media folder | **yes** | — | absolute path to folder of photos/videos |
51
28
  | Theme / event name | **yes** | — | e.g. "birthday camping", "Tokyo trip", "product unboxing" |
52
29
  | Date range | no | from media EXIF | — |
53
- | Language | no | en | narrator's language |
30
+ | Language | no | zh | narrator's language |
54
31
  | Style | no | `daily` | `daily` (casual), `lyrical` (cinematic), `humorous` (funny) |
55
32
  | Target duration | no | 60s | 15–120s for short vlogs |
56
- | Profile | **parse from** | `~/.pi/agent/user.md` | user's personality, region, family members, voice ref, life context. Read this file at the start — it's the authoritative source for who the creator is. The creator's name, region, family, and regular activities feed into narration naturally ("my daughter", "drove up from Ottawa", "my third camping trip this year"). |
33
+ | Profile | **parse from** | context | user's personality, region, family members, voice ref, life context. Read this file at the start — it's the authoritative source for who the creator is. The creator's name, region, family, and regular activities feed into narration naturally ("my daughter", "drove up from Ottawa", "my third camping trip this year"). |
57
34
  | Voice | no | en: `en-US-GuyNeural` | edge-tts voice. If profile has a voice reference URL, use TTS voice cloning instead |
58
35
  | BGM style | no | `ambient` | mood/genre for background music. BGM is **mandatory** — see §2 for root-level audio node |
59
- | Vlog history | **read from** | `vlog_history.json` | JSON array of past vlogs in the project. Used for story continuity ("last time I was here...", "my third camping trip this year"). If the file doesn't exist, start fresh. See §3 for history rules. |
36
+ | Vlog history | **read from** | `vlog_history.json` or folder | JSON array of past vlogs in the project. Used for story continuity ("last time I was here...", "my third camping trip this year"). If the file doesn't exist, start fresh. See §3 for history rules. |
60
37
 
61
38
  **Rule:** The golden rule of vlogs — **every word in narration, title, or captions must be traceable to real data**: (1) user-stated facts, (2) clip VLM descriptions, (3) GPS/timestamp metadata, (4) user labels from interactive labeling, (5) local news/searched events for the date/location. Never invent people, dialogue, events, or place names. When unsure, paraphrase conservatively ("a path", "someone") rather than guessing.
62
39
 
@@ -65,6 +42,13 @@ flowchart LR
65
42
  ### Overview
66
43
 
67
44
  ```
45
+ ---
46
+ storyoutline: # it's your logic to design story.
47
+ hook:
48
+ confliction:
49
+ emotion:
50
+ open-ending:
51
+ ---
68
52
  # video ← root: width:1920 height:1080 fps:30 layout:series
69
53
  │ tts:"<edge-tts CLI template>"
70
54
  ├── ## Route ← (only if GPS data available)
@@ -100,9 +84,11 @@ width:1920 height:1080 fps:30 layout:series
100
84
  - Volume: 0.10–0.20 for ambient BGM; 0.05–0.10 for lyrical music.
101
85
  - Source BGM from royalty-free libraries (see `prompts/bgm-select.md`).
102
86
 
103
- ### Core design: one scene per story beat, media + narration as parallel
87
+ ### Core design: one scene per story beat
88
+ - determine **Hook**, **Confliction**, **Emotion**, **Open-ending**
89
+ - the vlog's story arc: hook → chronological story beats → close.
104
90
 
105
- Each scene is a **story beat** — a single moment or idea that stands on its own. Inside a scene, `layout:parallel` means all media and the subtitle narration play simultaneously.
91
+ - Each scene is a **story beat** — a single moment or idea that stands on its own. Inside a scene, `layout:parallel` means all media and the subtitle narration play simultaneously.
106
92
 
107
93
  ```
108
94
  ## Into the Woods
@@ -113,7 +99,7 @@ layout:parallel
113
99
  ```
114
100
 
115
101
  **Rules:**
116
-
102
+ - determine story arc first, then group clips into scenes. Each scene is a single story beat.
117
103
  - **Scene count**: 5–12 scenes. Each 3–20 seconds. Total duration matches target ±15%.
118
104
  - **Hook → Core → Vibe → Close arc**: The first scene hooks the viewer (best visual + intriguing line). Middle scenes are chronological story beats. The final scene provides emotional/humorous closure.
119
105
  - **Transition between scenes**: The root `layout:series` plays scenes sequentially. For smooth transitions, add `transition:fade(0.5)` to the root config line.
@@ -123,19 +109,6 @@ layout:parallel
123
109
  - **BGM is mandatory**: Always add an `- audio isBackground:true foreground:true` at root level (see BGM section above).
124
110
  - **No bullet-reveal** — vlogs are linear media, not slides. No `current`/`on:` events needed.
125
111
 
126
- ### Scene media node style
127
-
128
- For images:
129
- ```markdown
130
- - image src:<relative-path> duration:<s> start:<s>
131
- ```
132
-
133
- For video clips (trimmed):
134
- ```markdown
135
- - video src:<relative-path> startFrom:<s> endAt:<s> start:<s>
136
- ```
137
-
138
- The `src` path is relative to the media folder. During assembly, media is symlinked/copied into the markcut project so `npx markcut render` can serve it.
139
112
 
140
113
  ## 2.b Production polish — route sync, overlays, effects
141
114
 
@@ -177,14 +150,14 @@ Use per-scene map overlays when GPS data is available for that specific clip. Us
177
150
 
178
151
  ## 3. Authoring rules — the professional bar
179
152
 
180
- ### Filter rules (see `prompts/group-clips.md`)
153
+ ### Filter rules (use `prompts/group-clips.md`)
181
154
 
182
- - Keep all clips unless: aesthetic score < 0.4, near-duplicate of a better clip, or off-topic.
155
+ - Keep all clips unless: near-duplicate of a better clip, or off-topic.
183
156
  - Always keep clips with a positive user label from the interactive labeling step.
184
157
  - Max ~20% drops. Never drop below 5 clips total.
185
158
  - List every dropped clip with a reason.
186
159
 
187
- ### Grouping rules (see `prompts/group-clips.md`)
160
+ ### Grouping rules (use `prompts/group-clips.md`)
188
161
 
189
162
  - **Visual continuity rule**: clips sharing the same background/outfit/subject-state form one scene. Exhaust one scene before moving to the next. No jumping A→B→A.
190
163
  - 1–4 clips per scene (a single long take >10s may be its own scene).
@@ -210,7 +183,7 @@ Before writing narration, search for local news, events, or weather for the vlog
210
183
  - **Local event**: "Turns out the Gatineau Park was running the fall colours festival that weekend."
211
184
  - **Seasonal context**: "Full moon that night, so the trail was brighter than usual."
212
185
 
213
- Rules:
186
+ **Rules:**
214
187
  - Only use facts confirmed by at least one search result. Never invent weather/events.
215
188
  - Keep it to 1–2 local-context references per vlog. Don't overwhelm the narration.
216
189
  - The context should **amplify** the story, not replace it. "Drove up during the fall colours festival so the park was packed" tells us more than just the festival fact.
@@ -230,7 +203,7 @@ Rules:
230
203
  - Keep references brief (one line per vlog max). The story should stand alone.
231
204
  - Never fabricate a past event. Only what exists in the history file.
232
205
 
233
- ### Creator profile
206
+ ### User profile
234
207
 
235
208
  Read `~/.pi/agent/user.md` at the start and weave the creator's identity into narration naturally:
236
209
 
@@ -249,44 +222,6 @@ One title, 3–15 words, poetic/suspenseful/summarizing, social-media friendly.
249
222
  - Same pattern as courseware: `# <lang>` variant block at file end with per-language TTS voice.
250
223
  - Every `script`/`subtitle` node gets a `<lang>:"..."` twin.
251
224
 
252
- ## 4. Components & styles
253
-
254
- Vlogs don't need custom JSX components — they use markcut's built-in `image`, `video`, `audio`, `subtitle`, and `map` nodes. However, you may add these optional enhancements:
255
-
256
- ### Optional: NarratorBox component
257
-
258
- If adding a narrator overlay with name label, include this in the `~~~js imports` block:
259
-
260
- ```jsx
261
- import { delayRender, continueRender } from 'remotion'
262
-
263
- export function NarratorBox({ src, name = '', size = 100 }) {
264
- return (
265
- <div style={{
266
- position: 'absolute', bottom: 40, left: 40,
267
- display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 6,
268
- }}>
269
- <div style={{
270
- width: size, height: size, borderRadius: '50%', overflow: 'hidden',
271
- border: '3px solid rgba(255,255,255,.6)',
272
- boxShadow: '0 0 20px rgba(0,0,0,.4)',
273
- }}>
274
- <img src={src} style={{ width: '100%', height: '100%', objectFit: 'cover' }} />
275
- </div>
276
- {name && (
277
- <span style={{
278
- fontSize: 14, color: '#fff', textShadow: '0 2px 8px rgba(0,0,0,.6)',
279
- whiteSpace: 'nowrap', fontWeight: 500,
280
- }}>{name}</span>
281
- )}
282
- </div>
283
- )
284
- }
285
- ```
286
-
287
- ### Stylesheet
288
-
289
- No stylesheet needed for standard vlogs. The built-in markcut rendering handles video/image filling, subtitle positioning, and audio mixing. Add one only if you need custom overlays.
290
225
 
291
226
  ## 5. Workflow
292
227
 
@@ -307,7 +242,7 @@ No stylesheet needed for standard vlogs. The built-in markcut rendering handles
307
242
  }
308
243
  ]
309
244
  ```
310
- If the file doesn't exist, start fresh.
245
+ If the file doesn't exist, try to extract for project folder.
311
246
 
312
247
  ### Phase 1: Prepare media
313
248
 
@@ -326,7 +261,7 @@ No stylesheet needed for standard vlogs. The built-in markcut rendering handles
326
261
  5. **Search local news/events** — Fill `prompts/local-context.md` with the vlog's location, date range, and theme. This searches for relevant weather, events, news, or seasonal information. The result feeds into narration with 1–2 natural references.
327
262
 
328
263
  6. **Group and filter clips** — Fill `prompts/group-clips.md` with clip data from `metadata.json`. This produces:
329
- - Dropped clips with reasons
264
+ - Speed up less-interesting clips vs Dropped clips
330
265
  - Visual-continuity scene groups
331
266
  - Trim boundaries for video clips
332
267
  - GPS waypoints for per-scene map overlays
@@ -348,16 +283,7 @@ No stylesheet needed for standard vlogs. The built-in markcut rendering handles
348
283
 
349
284
  8. **Select BGM** — Choose background music that matches the vlog's mood. Fill `prompts/bgm-select.md` or search using the `audio-sourcing` skill. Download the track and reference it as `src:bgm.mp3` in the root-level audio node.
350
285
 
351
- 9. **Assemble course.md** — Write the markcut markdown file using §2 grammar. The root config line includes TTS voice. BGM is at root level (`- audio isBackground:true foreground:true src:bgm.mp3 volume:0.15`). For scenes with GPS data, add per-scene map overlays (see §2.b).
352
-
353
- **Media staging**: Make media accessible to the renderer:
354
- ```
355
- mkdir -p .markcut/generated/media/
356
- cp <media-folder>/clips_normalized/*.mp4 .markcut/generated/media/
357
- cp <media-folder>/*.jpg .markcut/generated/media/
358
- cp /path/to/bgm.mp3 .markcut/generated/media/bgm.mp3
359
- ```
360
- Reference as `src:.markcut/generated/media/<file>` in the markdown.
286
+ 9. **Assemble .md** — Write the markcut markdown file using §2 grammar. For scenes with GPS data, add per-scene map overlays (see §2.b).
361
287
 
362
288
  10. **Render** — `npx @lalalic/markcut render course.md`. On engine errors: fix and re-render, max 3 attempts per error, then ask the user.
363
289
 
@@ -394,12 +320,7 @@ Done only when ALL hold:
394
320
  - [ ] BGM is **present** (ffprobe confirms audio stream or mixed track) and audible (not silent, not clipping)
395
321
  - [ ] BGM ducked under voice — TTS audible above music in ≥90% of spoken segments
396
322
  - [ ] local context references (if any) verified against search results — no invented weather/events
397
- - [ ] history references (if any) match actual entries in `vlog_history.json` — no fabricated past events
323
+ - [ ] history references — no fabricated past events
398
324
  - [ ] media `src` paths resolve correctly (no broken links in rendered output)
399
325
  - [ ] profile used: narration tone matches creator personality, family names from profile appear correctly
400
- - [ ] per-scene map overlays (if GPS available) positioned without obstructing main content
401
-
402
- ## 7. Reference — worked examples
403
-
404
- - Golden example: See `tests/fixtures/templates/courseware.md` for the template format.
405
- - Reference vlog storyboard: The bullx vlog project at `free2/bullx/packages/vlog/storyboard.md` shows a real vlog (birthday camping at Lac Philippe) with 13 chronological scenes, route map, clip trimming, and first-person narration. The project's `.pi/agents/story-writer.md` and `.pi/prompts/vlog-storyboard.md` contain the source of truth for the story-writing methodology documented here.
326
+ - [ ] per-scene map overlays (if GPS available) positioned without obstructing main conten