miriad-viz 0.13.0 → 0.13.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.
|
@@ -114,7 +114,8 @@ function generateVizCuration(script, clipDurations, densityInput, projectStart,
|
|
|
114
114
|
speaker: line.speaker,
|
|
115
115
|
text: line.text,
|
|
116
116
|
progress: positions[i],
|
|
117
|
-
duration: DEFAULT_PILL_DURATION
|
|
117
|
+
duration: DEFAULT_PILL_DURATION,
|
|
118
|
+
anchor: true
|
|
118
119
|
});
|
|
119
120
|
}
|
|
120
121
|
}
|
package/dist-cli/index.js
CHANGED
|
@@ -242,8 +242,9 @@ function buildCurationTable(narration, pills, densityInput, projectStart, projec
|
|
|
242
242
|
output.push(` \u{1F4AC} ${pills.length} pills placed:`);
|
|
243
243
|
for (const p of pills) {
|
|
244
244
|
const truncText = p.text.length > 40 ? `${p.text.slice(0, 37)}...` : p.text;
|
|
245
|
+
const anchorTag = p.anchor ? " [anchor]" : "";
|
|
245
246
|
output.push(
|
|
246
|
-
` @ ${p.progress.toFixed(1)} (${p.duration.toFixed(1)}s): ${p.speaker} \u2014 "${truncText}"`
|
|
247
|
+
` @ ${p.progress.toFixed(1)} (${p.duration.toFixed(1)}s): ${p.speaker} \u2014 "${truncText}"${anchorTag}`
|
|
247
248
|
);
|
|
248
249
|
}
|
|
249
250
|
}
|
|
@@ -480,45 +481,20 @@ function buildVizTimingInProgress(_progress, densityInput, scriptLines, projectS
|
|
|
480
481
|
output.push(" 2.0 = viz covers project time 2x faster (LESS screen time)");
|
|
481
482
|
output.push(" 0.5 = viz covers project time 2x slower (MORE screen time)");
|
|
482
483
|
output.push("");
|
|
483
|
-
output.push(
|
|
484
|
-
|
|
485
|
-
);
|
|
486
|
-
output.push("
|
|
487
|
-
output.push("
|
|
488
|
-
output.push(
|
|
489
|
-
" gapVizSpeed \u2014 viz speed BETWEEN this clip and the next (default: same as defaultVizSpeed)"
|
|
490
|
-
);
|
|
491
|
-
output.push(" note \u2014 your reasoning (for yourself, not rendered)");
|
|
492
|
-
output.push("");
|
|
493
|
-
output.push(" vizSpeed is a MULTIPLIER on project-time-per-second:");
|
|
494
|
-
output.push(" 1.0 = default rate");
|
|
495
|
-
output.push(" 2.0 = viz covers project time 2x faster (LESS screen time)");
|
|
496
|
-
output.push(" 0.5 = viz covers project time 2x slower (MORE screen time)");
|
|
497
|
-
output.push("");
|
|
498
|
-
output.push(" HOW SPEED FIELDS MAP TO THE TIMELINE:");
|
|
499
|
-
output.push(" \u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510 \u250C\u2500\u2500\u2500\u2500\u2500\u2510 \u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510 \u250C\u2500\u2500\u2500\u2500\u2500\u2510");
|
|
500
|
-
output.push(" \u2502 narrator-01 clip\u2502 \u2502 gap \u2502 \u2502 narrator-02 clip\u2502 \u2502 gap \u2502");
|
|
501
|
-
output.push(" \u2502 vizSpeed: 1.0 \u2502 \u2502 2.0 \u2502 \u2502 vizSpeed: 0.5 \u2502 \u2502 1.0 \u2502");
|
|
502
|
-
output.push(" \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518 \u2514\u2500\u2500\u2500\u2500\u2500\u2518 \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518 \u2514\u2500\u2500\u2500\u2500\u2500\u2518");
|
|
503
|
-
output.push(" \u2191 speed DURING clip \u2191 gapVizSpeed \u2191 speed DURING clip \u2191 gapVizSpeed");
|
|
504
|
-
output.push(" (audio playing) (silence/pause) (audio playing) (silence/pause)");
|
|
505
|
-
output.push("");
|
|
506
|
-
output.push(" SPEED REASONING (creative choice, not formula):");
|
|
507
|
-
output.push(" Dead time (nothing happening) \u2192 high gapVizSpeed (skip through it)");
|
|
508
|
-
output.push(" Lots of action \u2192 normal or slow vizSpeed (let viewer see the frenzy)");
|
|
509
|
-
output.push(" Dramatic reveal \u2192 big pauseAfter (tension before the next line)");
|
|
510
|
-
output.push(" These are guidelines. Trust your creative judgment.");
|
|
484
|
+
output.push(" HOW TO CONTROL PACING:");
|
|
485
|
+
output.push(" Move progress points CLOSER together \u2192 smaller gap \u2192 lower vizSpeed \u2192 slower");
|
|
486
|
+
output.push(" Move progress points FURTHER apart \u2192 larger gap \u2192 higher vizSpeed \u2192 faster");
|
|
487
|
+
output.push(" Dense areas (lots of action) \u2192 cluster narration points (smaller gaps)");
|
|
488
|
+
output.push(" Dead zones (nothing happening) \u2192 space out narration points (larger gaps)");
|
|
511
489
|
output.push("");
|
|
512
490
|
output.push(" AFTER EVERY EDIT, run ONE command:");
|
|
513
|
-
output.push(" npx miriad-viz preview --from-
|
|
491
|
+
output.push(" npx miriad-viz preview --from-curation");
|
|
514
492
|
output.push("");
|
|
515
493
|
output.push(" This auto-runs: generate-timing \u2192 transform \u2192 preview (all three).");
|
|
516
|
-
output.push(" You edit ONE file, run ONE command, see the result.");
|
|
494
|
+
output.push(" You edit ONE file (viz-curation.json), run ONE command, see the result.");
|
|
517
495
|
output.push("");
|
|
518
496
|
output.push(" Then ask the human to watch the preview and give feedback.");
|
|
519
|
-
output.push(
|
|
520
|
-
" Edit viz-curation.json or pacing.json based on feedback \u2192 run the command again \u2192 repeat."
|
|
521
|
-
);
|
|
497
|
+
output.push(" Edit viz-curation.json based on feedback \u2192 run the command again \u2192 repeat.");
|
|
522
498
|
output.push("");
|
|
523
499
|
output.push(" When human approves: npx miriad-viz next --timing-approved");
|
|
524
500
|
const vizTimingDoc = readBundledDoc("miriad-viz-viz-timing");
|
|
@@ -534,7 +510,7 @@ function buildVizTimingApprovalError() {
|
|
|
534
510
|
return [
|
|
535
511
|
"",
|
|
536
512
|
"\u2717 Cannot approve timing \u2014 no timing.json found.",
|
|
537
|
-
" Run `npx miriad-viz preview --from-
|
|
513
|
+
" Run `npx miriad-viz preview --from-curation` first to generate timing.json,",
|
|
538
514
|
" then approve with: npx miriad-viz next --timing-approved"
|
|
539
515
|
];
|
|
540
516
|
}
|
|
@@ -819,28 +795,38 @@ function computeNext(progress, existingFiles, logTails, flags = {}, dataSummary,
|
|
|
819
795
|
}
|
|
820
796
|
output.push("", " Run `npx miriad-viz curate` to generate scaffolds.");
|
|
821
797
|
output.push("");
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
"
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
798
|
+
if (vizCuration && vizCuration.narration.length > 0) {
|
|
799
|
+
output.push(" Edit viz-curation.json for narration placement + chat pills:");
|
|
800
|
+
output.push(" \u2022 narration array \u2014 progress points with clipDuration");
|
|
801
|
+
output.push(" \u2022 pills array \u2014 progress + duration + speaker + text");
|
|
802
|
+
output.push(" \u2022 Anchor pills (auto-generated from quotes) are marked [anchor]");
|
|
803
|
+
output.push(" Run `npx miriad-viz next` again to see updated vizSpeed and table.");
|
|
804
|
+
} else {
|
|
805
|
+
output.push(" Remember: the scaffold gives you RAW events (activity density).");
|
|
806
|
+
output.push(" You must ADD milestone events with labels at narration-aligned timestamps.");
|
|
807
|
+
output.push(" Each narration line needs at least one labeled event in its time range.");
|
|
808
|
+
output.push(
|
|
809
|
+
" Raw messages (messages-full.json) are your SOURCE \u2014 read them to find content."
|
|
810
|
+
);
|
|
811
|
+
output.push(
|
|
812
|
+
" timeline-events.json is your OUTPUT \u2014 the chat pills that appear on screen."
|
|
813
|
+
);
|
|
814
|
+
output.push("");
|
|
815
|
+
output.push(" Edit viz-curation.json for narration placement + chat pills:");
|
|
816
|
+
output.push(" \u2022 narration array \u2014 progress points with clipDuration");
|
|
817
|
+
output.push(" \u2022 pills array \u2014 progress + duration + speaker + text");
|
|
818
|
+
output.push(" Run `npx miriad-viz next` again to see updated vizSpeed and table.");
|
|
819
|
+
}
|
|
834
820
|
output.push("", " When done: npx miriad-viz next");
|
|
835
821
|
return { action: "waiting_for_edit", step, progress, output };
|
|
836
822
|
}
|
|
837
823
|
output.push("", "\u{1F4CB} Curate Editorial Content");
|
|
838
824
|
output.push("");
|
|
839
825
|
output.push(" This step generates scaffold files for editorial content:");
|
|
826
|
+
output.push(" \u2022 viz-curation.json \u2014 unified narration + pills (PRIMARY editing target)");
|
|
840
827
|
output.push(" \u2022 retro-page-data.json \u2014 phases, milestones, featured quotes, narration");
|
|
841
828
|
output.push(" \u2022 retro-page-quotes.json \u2014 additional standout quotes");
|
|
842
|
-
output.push(" \u2022 timeline-events.json \u2014
|
|
843
|
-
output.push(" \u2022 viz-curation.json \u2014 unified narration + pills (progress-based)");
|
|
829
|
+
output.push(" \u2022 timeline-events.json \u2014 legacy/retro-page format (optional, for retro page)");
|
|
844
830
|
output.push("");
|
|
845
831
|
output.push(" \u26A0\uFE0F Three concepts \u2014 understand these before editing:");
|
|
846
832
|
output.push(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500");
|
|
@@ -868,6 +854,7 @@ function computeNext(progress, existingFiles, logTails, flags = {}, dataSummary,
|
|
|
868
854
|
output.push(" - Cluster narration in dense areas, space out in dead zones");
|
|
869
855
|
output.push(" - ADD 15-20 milestone events with labels as pills");
|
|
870
856
|
output.push(' - Each pill: { speaker: "...", text: "...", progress: N, duration: N }');
|
|
857
|
+
output.push(" - Auto-generated anchor pills are marked with anchor: true");
|
|
871
858
|
output.push(" - Goal: every narration line should have related pills nearby");
|
|
872
859
|
output.push(" - Anchors shown in next output \u2014 place related pills nearby");
|
|
873
860
|
output.push(" - Run next again to see updated vizSpeed and table");
|
|
@@ -882,7 +869,7 @@ function computeNext(progress, existingFiles, logTails, flags = {}, dataSummary,
|
|
|
882
869
|
output.push("");
|
|
883
870
|
output.push(" \u2501\u2501\u2501 SCHEMA EXAMPLES (copy these as starting templates) \u2501\u2501\u2501");
|
|
884
871
|
output.push("");
|
|
885
|
-
output.push(" \u{1F4C4} viz-curation.json:");
|
|
872
|
+
output.push(" \u{1F4C4} viz-curation.json (PRIMARY \u2014 edit this):");
|
|
886
873
|
output.push(" ```json");
|
|
887
874
|
output.push(" {");
|
|
888
875
|
output.push(' "meta": { "totalClipDuration": 226.5, "totalProgress": 100,');
|
|
@@ -899,7 +886,7 @@ function computeNext(progress, existingFiles, logTails, flags = {}, dataSummary,
|
|
|
899
886
|
output.push(" ],");
|
|
900
887
|
output.push(' "pills": [');
|
|
901
888
|
output.push(' { "speaker": "snorre", "text": "THE NEW CLIP DOES NOT HAVE SOUND",');
|
|
902
|
-
output.push(' "progress": 7.1, "duration": 2.0 },');
|
|
889
|
+
output.push(' "progress": 7.1, "duration": 2.0, "anchor": true },');
|
|
903
890
|
output.push(' { "speaker": "bob", "text": "rendering now with narration scale 3.5",');
|
|
904
891
|
output.push(' "progress": 8.0, "duration": 1.5 }');
|
|
905
892
|
output.push(" ]");
|
|
@@ -908,8 +895,11 @@ function computeNext(progress, existingFiles, logTails, flags = {}, dataSummary,
|
|
|
908
895
|
output.push(" \u26A0\uFE0F narration.progress is a SINGLE number (0-100), not a range.");
|
|
909
896
|
output.push(" \u26A0\uFE0F pills are a SEPARATE top-level array, not nested under narration lines.");
|
|
910
897
|
output.push(" \u26A0\uFE0F vizSpeed is COMPUTED (gap/clipDuration) \u2014 shown in the table, not stored.");
|
|
898
|
+
output.push(
|
|
899
|
+
" \u26A0\uFE0F anchor: true marks auto-generated pills \u2014 add your own pills WITHOUT anchor."
|
|
900
|
+
);
|
|
911
901
|
output.push("");
|
|
912
|
-
output.push(" \u{1F4C4} timeline-events.json:");
|
|
902
|
+
output.push(" \u{1F4C4} timeline-events.json (legacy \u2014 for retro page only):");
|
|
913
903
|
output.push(" ```json");
|
|
914
904
|
output.push(" [");
|
|
915
905
|
output.push(' { "type": "message", "t": "2026-02-20T14:30:00Z", "from": "bob",');
|
|
@@ -2095,7 +2085,7 @@ async function main() {
|
|
|
2095
2085
|
case "scaffold-curation": {
|
|
2096
2086
|
const { projectDir, progress } = requireProject();
|
|
2097
2087
|
const { ScriptFileSchema } = await import("./script-V3LKE4ZU.js");
|
|
2098
|
-
const { generateVizCuration } = await import("./generate-viz-curation-
|
|
2088
|
+
const { generateVizCuration } = await import("./generate-viz-curation-R6SUTONK.js");
|
|
2099
2089
|
const { readAudioDurations } = await import("./read-audio-durations-TIGU4A7K.js");
|
|
2100
2090
|
const { writeFileSync, mkdirSync: mkdirSync2 } = await import("fs");
|
|
2101
2091
|
const dataDir = resolve3(projectDir, progress.project.dataDir);
|
|
@@ -133,19 +133,19 @@ See [Voice Generation Guide](./miriad-viz-voice-generation.md) for the full guid
|
|
|
133
133
|
|
|
134
134
|
See [Viz Timing Guide](./miriad-viz-viz-timing.md) for the full guide.
|
|
135
135
|
|
|
136
|
-
**This is where the video's rhythm is crafted.** You scaffold a
|
|
136
|
+
**This is where the video's rhythm is crafted.** You scaffold a curation file, adjust narration placement and pills, and iterate with the human watching the preview.
|
|
137
137
|
|
|
138
138
|
**The iterate loop:**
|
|
139
139
|
```bash
|
|
140
|
-
# 1. Scaffold
|
|
141
|
-
npx miriad-viz@latest scaffold-
|
|
140
|
+
# 1. Scaffold curation file (once)
|
|
141
|
+
npx miriad-viz@latest scaffold-curation
|
|
142
142
|
|
|
143
|
-
# 2. Edit
|
|
144
|
-
npx miriad-viz@latest preview --from-
|
|
143
|
+
# 2. Edit viz-curation.json, then run ONE command:
|
|
144
|
+
npx miriad-viz@latest preview --from-curation
|
|
145
145
|
# This auto-chains: generate-timing → transform → preview
|
|
146
146
|
```
|
|
147
147
|
|
|
148
|
-
You edit ONE file (`
|
|
148
|
+
You edit ONE file (`viz-curation.json`), run ONE command, see the result. Repeat until the human approves.
|
|
149
149
|
|
|
150
150
|
**Timeline padding:**
|
|
151
151
|
```bash
|
|
@@ -185,7 +185,7 @@ See [Video Export](./miriad-viz-video.md) for the full guide. Mechanical step
|
|
|
185
185
|
| Extract (git) | `npx miriad-viz@latest extract &> extract.log &` |
|
|
186
186
|
| Extract (chat) | `./extract-channel.sh ./raw-data &> chat-extract.log &` |
|
|
187
187
|
| Transform | `npx miriad-viz@latest transform &> transform.log &` |
|
|
188
|
-
| Preview (from
|
|
188
|
+
| Preview (from curation) | `npx miriad-viz@latest preview --from-curation &> preview.log &` |
|
|
189
189
|
| Render (video) | `pnpm render &> render.log &` |
|
|
190
190
|
|
|
191
191
|
Pattern: start in background → set alarm (1-min for extract/transform, 3-min for render) → poll + report → repeat until done.
|
|
@@ -4,10 +4,11 @@
|
|
|
4
4
|
|
|
5
5
|
Place narration on the timeline and decide how fast the viz moves. This is where the video's **pacing** is crafted — the rhythm of fast and slow, tension and release.
|
|
6
6
|
|
|
7
|
-
Voice clip durations are fixed facts (from Step 3). You control **
|
|
8
|
-
1.
|
|
9
|
-
2.
|
|
10
|
-
|
|
7
|
+
Voice clip durations are fixed facts (from Step 3). You control **two things:**
|
|
8
|
+
1. Where each narration line sits on the 0-100 progress scale (`progress`)
|
|
9
|
+
2. Which chat pills appear and where (`pills` array)
|
|
10
|
+
|
|
11
|
+
vizSpeed is **computed mechanically**: `gap / clipDuration`. You control it indirectly by moving progress points closer (slower) or further apart (faster).
|
|
11
12
|
|
|
12
13
|
## Files
|
|
13
14
|
|
|
@@ -15,131 +16,131 @@ Voice clip durations are fixed facts (from Step 3). You control **three things:*
|
|
|
15
16
|
|------|-----------|-------------|
|
|
16
17
|
| `data/script.json` | Read | The narrative script |
|
|
17
18
|
| `audio/*.mp3` | Read | Voice clips (durations measured automatically) |
|
|
18
|
-
| `data/
|
|
19
|
-
| `data/timing.json` | Generated | Engine-facing timing (generated by
|
|
20
|
-
| `output/viz-data.json` | Generated | Final viz data (generated by
|
|
19
|
+
| `data/viz-curation.json` | **Write** | Your narration placement + chat pills |
|
|
20
|
+
| `data/timing.json` | Generated | Engine-facing timing (generated by transform) |
|
|
21
|
+
| `output/viz-data.json` | Generated | Final viz data (generated by transform) |
|
|
21
22
|
|
|
22
23
|
## Getting Started
|
|
23
24
|
|
|
24
|
-
### 1. Scaffold the
|
|
25
|
+
### 1. Scaffold the Curation File
|
|
25
26
|
|
|
26
27
|
```bash
|
|
27
|
-
npx miriad-viz@latest scaffold-
|
|
28
|
+
npx miriad-viz@latest scaffold-curation
|
|
28
29
|
```
|
|
29
30
|
|
|
30
|
-
This reads `script.json` + measures audio clip durations → generates `
|
|
31
|
+
This reads `script.json` + measures audio clip durations → generates `viz-curation.json` with density-proportional defaults. The output is ready to preview immediately — you only edit where you want to deviate from defaults.
|
|
31
32
|
|
|
32
|
-
### 2. Understand the
|
|
33
|
+
### 2. Understand the Curation File
|
|
33
34
|
|
|
34
35
|
```json
|
|
35
36
|
{
|
|
36
|
-
"
|
|
37
|
-
|
|
38
|
-
"
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
"
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
"
|
|
50
|
-
{ "
|
|
51
|
-
"
|
|
37
|
+
"meta": { "totalClipDuration": 226.5, "totalProgress": 100,
|
|
38
|
+
"phases": [{ "id": "bootstrap", "label": "Bootstrap", "progress": [0, 22] }] },
|
|
39
|
+
"narration": [
|
|
40
|
+
{ "id": "narrator-01", "type": "narrator", "speaker": "narrator",
|
|
41
|
+
"text": "February 9th. Midnight.", "clipDuration": 3.2, "progress": 3.5 },
|
|
42
|
+
{ "id": "narrator-02", "type": "narrator", "speaker": "narrator",
|
|
43
|
+
"text": "The team had been shipping for 12 hours.",
|
|
44
|
+
"clipDuration": 4.8, "progress": 8.3 },
|
|
45
|
+
{ "id": "snorre-04", "type": "quote", "speaker": "snorre",
|
|
46
|
+
"text": "Full throttle.", "clipDuration": 1.4, "progress": 45.0 }
|
|
47
|
+
],
|
|
48
|
+
"pills": [
|
|
49
|
+
{ "speaker": "snorre", "text": "Full throttle.", "progress": 45.0,
|
|
50
|
+
"duration": 2.0, "anchor": true },
|
|
51
|
+
{ "speaker": "bob", "text": "rendering now", "progress": 46.5,
|
|
52
|
+
"duration": 1.5 }
|
|
52
53
|
]
|
|
53
54
|
}
|
|
54
55
|
```
|
|
55
56
|
|
|
56
|
-
### What You Can Edit
|
|
57
|
+
### What You Can Edit
|
|
58
|
+
|
|
59
|
+
**Narration array** — adjust `progress` (0-100) to control where each line plays:
|
|
60
|
+
- Move points **closer together** → smaller gap → lower vizSpeed → slower playback
|
|
61
|
+
- Move points **further apart** → larger gap → higher vizSpeed → faster playback
|
|
57
62
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
63
|
+
**Pills array** — add/edit/remove chat pills:
|
|
64
|
+
- `speaker` — who says it
|
|
65
|
+
- `text` — what appears on screen (can be fabricated)
|
|
66
|
+
- `progress` — where it appears (0-100)
|
|
67
|
+
- `duration` — how long it stays visible (in progress-space units)
|
|
68
|
+
- Pills with `anchor: true` are auto-generated from quote lines — add your own WITHOUT anchor
|
|
64
69
|
|
|
65
70
|
### What You Must NOT Edit (read-only)
|
|
66
71
|
|
|
67
72
|
| Field | Why |
|
|
68
73
|
|-------|-----|
|
|
69
74
|
| `id` | Matches script.json and audio filenames |
|
|
70
|
-
| `text` | From script.json — shown so you can reason about content
|
|
75
|
+
| `text` (narration) | From script.json — shown so you can reason about content |
|
|
71
76
|
| `clipDuration` | Measured from audio clips — this is a fact, not a choice |
|
|
77
|
+
| `meta` | Computed by the lib — phases, totalClipDuration |
|
|
72
78
|
|
|
73
|
-
|
|
79
|
+
## How vizSpeed Works
|
|
74
80
|
|
|
75
|
-
|
|
76
|
-
|-------|-------------|
|
|
77
|
-
| `leadInSec` | Silence before the first line (intro breathing room) |
|
|
78
|
-
| `tailOutSec` | Silence after the last line (outro) |
|
|
79
|
-
| `defaultPauseSec` | Default pause between lines (when `pauseAfter` is absent) |
|
|
80
|
-
| `defaultVizSpeed` | Default viz speed (when `vizSpeed`/`gapVizSpeed` are absent) |
|
|
81
|
-
|
|
82
|
-
## How Speed Works
|
|
81
|
+
vizSpeed is **computed**, not set directly:
|
|
83
82
|
|
|
84
83
|
```
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
└─────────────────┘ └─────┘ └─────────────────┘ └─────┘
|
|
89
|
-
↑ speed DURING clip ↑ gapVizSpeed ↑ speed DURING clip ↑ gapVizSpeed
|
|
90
|
-
(audio playing) (silence/pause) (audio playing) (silence/pause)
|
|
84
|
+
vizSpeed = gap / clipDuration
|
|
85
|
+
|
|
86
|
+
gap = distance to next narration point in progress-space
|
|
91
87
|
```
|
|
92
88
|
|
|
89
|
+
**Example:**
|
|
90
|
+
- narrator-01 at progress 3.5, clipDuration 5.0s
|
|
91
|
+
- narrator-02 at progress 8.3
|
|
92
|
+
- gap = 8.3 - 3.5 = 4.8
|
|
93
|
+
- vizSpeed = 4.8 / 5.0 = 0.96x
|
|
94
|
+
|
|
93
95
|
**vizSpeed is a multiplier on project-time-per-second:**
|
|
94
96
|
- `1.0` = default rate
|
|
95
97
|
- `2.0` = viz covers project time 2× faster → **less screen time** (fast-forward)
|
|
96
98
|
- `0.5` = viz covers project time 2× slower → **more screen time** (slow motion)
|
|
97
99
|
|
|
98
|
-
**Key insight:** higher vizSpeed = faster
|
|
99
|
-
|
|
100
|
-
**`gapVizSpeed` defaults to `defaultVizSpeed`, NOT to the line's `vizSpeed`.** This is intentional — a slow dramatic line (`vizSpeed: 0.5`) shouldn't automatically make its pause slow too. The pause speed is a separate creative choice.
|
|
100
|
+
**Key insight:** wider gaps = higher vizSpeed = faster. Narrower gaps = lower vizSpeed = slower. The curation table (shown by `next`) displays concrete vizSpeed per line so you don't have to do the math.
|
|
101
101
|
|
|
102
102
|
## The Iterate Loop
|
|
103
103
|
|
|
104
|
-
After every edit to `
|
|
104
|
+
After every edit to `viz-curation.json`, run ONE command:
|
|
105
105
|
|
|
106
106
|
```bash
|
|
107
|
-
npx miriad-viz@latest preview --from-
|
|
107
|
+
npx miriad-viz@latest preview --from-curation
|
|
108
108
|
```
|
|
109
109
|
|
|
110
110
|
This auto-chains three operations:
|
|
111
|
-
1.
|
|
111
|
+
1. Derives `timing.json` from viz-curation.json + project time range
|
|
112
112
|
2. `transform` — produces `viz-data.json` with timing-aware narration
|
|
113
113
|
3. Opens the preview viewer
|
|
114
114
|
|
|
115
|
-
**You edit ONE file, run ONE command, see the result.** The three-step chain is an implementation detail.
|
|
115
|
+
**You edit ONE file (viz-curation.json), run ONE command, see the result.** The three-step chain is an implementation detail.
|
|
116
116
|
|
|
117
|
-
### The
|
|
117
|
+
### The Curation Table
|
|
118
118
|
|
|
119
|
-
`
|
|
119
|
+
`next` prints a table showing computed vizSpeed and density per line:
|
|
120
120
|
|
|
121
121
|
```
|
|
122
|
-
|
|
123
|
-
narrator-
|
|
124
|
-
|
|
125
|
-
|
|
122
|
+
LINE | CLIP | PROGRESS | GAP | vizSpeed | MSGS | COMMITS
|
|
123
|
+
narrator-01 | 5.2s | @ 3.5 | 3.6 | 0.69x | 182 | 12
|
|
124
|
+
snorre-01 | 3.2s | @ 7.1 | 4.9 | 1.53x | 47 | 3
|
|
125
|
+
narrator-02 | 4.1s | @ 12.0 | 16.0 | 3.90x | 0 | 0 ← dead zone
|
|
126
126
|
```
|
|
127
127
|
|
|
128
128
|
Use this to catch problems **before watching the preview:**
|
|
129
|
-
-
|
|
130
|
-
-
|
|
131
|
-
-
|
|
129
|
+
- `← dead zone` — zero activity in this region. Space out narration (larger gaps) to skip through it.
|
|
130
|
+
- High vizSpeed (> 3x) — the viz is moving very fast here. Is that intentional?
|
|
131
|
+
- Low vizSpeed (< 0.5x) — the viz is very slow. Enough content to fill the screen time?
|
|
132
|
+
- Anchors — quote-type lines auto-generate pills. Place related pills nearby.
|
|
132
133
|
|
|
133
|
-
##
|
|
134
|
+
## Pacing Reasoning — Creative Choice, Not Formula
|
|
134
135
|
|
|
135
|
-
The `next` output shows **event density** per
|
|
136
|
+
The `next` output shows **event density** per narration line — how many messages and commits happened in each line's progress window. Use this as input, not as a rule:
|
|
136
137
|
|
|
137
138
|
**Guidelines (not rules):**
|
|
138
|
-
- **Dead
|
|
139
|
-
- **
|
|
140
|
-
- **Dramatic reveal** →
|
|
141
|
-
- **Emotional moment** → low
|
|
142
|
-
- **Montage** → high
|
|
139
|
+
- **Dead zones** (zero events) → space out narration points (larger gaps, higher vizSpeed)
|
|
140
|
+
- **Dense areas** (many events) → cluster narration points (smaller gaps, lower vizSpeed)
|
|
141
|
+
- **Dramatic reveal** → place narration points close together before the reveal (slow buildup)
|
|
142
|
+
- **Emotional moment** → narrow gap (low vizSpeed, slow down for impact)
|
|
143
|
+
- **Montage** → wide gap (high vizSpeed, time-lapse of activity)
|
|
143
144
|
|
|
144
145
|
**Trust your creative judgment.** The data tells you what happened. You and the human decide how it should feel. A quiet period might deserve slow pacing if it's the calm before the storm. A busy period might deserve fast pacing if it's routine work.
|
|
145
146
|
|
|
@@ -148,23 +149,15 @@ The `next` output shows **event density** per script line — how many PRs, comm
|
|
|
148
149
|
Share the preview link and watch together. The human evaluates:
|
|
149
150
|
|
|
150
151
|
- **Does the pacing feel right?** Too fast? Too slow? Uneven?
|
|
151
|
-
- **Do the
|
|
152
|
+
- **Do the pills land?** Are they readable? Too many? Too few?
|
|
152
153
|
- **Does the narration sync with the visuals?** When the narrator says "then everything broke," is the viz showing the crisis?
|
|
153
154
|
- **Is the total duration right?** Too long for the content? Too short to absorb?
|
|
154
155
|
|
|
155
156
|
Common feedback and how to respond:
|
|
156
|
-
- *"Slow down the middle section"* →
|
|
157
|
-
- *"
|
|
158
|
-
- *"
|
|
159
|
-
- *"The whole thing is too long"* →
|
|
160
|
-
|
|
161
|
-
## Validation
|
|
162
|
-
|
|
163
|
-
`generate-timing` validates the pacing file and warns about:
|
|
164
|
-
- Total wall-clock time < 30s or > 30min (unusual for typical scripts)
|
|
165
|
-
- Viz coverage < 50% (narration only covers half the timeline — intentional?)
|
|
166
|
-
- Negative values (invalid)
|
|
167
|
-
- Missing line IDs (script.json has lines not in pacing.json)
|
|
157
|
+
- *"Slow down the middle section"* → move narration points closer together in that region
|
|
158
|
+
- *"Skip through the overnight period faster"* → space out narration points in that region
|
|
159
|
+
- *"Add a pill when Bob says that"* → add a pill to the pills array at the right progress point
|
|
160
|
+
- *"The whole thing is too long"* → space out narration points globally (wider gaps)
|
|
168
161
|
|
|
169
162
|
## What You Cannot Control (v1)
|
|
170
163
|
|
|
@@ -172,12 +165,12 @@ Be honest with the human about these boundaries:
|
|
|
172
165
|
|
|
173
166
|
| Cannot control | Why | Future |
|
|
174
167
|
|---|---|---|
|
|
175
|
-
| **Visual emphasis** — can't zoom into an agent, hide a band, or highlight a node at a specific moment | Viz is a pure function of progress. No per-moment visual overrides. | Camera directives
|
|
176
|
-
| **Cross-fades between lines** — every line cuts to the next, no overlap or blend | Audio clips are discrete files, no mixing in v1 | Crossfade
|
|
177
|
-
| **What data appears** — can't add/remove agents, PRs, or events from the viz | Data comes from extraction, not
|
|
168
|
+
| **Visual emphasis** — can't zoom into an agent, hide a band, or highlight a node at a specific moment | Viz is a pure function of progress. No per-moment visual overrides. | Camera directives |
|
|
169
|
+
| **Cross-fades between lines** — every line cuts to the next, no overlap or blend | Audio clips are discrete files, no mixing in v1 | Crossfade support |
|
|
170
|
+
| **What data appears** — can't add/remove agents, PRs, or events from the viz | Data comes from extraction, not curation | Selective data filtering |
|
|
178
171
|
| **Layout or visual style** — can't change colors, positions, or rendering | Renderer is fixed, driven by viz-data | Style overrides per phase |
|
|
179
172
|
|
|
180
|
-
You control **pacing** (
|
|
173
|
+
You control **pacing** (via narration placement) and **pills** (what text appears on screen). The visual content is determined by the extracted data and the renderer. This is by design — it keeps the curation file simple and the iterate loop fast.
|
|
181
174
|
|
|
182
175
|
## When to Stop for Human Review
|
|
183
176
|
|
|
@@ -187,4 +180,4 @@ You control **pacing** (how fast time moves) and **audio** (what you hear). The
|
|
|
187
180
|
npx miriad-viz@latest next --timing-approved
|
|
188
181
|
```
|
|
189
182
|
|
|
190
|
-
**Timing is now locked.** Step 5 (Sound Design) adds SFX and music on top of the locked timing. Changes to
|
|
183
|
+
**Timing is now locked.** Step 5 (Sound Design) adds SFX and music on top of the locked timing. Changes to curation after this point require going back to this step.
|
|
@@ -21,7 +21,7 @@ echo $ELEVENLABS_API_KEY
|
|
|
21
21
|
|
|
22
22
|
**If missing:** Tell the human: "ElevenLabs API key not configured. To add narration, set `ELEVENLABS_API_KEY` in your environment. Without it, I can skip voices — the viz will use estimated durations based on text length, and the video will be silent."
|
|
23
23
|
|
|
24
|
-
**No API key is not a blocker.** The pipeline continues without audio — `scaffold-
|
|
24
|
+
**No API key is not a blocker.** The pipeline continues without audio — `scaffold-curation` estimates clip durations from text length, and the viz renders with text-only narration. Audio can be added later.
|
|
25
25
|
|
|
26
26
|
## The Filename Convention
|
|
27
27
|
|
|
@@ -35,7 +35,7 @@ script.json line: { "id": "lead-03", ... }
|
|
|
35
35
|
audio file: audio/lead-03.mp3
|
|
36
36
|
```
|
|
37
37
|
|
|
38
|
-
`scaffold-
|
|
38
|
+
`scaffold-curation` (Step 4) looks up clips by this convention. Wrong filenames = missing audio = broken timing.
|
|
39
39
|
|
|
40
40
|
## Workflow
|
|
41
41
|
|
|
@@ -103,7 +103,7 @@ After generating all clips, measure their durations:
|
|
|
103
103
|
ffprobe -v quiet -show_entries format=duration -of csv=p=0 audio/narrator-01.mp3
|
|
104
104
|
```
|
|
105
105
|
|
|
106
|
-
You don't need to record these manually — `scaffold-
|
|
106
|
+
You don't need to record these manually — `scaffold-curation` (Step 4) reads them automatically from the audio files.
|
|
107
107
|
|
|
108
108
|
### 5. Human Review
|
|
109
109
|
|