@olegkoval/agent-skills 1.60.0 → 1.62.0
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/.github/copilot-instructions.md +2 -0
- package/.github/prompts/carousel-maker.prompt.md +102 -0
- package/.github/prompts/shorts-animator.prompt.md +103 -0
- package/.kiro/steering/carousel-maker.md +101 -0
- package/.kiro/steering/shorts-animator.md +102 -0
- package/.windsurf/rules/carousel-maker.md +100 -0
- package/.windsurf/rules/shorts-animator.md +101 -0
- package/README.md +4 -2
- package/adapters/claude/olko-creative/skills/carousel-maker/SKILL.md +112 -0
- package/adapters/claude/olko-creative/skills/carousel-maker/scripts/cm.ts +220 -0
- package/adapters/claude/olko-creative/skills/carousel-maker/scripts/domain/check.ts +68 -0
- package/adapters/claude/olko-creative/skills/carousel-maker/scripts/domain/layout.ts +75 -0
- package/adapters/claude/olko-creative/skills/carousel-maker/scripts/domain/schema.ts +54 -0
- package/adapters/claude/olko-creative/skills/carousel-maker/scripts/domain/types.ts +33 -0
- package/adapters/claude/olko-creative/skills/carousel-maker/scripts/edge/io.ts +87 -0
- package/adapters/claude/olko-creative/skills/carousel-maker/scripts/package.json +4 -0
- package/adapters/claude/olko-creative/skills/carousel-maker/scripts/test/domain.test.ts +99 -0
- package/adapters/claude/olko-creative/skills/carousel-maker/scripts/tsconfig.json +16 -0
- package/adapters/claude/olko-creative/skills/shorts-animator/SKILL.md +113 -0
- package/adapters/claude/olko-creative/skills/shorts-animator/references/kit.md +59 -0
- package/adapters/claude/olko-creative/skills/shorts-animator/references/looks.md +32 -0
- package/adapters/claude/olko-creative/skills/shorts-animator/references/writing.md +9 -0
- package/adapters/claude/olko-creative/skills/shorts-animator/scripts/audio/compose.ts +290 -0
- package/adapters/claude/olko-creative/skills/shorts-animator/scripts/audio/dsp.ts +193 -0
- package/adapters/claude/olko-creative/skills/shorts-animator/scripts/audio/instruments.ts +185 -0
- package/adapters/claude/olko-creative/skills/shorts-animator/scripts/audio/master.ts +55 -0
- package/adapters/claude/olko-creative/skills/shorts-animator/scripts/audio/soundtrack.ts +119 -0
- package/adapters/claude/olko-creative/skills/shorts-animator/scripts/domain/captions.ts +35 -0
- package/adapters/claude/olko-creative/skills/shorts-animator/scripts/domain/check.ts +96 -0
- package/adapters/claude/olko-creative/skills/shorts-animator/scripts/domain/look.ts +26 -0
- package/adapters/claude/olko-creative/skills/shorts-animator/scripts/domain/patch.ts +38 -0
- package/adapters/claude/olko-creative/skills/shorts-animator/scripts/domain/schema.ts +81 -0
- package/adapters/claude/olko-creative/skills/shorts-animator/scripts/domain/timeline.ts +75 -0
- package/adapters/claude/olko-creative/skills/shorts-animator/scripts/domain/types.ts +91 -0
- package/adapters/claude/olko-creative/skills/shorts-animator/scripts/edge/chrome.ts +169 -0
- package/adapters/claude/olko-creative/skills/shorts-animator/scripts/edge/doctor.ts +52 -0
- package/adapters/claude/olko-creative/skills/shorts-animator/scripts/edge/media.ts +105 -0
- package/adapters/claude/olko-creative/skills/shorts-animator/scripts/edge/page.ts +53 -0
- package/adapters/claude/olko-creative/skills/shorts-animator/scripts/edge/probe.ts +75 -0
- package/adapters/claude/olko-creative/skills/shorts-animator/scripts/edge/sound.ts +46 -0
- package/adapters/claude/olko-creative/skills/shorts-animator/scripts/edge/voice.ts +183 -0
- package/adapters/claude/olko-creative/skills/shorts-animator/scripts/looks/gouache.json +7 -0
- package/adapters/claude/olko-creative/skills/shorts-animator/scripts/looks/ink.json +6 -0
- package/adapters/claude/olko-creative/skills/shorts-animator/scripts/looks/oil.json +7 -0
- package/adapters/claude/olko-creative/skills/shorts-animator/scripts/looks/pastel.json +7 -0
- package/adapters/claude/olko-creative/skills/shorts-animator/scripts/looks/watercolor.json +6 -0
- package/adapters/claude/olko-creative/skills/shorts-animator/scripts/package.json +4 -0
- package/adapters/claude/olko-creative/skills/shorts-animator/scripts/runtime/globals.d.ts +199 -0
- package/adapters/claude/olko-creative/skills/shorts-animator/scripts/runtime/kit.ts +856 -0
- package/adapters/claude/olko-creative/skills/shorts-animator/scripts/runtime/paint.ts +268 -0
- package/adapters/claude/olko-creative/skills/shorts-animator/scripts/runtime/stage.ts +181 -0
- package/adapters/claude/olko-creative/skills/shorts-animator/scripts/runtime/tsconfig.json +12 -0
- package/adapters/claude/olko-creative/skills/shorts-animator/scripts/sa.ts +262 -0
- package/adapters/claude/olko-creative/skills/shorts-animator/scripts/test/domain.test.ts +107 -0
- package/adapters/claude/olko-creative/skills/shorts-animator/scripts/tsconfig.json +16 -0
- package/adapters/claude/olko-creative/skills/shorts-animator/scripts/tts.py +20 -0
- package/adapters/claude/olko-github-pr/skills/lekker-review/SKILL.md +30 -0
- package/adapters/claude/olko-github-pr/skills/lekker-review/references/context-gathering.md +3 -0
- package/adapters/claude/olko-github-pr/skills/lekker-review/scripts/finding-state.mjs +207 -0
- package/adapters/codex/olko-creative/README.md +2 -0
- package/adapters/cursor/olko-creative/skills/carousel-maker/SKILL.md +112 -0
- package/adapters/cursor/olko-creative/skills/carousel-maker/scripts/cm.ts +220 -0
- package/adapters/cursor/olko-creative/skills/carousel-maker/scripts/domain/check.ts +68 -0
- package/adapters/cursor/olko-creative/skills/carousel-maker/scripts/domain/layout.ts +75 -0
- package/adapters/cursor/olko-creative/skills/carousel-maker/scripts/domain/schema.ts +54 -0
- package/adapters/cursor/olko-creative/skills/carousel-maker/scripts/domain/types.ts +33 -0
- package/adapters/cursor/olko-creative/skills/carousel-maker/scripts/edge/io.ts +87 -0
- package/adapters/cursor/olko-creative/skills/carousel-maker/scripts/package.json +4 -0
- package/adapters/cursor/olko-creative/skills/carousel-maker/scripts/test/domain.test.ts +99 -0
- package/adapters/cursor/olko-creative/skills/carousel-maker/scripts/tsconfig.json +16 -0
- package/adapters/cursor/olko-creative/skills/shorts-animator/SKILL.md +113 -0
- package/adapters/cursor/olko-creative/skills/shorts-animator/references/kit.md +59 -0
- package/adapters/cursor/olko-creative/skills/shorts-animator/references/looks.md +32 -0
- package/adapters/cursor/olko-creative/skills/shorts-animator/references/writing.md +9 -0
- package/adapters/cursor/olko-creative/skills/shorts-animator/scripts/audio/compose.ts +290 -0
- package/adapters/cursor/olko-creative/skills/shorts-animator/scripts/audio/dsp.ts +193 -0
- package/adapters/cursor/olko-creative/skills/shorts-animator/scripts/audio/instruments.ts +185 -0
- package/adapters/cursor/olko-creative/skills/shorts-animator/scripts/audio/master.ts +55 -0
- package/adapters/cursor/olko-creative/skills/shorts-animator/scripts/audio/soundtrack.ts +119 -0
- package/adapters/cursor/olko-creative/skills/shorts-animator/scripts/domain/captions.ts +35 -0
- package/adapters/cursor/olko-creative/skills/shorts-animator/scripts/domain/check.ts +96 -0
- package/adapters/cursor/olko-creative/skills/shorts-animator/scripts/domain/look.ts +26 -0
- package/adapters/cursor/olko-creative/skills/shorts-animator/scripts/domain/patch.ts +38 -0
- package/adapters/cursor/olko-creative/skills/shorts-animator/scripts/domain/schema.ts +81 -0
- package/adapters/cursor/olko-creative/skills/shorts-animator/scripts/domain/timeline.ts +75 -0
- package/adapters/cursor/olko-creative/skills/shorts-animator/scripts/domain/types.ts +91 -0
- package/adapters/cursor/olko-creative/skills/shorts-animator/scripts/edge/chrome.ts +169 -0
- package/adapters/cursor/olko-creative/skills/shorts-animator/scripts/edge/doctor.ts +52 -0
- package/adapters/cursor/olko-creative/skills/shorts-animator/scripts/edge/media.ts +105 -0
- package/adapters/cursor/olko-creative/skills/shorts-animator/scripts/edge/page.ts +53 -0
- package/adapters/cursor/olko-creative/skills/shorts-animator/scripts/edge/probe.ts +75 -0
- package/adapters/cursor/olko-creative/skills/shorts-animator/scripts/edge/sound.ts +46 -0
- package/adapters/cursor/olko-creative/skills/shorts-animator/scripts/edge/voice.ts +183 -0
- package/adapters/cursor/olko-creative/skills/shorts-animator/scripts/looks/gouache.json +7 -0
- package/adapters/cursor/olko-creative/skills/shorts-animator/scripts/looks/ink.json +6 -0
- package/adapters/cursor/olko-creative/skills/shorts-animator/scripts/looks/oil.json +7 -0
- package/adapters/cursor/olko-creative/skills/shorts-animator/scripts/looks/pastel.json +7 -0
- package/adapters/cursor/olko-creative/skills/shorts-animator/scripts/looks/watercolor.json +6 -0
- package/adapters/cursor/olko-creative/skills/shorts-animator/scripts/package.json +4 -0
- package/adapters/cursor/olko-creative/skills/shorts-animator/scripts/runtime/globals.d.ts +199 -0
- package/adapters/cursor/olko-creative/skills/shorts-animator/scripts/runtime/kit.ts +856 -0
- package/adapters/cursor/olko-creative/skills/shorts-animator/scripts/runtime/paint.ts +268 -0
- package/adapters/cursor/olko-creative/skills/shorts-animator/scripts/runtime/stage.ts +181 -0
- package/adapters/cursor/olko-creative/skills/shorts-animator/scripts/runtime/tsconfig.json +12 -0
- package/adapters/cursor/olko-creative/skills/shorts-animator/scripts/sa.ts +262 -0
- package/adapters/cursor/olko-creative/skills/shorts-animator/scripts/test/domain.test.ts +107 -0
- package/adapters/cursor/olko-creative/skills/shorts-animator/scripts/tsconfig.json +16 -0
- package/adapters/cursor/olko-creative/skills/shorts-animator/scripts/tts.py +20 -0
- package/adapters/grok/olko-creative/skills/carousel-maker/SKILL.md +112 -0
- package/adapters/grok/olko-creative/skills/carousel-maker/scripts/cm.ts +220 -0
- package/adapters/grok/olko-creative/skills/carousel-maker/scripts/domain/check.ts +68 -0
- package/adapters/grok/olko-creative/skills/carousel-maker/scripts/domain/layout.ts +75 -0
- package/adapters/grok/olko-creative/skills/carousel-maker/scripts/domain/schema.ts +54 -0
- package/adapters/grok/olko-creative/skills/carousel-maker/scripts/domain/types.ts +33 -0
- package/adapters/grok/olko-creative/skills/carousel-maker/scripts/edge/io.ts +87 -0
- package/adapters/grok/olko-creative/skills/carousel-maker/scripts/package.json +4 -0
- package/adapters/grok/olko-creative/skills/carousel-maker/scripts/test/domain.test.ts +99 -0
- package/adapters/grok/olko-creative/skills/carousel-maker/scripts/tsconfig.json +16 -0
- package/adapters/grok/olko-creative/skills/shorts-animator/SKILL.md +113 -0
- package/adapters/grok/olko-creative/skills/shorts-animator/references/kit.md +59 -0
- package/adapters/grok/olko-creative/skills/shorts-animator/references/looks.md +32 -0
- package/adapters/grok/olko-creative/skills/shorts-animator/references/writing.md +9 -0
- package/adapters/grok/olko-creative/skills/shorts-animator/scripts/audio/compose.ts +290 -0
- package/adapters/grok/olko-creative/skills/shorts-animator/scripts/audio/dsp.ts +193 -0
- package/adapters/grok/olko-creative/skills/shorts-animator/scripts/audio/instruments.ts +185 -0
- package/adapters/grok/olko-creative/skills/shorts-animator/scripts/audio/master.ts +55 -0
- package/adapters/grok/olko-creative/skills/shorts-animator/scripts/audio/soundtrack.ts +119 -0
- package/adapters/grok/olko-creative/skills/shorts-animator/scripts/domain/captions.ts +35 -0
- package/adapters/grok/olko-creative/skills/shorts-animator/scripts/domain/check.ts +96 -0
- package/adapters/grok/olko-creative/skills/shorts-animator/scripts/domain/look.ts +26 -0
- package/adapters/grok/olko-creative/skills/shorts-animator/scripts/domain/patch.ts +38 -0
- package/adapters/grok/olko-creative/skills/shorts-animator/scripts/domain/schema.ts +81 -0
- package/adapters/grok/olko-creative/skills/shorts-animator/scripts/domain/timeline.ts +75 -0
- package/adapters/grok/olko-creative/skills/shorts-animator/scripts/domain/types.ts +91 -0
- package/adapters/grok/olko-creative/skills/shorts-animator/scripts/edge/chrome.ts +169 -0
- package/adapters/grok/olko-creative/skills/shorts-animator/scripts/edge/doctor.ts +52 -0
- package/adapters/grok/olko-creative/skills/shorts-animator/scripts/edge/media.ts +105 -0
- package/adapters/grok/olko-creative/skills/shorts-animator/scripts/edge/page.ts +53 -0
- package/adapters/grok/olko-creative/skills/shorts-animator/scripts/edge/probe.ts +75 -0
- package/adapters/grok/olko-creative/skills/shorts-animator/scripts/edge/sound.ts +46 -0
- package/adapters/grok/olko-creative/skills/shorts-animator/scripts/edge/voice.ts +183 -0
- package/adapters/grok/olko-creative/skills/shorts-animator/scripts/looks/gouache.json +7 -0
- package/adapters/grok/olko-creative/skills/shorts-animator/scripts/looks/ink.json +6 -0
- package/adapters/grok/olko-creative/skills/shorts-animator/scripts/looks/oil.json +7 -0
- package/adapters/grok/olko-creative/skills/shorts-animator/scripts/looks/pastel.json +7 -0
- package/adapters/grok/olko-creative/skills/shorts-animator/scripts/looks/watercolor.json +6 -0
- package/adapters/grok/olko-creative/skills/shorts-animator/scripts/package.json +4 -0
- package/adapters/grok/olko-creative/skills/shorts-animator/scripts/runtime/globals.d.ts +199 -0
- package/adapters/grok/olko-creative/skills/shorts-animator/scripts/runtime/kit.ts +856 -0
- package/adapters/grok/olko-creative/skills/shorts-animator/scripts/runtime/paint.ts +268 -0
- package/adapters/grok/olko-creative/skills/shorts-animator/scripts/runtime/stage.ts +181 -0
- package/adapters/grok/olko-creative/skills/shorts-animator/scripts/runtime/tsconfig.json +12 -0
- package/adapters/grok/olko-creative/skills/shorts-animator/scripts/sa.ts +262 -0
- package/adapters/grok/olko-creative/skills/shorts-animator/scripts/test/domain.test.ts +107 -0
- package/adapters/grok/olko-creative/skills/shorts-animator/scripts/tsconfig.json +16 -0
- package/adapters/grok/olko-creative/skills/shorts-animator/scripts/tts.py +20 -0
- package/adapters/hermes/olko-creative/README.md +2 -0
- package/catalog/skills.json +46 -0
- package/package.json +1 -1
- package/plugins/olko-apple-kit/.claude-plugin/plugin.json +1 -1
- package/plugins/olko-creative/.claude-plugin/plugin.json +1 -1
- package/plugins/olko-creative/skills/carousel-maker/SKILL.md +111 -0
- package/plugins/olko-creative/skills/carousel-maker/scripts/cm.ts +220 -0
- package/plugins/olko-creative/skills/carousel-maker/scripts/domain/check.ts +68 -0
- package/plugins/olko-creative/skills/carousel-maker/scripts/domain/layout.ts +75 -0
- package/plugins/olko-creative/skills/carousel-maker/scripts/domain/schema.ts +54 -0
- package/plugins/olko-creative/skills/carousel-maker/scripts/domain/types.ts +33 -0
- package/plugins/olko-creative/skills/carousel-maker/scripts/edge/io.ts +87 -0
- package/plugins/olko-creative/skills/carousel-maker/scripts/package.json +4 -0
- package/plugins/olko-creative/skills/carousel-maker/scripts/test/domain.test.ts +99 -0
- package/plugins/olko-creative/skills/carousel-maker/scripts/tsconfig.json +16 -0
- package/plugins/olko-creative/skills/shorts-animator/SKILL.md +112 -0
- package/plugins/olko-creative/skills/shorts-animator/references/kit.md +59 -0
- package/plugins/olko-creative/skills/shorts-animator/references/looks.md +32 -0
- package/plugins/olko-creative/skills/shorts-animator/references/writing.md +9 -0
- package/plugins/olko-creative/skills/shorts-animator/scripts/audio/compose.ts +290 -0
- package/plugins/olko-creative/skills/shorts-animator/scripts/audio/dsp.ts +193 -0
- package/plugins/olko-creative/skills/shorts-animator/scripts/audio/instruments.ts +185 -0
- package/plugins/olko-creative/skills/shorts-animator/scripts/audio/master.ts +55 -0
- package/plugins/olko-creative/skills/shorts-animator/scripts/audio/soundtrack.ts +119 -0
- package/plugins/olko-creative/skills/shorts-animator/scripts/domain/captions.ts +35 -0
- package/plugins/olko-creative/skills/shorts-animator/scripts/domain/check.ts +96 -0
- package/plugins/olko-creative/skills/shorts-animator/scripts/domain/look.ts +26 -0
- package/plugins/olko-creative/skills/shorts-animator/scripts/domain/patch.ts +38 -0
- package/plugins/olko-creative/skills/shorts-animator/scripts/domain/schema.ts +81 -0
- package/plugins/olko-creative/skills/shorts-animator/scripts/domain/timeline.ts +75 -0
- package/plugins/olko-creative/skills/shorts-animator/scripts/domain/types.ts +91 -0
- package/plugins/olko-creative/skills/shorts-animator/scripts/edge/chrome.ts +169 -0
- package/plugins/olko-creative/skills/shorts-animator/scripts/edge/doctor.ts +52 -0
- package/plugins/olko-creative/skills/shorts-animator/scripts/edge/media.ts +105 -0
- package/plugins/olko-creative/skills/shorts-animator/scripts/edge/page.ts +53 -0
- package/plugins/olko-creative/skills/shorts-animator/scripts/edge/probe.ts +75 -0
- package/plugins/olko-creative/skills/shorts-animator/scripts/edge/sound.ts +46 -0
- package/plugins/olko-creative/skills/shorts-animator/scripts/edge/voice.ts +183 -0
- package/plugins/olko-creative/skills/shorts-animator/scripts/looks/gouache.json +7 -0
- package/plugins/olko-creative/skills/shorts-animator/scripts/looks/ink.json +6 -0
- package/plugins/olko-creative/skills/shorts-animator/scripts/looks/oil.json +7 -0
- package/plugins/olko-creative/skills/shorts-animator/scripts/looks/pastel.json +7 -0
- package/plugins/olko-creative/skills/shorts-animator/scripts/looks/watercolor.json +6 -0
- package/plugins/olko-creative/skills/shorts-animator/scripts/package.json +4 -0
- package/plugins/olko-creative/skills/shorts-animator/scripts/runtime/globals.d.ts +199 -0
- package/plugins/olko-creative/skills/shorts-animator/scripts/runtime/kit.ts +856 -0
- package/plugins/olko-creative/skills/shorts-animator/scripts/runtime/paint.ts +268 -0
- package/plugins/olko-creative/skills/shorts-animator/scripts/runtime/stage.ts +181 -0
- package/plugins/olko-creative/skills/shorts-animator/scripts/runtime/tsconfig.json +12 -0
- package/plugins/olko-creative/skills/shorts-animator/scripts/sa.ts +262 -0
- package/plugins/olko-creative/skills/shorts-animator/scripts/test/domain.test.ts +107 -0
- package/plugins/olko-creative/skills/shorts-animator/scripts/tsconfig.json +16 -0
- package/plugins/olko-creative/skills/shorts-animator/scripts/tts.py +20 -0
- package/plugins/olko-garmin-kit/.claude-plugin/plugin.json +1 -1
- package/plugins/olko-git-tools/.claude-plugin/plugin.json +1 -1
- package/plugins/olko-github-pr/.claude-plugin/plugin.json +1 -1
- package/plugins/olko-github-pr/skills/lekker-review/SKILL.md +30 -0
- package/plugins/olko-github-pr/skills/lekker-review/references/context-gathering.md +3 -0
- package/plugins/olko-github-pr/skills/lekker-review/scripts/finding-state.mjs +207 -0
- package/plugins/olko-github-pr/skills/lekker-review/workflow.js +46 -3
- package/plugins/olko-obsidian/.claude-plugin/plugin.json +1 -1
- package/plugins/olko-product/.claude-plugin/plugin.json +1 -1
- package/plugins/olko-reflection/.claude-plugin/plugin.json +1 -1
- package/plugins/olko-release/.claude-plugin/plugin.json +1 -1
- package/plugins/olko-skill-meta/.claude-plugin/plugin.json +1 -1
- package/plugins/olko-web-ops/.claude-plugin/plugin.json +1 -1
- package/scripts/lib/catalog.mjs +2 -1
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
// The soundtrack: every take at its moment on one voice track, the score, and the ducked mix.
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { PRESETS, renderSoundtrack } from "../audio/soundtrack.ts";
|
|
4
|
+
import type { Short, Take, Timeline } from "../domain/types.ts";
|
|
5
|
+
import { mix, mkdir, pcm, SR, wav } from "./media.ts";
|
|
6
|
+
|
|
7
|
+
export const MOODS = ["tender", "hopeful", "night", "wonder", "playful"];
|
|
8
|
+
const LEAD_IN = 0.05;
|
|
9
|
+
|
|
10
|
+
type Placed = { readonly at: number; readonly data: Float32Array };
|
|
11
|
+
|
|
12
|
+
const place = (tl: Timeline) => (t: Take, i: number): readonly Placed[] => {
|
|
13
|
+
if (!t.file) return [];
|
|
14
|
+
const at = tl.voiceAt[i] ?? 0;
|
|
15
|
+
return t.slice
|
|
16
|
+
? [{ at: Math.round((at - LEAD_IN) * SR), data: pcm(t.file, Math.max(0, (t.from ?? 0) - LEAD_IN), t.dur + 0.15) }]
|
|
17
|
+
: [{ at: Math.round(at * SR), data: pcm(t.file) }];
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
/** Sums the takes into one buffer; a take that runs past the end is cut there. */
|
|
21
|
+
function voiceTrack(tl: Timeline, takes: readonly Placed[]): Float32Array {
|
|
22
|
+
const track = new Float32Array(Math.ceil((tl.duration + 0.5) * SR));
|
|
23
|
+
takes.forEach(({ at, data }) => data.subarray(0, Math.max(0, track.length - at)).forEach((x, j) => { track[at + j] = (track[at + j] ?? 0) + x; }));
|
|
24
|
+
return track;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/** Returns the mixed WAV the encoder muxes in. */
|
|
28
|
+
export function soundtrack(dir: string, s: Short, tl: Timeline, takes: readonly Take[]): string {
|
|
29
|
+
const a = mkdir(dir, "build", "audio");
|
|
30
|
+
const placed = takes.flatMap(place(tl));
|
|
31
|
+
const voice = path.join(a, "voice.wav");
|
|
32
|
+
wav(voice, voiceTrack(tl, placed));
|
|
33
|
+
const m = s.music;
|
|
34
|
+
const own = !MOODS.includes(m.mood) && !(m.mood in PRESETS);
|
|
35
|
+
const music = m.mood === "none" ? null : path.join(a, "music.wav");
|
|
36
|
+
if (music) {
|
|
37
|
+
renderSoundtrack(
|
|
38
|
+
{ duration: tl.duration, preset: own ? "tender" : m.mood, music: own ? path.resolve(dir, m.mood) : "auto", cues: [], outroAt: tl.duration - 1.5, musicStart: m.start },
|
|
39
|
+
music,
|
|
40
|
+
{ seed: m.seed ?? 11 },
|
|
41
|
+
);
|
|
42
|
+
}
|
|
43
|
+
const hasVoice = placed.length > 0;
|
|
44
|
+
const volume = m.volume ?? (hasVoice ? 0.42 : 0.9);
|
|
45
|
+
return mix({ voice, music, hasVoice, volume, duration: tl.duration }, path.join(a, "mix.wav"));
|
|
46
|
+
}
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
// The narrator: Kokoro-82M in a private Python, or the person's own recording, timed by transcript.
|
|
2
|
+
// Everything is pinned and the model files are checked by sha256, so a setup is the same setup.
|
|
3
|
+
import { spawnSync } from "node:child_process";
|
|
4
|
+
import crypto from "node:crypto";
|
|
5
|
+
import fs from "node:fs";
|
|
6
|
+
import os from "node:os";
|
|
7
|
+
import path from "node:path";
|
|
8
|
+
import { words } from "../domain/captions.ts";
|
|
9
|
+
import { isMap } from "../domain/schema.ts";
|
|
10
|
+
import type { Json, Short, Take } from "../domain/types.ts";
|
|
11
|
+
import { duration, mkdir, run } from "./media.ts";
|
|
12
|
+
|
|
13
|
+
const HOME = path.join(os.homedir(), ".cache", "shorts-animator");
|
|
14
|
+
const VENV = path.join(HOME, "venv");
|
|
15
|
+
const PY = path.join(VENV, process.platform === "win32" ? "Scripts/python.exe" : "bin/python");
|
|
16
|
+
const TTS = path.join(import.meta.dirname, "..", "tts.py");
|
|
17
|
+
const PACKAGES = ["kokoro-onnx==0.6.1", "soundfile==0.14.0"];
|
|
18
|
+
export const HYPERFRAMES = "hyperframes@0.8.150";
|
|
19
|
+
const NPX = process.platform === "win32" ? "npx.cmd" : "npx";
|
|
20
|
+
const RELEASE = "https://github.com/thewh1teagle/kokoro-onnx/releases/download/model-files-v1.0";
|
|
21
|
+
const MODEL = {
|
|
22
|
+
"kokoro-v1.0.onnx": "7d5df8ecf7d4b1878015a32686053fd0eebe2bc377234608764cc0ef3636a6c5",
|
|
23
|
+
"voices-v1.0.bin": "bca610b8308e8d99f32e6fe4197e7ec01679264efed0cac9140fe9c29f1fbf7d",
|
|
24
|
+
};
|
|
25
|
+
type ModelFile = keyof typeof MODEL;
|
|
26
|
+
|
|
27
|
+
export const hash = (...xs: readonly Json[]): string =>
|
|
28
|
+
crypto.createHash("sha1").update(xs.map((x) => JSON.stringify(x)).join("|")).digest("hex").slice(0, 10);
|
|
29
|
+
|
|
30
|
+
const sha256 = (f: string): string => crypto.createHash("sha256").update(fs.readFileSync(f)).digest("hex");
|
|
31
|
+
|
|
32
|
+
/** HyperFrames' cache first (it ships the same files), then ours. */
|
|
33
|
+
const modelFiles = (): readonly [string, string] | undefined => {
|
|
34
|
+
const hf = path.join(os.homedir(), ".cache", "hyperframes", "tts");
|
|
35
|
+
const pairs: readonly (readonly [string, string])[] = [
|
|
36
|
+
[path.join(hf, "models", "kokoro-v1.0.onnx"), path.join(hf, "voices", "voices-v1.0.bin")],
|
|
37
|
+
[path.join(HOME, "kokoro", "kokoro-v1.0.onnx"), path.join(HOME, "kokoro", "voices-v1.0.bin")],
|
|
38
|
+
];
|
|
39
|
+
return pairs.find(([m, v]) => fs.existsSync(m) && fs.existsSync(v));
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
const pythonReady = (): boolean => fs.existsSync(PY) && spawnSync(PY, ["-I", "-c", "import kokoro_onnx, soundfile"]).status === 0;
|
|
43
|
+
export const narratorReady = (): boolean => pythonReady() && modelFiles() !== undefined;
|
|
44
|
+
|
|
45
|
+
const PYTHONS = ["python3.13", "python3.12", "python3.11", "python3.10", "python3", "python", "py"];
|
|
46
|
+
const modernPython = (): string | undefined =>
|
|
47
|
+
PYTHONS.find((b) => spawnSync(b, ["-c", "import sys; print(sys.version_info >= (3, 10) and sys.version_info < (3, 14))"], { encoding: "utf8" }).stdout?.trim() === "True");
|
|
48
|
+
|
|
49
|
+
function installPython(log: (m: string) => void): string | undefined {
|
|
50
|
+
mkdir(HOME);
|
|
51
|
+
if (spawnSync("uv", ["--version"]).status === 0) {
|
|
52
|
+
log("· setting up the narrator's Python (uv)...");
|
|
53
|
+
spawnSync("uv", ["venv", "-q", "--allow-existing", "-p", "3.12", VENV], { stdio: "inherit" });
|
|
54
|
+
spawnSync("uv", ["pip", "install", "-q", "-p", PY, ...PACKAGES], { stdio: "inherit" });
|
|
55
|
+
} else {
|
|
56
|
+
const py = modernPython();
|
|
57
|
+
if (!py) return 'the narrator needs Python 3.10 to 3.13, or uv (https://docs.astral.sh/uv/). Or set voice to "none", or use your own recording';
|
|
58
|
+
log(`· setting up the narrator's Python (${py})...`);
|
|
59
|
+
spawnSync(py, ["-m", "venv", VENV], { stdio: "inherit" });
|
|
60
|
+
spawnSync(PY, ["-m", "pip", "install", "-q", ...PACKAGES], { stdio: "inherit" });
|
|
61
|
+
}
|
|
62
|
+
return pythonReady() ? undefined : `couldn't install ${PACKAGES.join(" ")} into ${VENV}`;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
async function download(dir: string, f: ModelFile, log: (m: string) => void): Promise<string | undefined> {
|
|
66
|
+
log(`· downloading the voice model: ${f}...`);
|
|
67
|
+
const res = await fetch(`${RELEASE}/${f}`);
|
|
68
|
+
if (!res.ok) return `couldn't download ${f} (${res.status})`;
|
|
69
|
+
const part = path.join(dir, f + ".part");
|
|
70
|
+
fs.writeFileSync(part, Buffer.from(await res.arrayBuffer()));
|
|
71
|
+
if (sha256(part) !== MODEL[f]) {
|
|
72
|
+
fs.rmSync(part);
|
|
73
|
+
return `${f} failed its checksum; nothing was installed`;
|
|
74
|
+
}
|
|
75
|
+
fs.renameSync(part, path.join(dir, f));
|
|
76
|
+
return undefined;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/** Sets the narrator up. Returns undefined when ready, or what is missing. */
|
|
80
|
+
export async function setupNarrator(log: (m: string) => void = console.log): Promise<string | undefined> {
|
|
81
|
+
const py = pythonReady() ? undefined : installPython(log);
|
|
82
|
+
if (py || modelFiles()) return py;
|
|
83
|
+
const dir = mkdir(HOME, "kokoro");
|
|
84
|
+
const files: readonly ModelFile[] = ["voices-v1.0.bin", "kokoro-v1.0.onnx"];
|
|
85
|
+
return files.reduce<Promise<string | undefined>>(async (prev, f) => (await prev) ?? download(dir, f, log), Promise.resolve(undefined));
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/* ------------------------------------------------------------------ takes */
|
|
89
|
+
|
|
90
|
+
const silent = (s: Short): readonly Take[] => s.lines.map((l) => ({ file: null, dur: Math.max(1.3, words(l.say).length * 0.36) }));
|
|
91
|
+
|
|
92
|
+
type Job = { readonly text: string; readonly file: string };
|
|
93
|
+
|
|
94
|
+
const kokoroJobs = (s: Short, vdir: string, id: string, speed: number): readonly (Job & { readonly fresh: boolean })[] =>
|
|
95
|
+
s.lines.map((l, i) => {
|
|
96
|
+
const text = (l.speak ?? l.say).replace(/\*|\|/g, "");
|
|
97
|
+
const n = String(i + 1).padStart(2, "0");
|
|
98
|
+
const file = path.join(vdir, `${n}-${hash(text, id, speed)}.wav`);
|
|
99
|
+
return { text, file, fresh: fs.existsSync(file) };
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
/** A line's text changed: its old take goes, so the folder never holds two takes of one line. */
|
|
103
|
+
const dropStale = (vdir: string, file: string): void => {
|
|
104
|
+
const prefix = path.basename(file).slice(0, 3);
|
|
105
|
+
fs.readdirSync(vdir).filter((f) => f.startsWith(prefix) && f !== path.basename(file)).forEach((f) => fs.rmSync(path.join(vdir, f)));
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
async function kokoro(s: Short, vdir: string, id: string, speed: number, quiet: boolean): Promise<readonly Take[]> {
|
|
109
|
+
const jobs = kokoroJobs(s, vdir, id, speed);
|
|
110
|
+
const todo = jobs.filter((j) => !j.fresh);
|
|
111
|
+
if (todo.length) {
|
|
112
|
+
const why = narratorReady() ? undefined : await setupNarrator();
|
|
113
|
+
const files = modelFiles();
|
|
114
|
+
if (why || !files) throw new Error(why ?? "no voice model");
|
|
115
|
+
todo.forEach((j) => dropStale(vdir, j.file));
|
|
116
|
+
if (!quiet) console.log(`· narrating ${todo.length} line${todo.length > 1 ? "s" : ""} with ${id} (Kokoro, on this computer)...`);
|
|
117
|
+
run(PY, ["-I", TTS, ...files, id, String(speed)], { input: JSON.stringify(todo.map((j) => [j.text, j.file])) });
|
|
118
|
+
}
|
|
119
|
+
return jobs.map((j) => ({ file: j.file, dur: duration(j.file) }));
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
type Word = { readonly start: number; readonly end: number };
|
|
123
|
+
|
|
124
|
+
/** Every {word|text, start, end} in a transcript, wherever the tool nested it. */
|
|
125
|
+
const pickWords = (o: Json | undefined): readonly Word[] => {
|
|
126
|
+
if (Array.isArray(o)) return o.flatMap(pickWords);
|
|
127
|
+
if (!isMap(o)) return [];
|
|
128
|
+
const { start, end } = o;
|
|
129
|
+
if ((o.word || o.text) && typeof start === "number" && typeof end === "number" && !o.words) return [{ start, end }];
|
|
130
|
+
return Object.values(o).flatMap(pickWords);
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
const parseTranscript = (raw: string): Json | undefined => {
|
|
134
|
+
try {
|
|
135
|
+
const v: unknown = JSON.parse(raw.slice(raw.indexOf("{")));
|
|
136
|
+
return isMap(v) ? v : undefined;
|
|
137
|
+
} catch {
|
|
138
|
+
return undefined;
|
|
139
|
+
}
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
function transcriptWords(src: string, vdir: string, quiet: boolean): readonly Word[] {
|
|
143
|
+
const tj = path.join(vdir, `transcript-${hash(fs.statSync(src).mtimeMs, src)}.json`);
|
|
144
|
+
if (!fs.existsSync(tj)) {
|
|
145
|
+
if (!quiet) console.log("· transcribing your recording for the timing...");
|
|
146
|
+
fs.writeFileSync(tj, run(NPX, ["--yes", HYPERFRAMES, "transcribe", src, "--json", "-d", vdir], { cwd: vdir }));
|
|
147
|
+
}
|
|
148
|
+
const direct = pickWords(parseTranscript(fs.readFileSync(tj, "utf8")));
|
|
149
|
+
const side = fs.readdirSync(vdir).find((f) => f.endsWith("transcript.json") && !f.startsWith("transcript-"));
|
|
150
|
+
const ws = direct.length || !side ? direct : pickWords(parseTranscript(fs.readFileSync(path.join(vdir, side), "utf8")));
|
|
151
|
+
return ws.toSorted((a, b) => a.start - b.start);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/** Their own recording: each line takes the next run of words, in order. */
|
|
155
|
+
function ownRecording(s: Short, dir: string, vdir: string, file: string, quiet: boolean): readonly Take[] {
|
|
156
|
+
const src = path.resolve(dir, file);
|
|
157
|
+
if (!fs.existsSync(src)) throw new Error(`voice.file ${file} not found`);
|
|
158
|
+
const ws = transcriptWords(src, vdir, quiet);
|
|
159
|
+
if (!ws.length) throw new Error("couldn't read word timings from the transcript");
|
|
160
|
+
const word = (i: number): Word => ws[Math.min(i, ws.length - 1)] ?? { start: 0, end: 0 };
|
|
161
|
+
const firsts = s.lines.map((_, i) => s.lines.slice(0, i).reduce((n, l) => n + words(l.say).length, 0));
|
|
162
|
+
return s.lines.map((l, i) => {
|
|
163
|
+
const a = word(firsts[i] ?? 0);
|
|
164
|
+
const b = word((firsts[i] ?? 0) + words(l.say).length - 1);
|
|
165
|
+
return { file: src, from: a.start, dur: Math.max(0.6, b.end - a.start), slice: true };
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
export async function narrate(dir: string, s: Short, quiet = false): Promise<readonly Take[]> {
|
|
170
|
+
const vdir = mkdir(dir, "voice");
|
|
171
|
+
const v = s.voice;
|
|
172
|
+
if (v.kind === "none") return silent(s);
|
|
173
|
+
if (v.kind === "file") return ownRecording(s, dir, vdir, v.file, quiet);
|
|
174
|
+
return kokoro(s, vdir, v.id, v.speed, quiet);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/** Timing without narrating: cached Kokoro takes when every line has one, word-count estimates otherwise. */
|
|
178
|
+
export function cachedTakes(dir: string, s: Short): readonly Take[] {
|
|
179
|
+
const v = s.voice;
|
|
180
|
+
if (v.kind !== "kokoro") return silent(s);
|
|
181
|
+
const jobs = kokoroJobs(s, path.join(dir, "voice"), v.id, v.speed);
|
|
182
|
+
return jobs.every((j) => j.fresh) ? jobs.map((j) => ({ file: j.file, dur: duration(j.file) })) : silent(s);
|
|
183
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"about": "Gouache storybook: flat matte shapes, crisp edges, a little brush texture and paper. Calm and graphic.",
|
|
3
|
+
"mode": "strokes", "brush": 6, "length": 2.2, "layers": 2, "swirl": 0.2, "flow": 300, "curve": 0.3,
|
|
4
|
+
"bristle": 0.35, "impasto": 0.1, "hueJitter": 0.05, "valueJitter": 0.07, "satJitter": 0.1, "respect": 1,
|
|
5
|
+
"boil": 0.3, "paper": 0.8, "grain": 0.35, "saturation": 1.02, "contrast": 1.02, "warmth": 0.1, "vignette": 0.15, "bloom": 0.05,
|
|
6
|
+
"captions": { "style": "box", "size": 44 }
|
|
7
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
{
|
|
2
|
+
"about": "Ink and wash, like a graphic novel: wobbly hand-inked outlines that boil, flat washes of colour, paper.",
|
|
3
|
+
"mode": "wash", "wash": 0.5, "bleed": 0.3, "outline": 0.95, "ink": "#1b1712", "paper": 0.7, "paperColor": "#f4efe3",
|
|
4
|
+
"grain": 0.3, "boil": 1, "posterize": 0, "saturation": 0.85, "contrast": 1.05, "warmth": 0.1, "vignette": 0.1, "bloom": 0,
|
|
5
|
+
"captions": { "style": "box", "size": 44, "box": "rgba(27,23,18,0.8)" }
|
|
6
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"about": "Oil paint on canvas: swirling strokes, deep saturated colour, a slow shimmer. The painted-shorts look.",
|
|
3
|
+
"mode": "strokes", "brush": 11, "length": 2.8, "layers": 3, "swirl": 0.7, "flow": 160, "curve": 0.9,
|
|
4
|
+
"bristle": 0.75, "impasto": 0.6, "hueJitter": 0.2, "valueJitter": 0.22, "satJitter": 0.35, "respect": 0.85,
|
|
5
|
+
"boil": 0.5, "paper": 0.6, "grain": 0.5, "saturation": 1.18, "contrast": 1.06, "warmth": 0.2, "vignette": 0.35, "bloom": 0.3,
|
|
6
|
+
"captions": { "style": "box", "size": 44, "y": 0.775 }
|
|
7
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"about": "Soft pastel / crayon on toned paper: straight chalky hatching, paper tooth everywhere, pale gentle colour.",
|
|
3
|
+
"mode": "strokes", "brush": 6, "length": 4.6, "layers": 3, "swirl": 0.05, "flow": 600, "curve": 0,
|
|
4
|
+
"bristle": 1, "impasto": 0, "hueJitter": 0.1, "valueJitter": 0.3, "satJitter": 0.2, "respect": 0.5,
|
|
5
|
+
"boil": 0.8, "paper": 1.6, "grain": 0.8, "saturation": 0.82, "contrast": 0.9, "warmth": 0.35, "vignette": 0.2, "bloom": 0.1,
|
|
6
|
+
"captions": { "style": "box", "size": 44 }
|
|
7
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
{
|
|
2
|
+
"about": "Watercolour on cold-press paper: soft bleeding washes, pigment pooled at the edges, granulation, light and airy.",
|
|
3
|
+
"mode": "wash", "wash": 0.9, "bleed": 0.8, "paper": 0.9, "paperColor": "#f6f1e6", "grain": 0.25, "boil": 0.4,
|
|
4
|
+
"outline": 0, "saturation": 0.92, "contrast": 0.96, "warmth": 0.1, "vignette": 0.12, "bloom": 0.1,
|
|
5
|
+
"captions": { "style": "box", "size": 44, "box": "rgba(34,38,58,0.58)" }
|
|
6
|
+
}
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Ambient types shared by kit.ts, paint.ts and stage.ts. This file is a script
|
|
3
|
+
* (no import/export), so everything here is global. It is never shipped: the
|
|
4
|
+
* three runtime scripts are type-stripped and loaded as classic <script> tags.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
type Pt = [number, number];
|
|
8
|
+
type Seed = number | string;
|
|
9
|
+
type Palette = Record<string, string>;
|
|
10
|
+
type Rng = () => number;
|
|
11
|
+
|
|
12
|
+
/* ---------- the timeline the CLI builds (window.SHORT) ---------- */
|
|
13
|
+
|
|
14
|
+
interface PaintLook {
|
|
15
|
+
mode?: string;
|
|
16
|
+
brush?: number; length?: number; layers?: number; swirl?: number; flow?: number; curve?: number;
|
|
17
|
+
bristle?: number; impasto?: number; hueJitter?: number; valueJitter?: number; satJitter?: number;
|
|
18
|
+
respect?: number; boil?: number; paper?: number; grain?: number; wash?: number; bleed?: number;
|
|
19
|
+
outline?: number; ink?: string; paperColor?: string; posterize?: number; saturation?: number;
|
|
20
|
+
contrast?: number; warmth?: number; vignette?: number; bloom?: number;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
type PaintSettings = Required<PaintLook>;
|
|
24
|
+
|
|
25
|
+
type PaintNumberKey =
|
|
26
|
+
| "brush" | "length" | "layers" | "swirl" | "flow" | "curve" | "bristle" | "impasto"
|
|
27
|
+
| "hueJitter" | "valueJitter" | "satJitter" | "respect" | "boil" | "paper" | "grain"
|
|
28
|
+
| "wash" | "bleed" | "outline" | "posterize" | "saturation" | "contrast" | "warmth"
|
|
29
|
+
| "vignette" | "bloom";
|
|
30
|
+
|
|
31
|
+
interface Captions {
|
|
32
|
+
style?: string; size?: number; weight?: number | string; font?: string; case?: string;
|
|
33
|
+
width?: number; y?: number; box?: string; color?: string;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
interface Look extends PaintLook {
|
|
37
|
+
palette?: Palette;
|
|
38
|
+
captions?: Captions;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
interface Beat {
|
|
42
|
+
line: number; scene: string; start: number; dur: number;
|
|
43
|
+
move?: string; moveAmount?: number; seed?: Seed; transition?: string; fadeDur?: number;
|
|
44
|
+
say?: string; caption?: string;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
interface CaptionChunk { at: number; end: number; text: string }
|
|
48
|
+
|
|
49
|
+
interface Short {
|
|
50
|
+
W: number; H: number; fps: number; twos: number; duration: number;
|
|
51
|
+
beats: Beat[]; track?: CaptionChunk[]; voiceAt?: number[];
|
|
52
|
+
look: Look; captions?: Captions;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/* ---------- options each kit helper takes (all optional unless marked) ---------- */
|
|
56
|
+
|
|
57
|
+
type SkyStop = string | [number, string];
|
|
58
|
+
|
|
59
|
+
interface SkyOpts { stops?: SkyStop[]; y0?: number; y1?: number }
|
|
60
|
+
interface CloudsOpts {
|
|
61
|
+
y?: number; band?: number; n?: number; color?: string; shade?: string | null; size?: number;
|
|
62
|
+
drift?: number; seed?: Seed; opacity?: number; perspective?: boolean;
|
|
63
|
+
}
|
|
64
|
+
interface SunOpts { x?: number; y?: number; r?: number; color?: string; glow?: string; glowSize?: number; glowAlpha?: number }
|
|
65
|
+
interface GlowOpts { x: number; y: number; r?: number; color?: string; a?: number }
|
|
66
|
+
interface StarsOpts { n?: number; y1?: number; color?: string; size?: number; seed?: Seed }
|
|
67
|
+
interface RaysOpts {
|
|
68
|
+
x?: number; y?: number; angle?: number; spread?: number; n?: number; length?: number;
|
|
69
|
+
color?: string; a?: number; width?: number;
|
|
70
|
+
}
|
|
71
|
+
interface RainOpts { n?: number; color?: string; a?: number; slant?: number; length?: number; speed?: number; seed?: Seed }
|
|
72
|
+
interface MotesOpts { n?: number; y0?: number; y1?: number; color?: string; r?: number; seed?: Seed }
|
|
73
|
+
interface HazeOpts { y?: number; h?: number; color?: string; a?: number }
|
|
74
|
+
interface RidgeOpts {
|
|
75
|
+
y?: number; amp?: number; freq?: number; color?: string; shape?: string; seed?: Seed;
|
|
76
|
+
to?: number; x0?: number; x1?: number; top?: string | null;
|
|
77
|
+
}
|
|
78
|
+
interface GroundOpts {
|
|
79
|
+
y?: number; color?: string; far?: string | null; stripes?: number; stripe?: string | null;
|
|
80
|
+
flowers?: number; flower?: string; seed?: Seed;
|
|
81
|
+
}
|
|
82
|
+
interface PathOpts { x?: number; y?: number; near?: number; far?: number; color?: string; to?: number; bend?: number }
|
|
83
|
+
interface WaterOpts {
|
|
84
|
+
y0?: number; y1?: number; color?: string; deep?: string; glint?: string; glints?: number;
|
|
85
|
+
seed?: Seed; speed?: number;
|
|
86
|
+
}
|
|
87
|
+
interface WaterfallOpts { x?: number; w?: number; y0?: number; y1?: number; color?: string; shade?: string; speed?: number }
|
|
88
|
+
interface TreeOpts {
|
|
89
|
+
x?: number; y?: number; h?: number; kind?: string; crown?: string; trunk?: string;
|
|
90
|
+
fruit?: string | null; seed?: Seed;
|
|
91
|
+
}
|
|
92
|
+
interface TreesOpts {
|
|
93
|
+
n?: number; y?: number; h?: number; x0?: number; x1?: number; kind?: string; crown?: string;
|
|
94
|
+
trunk?: string; jitter?: number; seed?: Seed;
|
|
95
|
+
}
|
|
96
|
+
interface FoliageOpts {
|
|
97
|
+
side?: string; x?: number | null; y0?: number; y1?: number; depth?: number;
|
|
98
|
+
color?: string; light?: string; seed?: Seed;
|
|
99
|
+
}
|
|
100
|
+
interface TrunksOpts {
|
|
101
|
+
n?: number; color?: string; light?: string; y1?: number; seed?: Seed; x0?: number; x1?: number; width?: number;
|
|
102
|
+
}
|
|
103
|
+
interface HouseOpts {
|
|
104
|
+
x?: number; y?: number; w?: number; h?: number; wall?: string; roof?: string; window?: string;
|
|
105
|
+
lit?: boolean; litColor?: string; floors?: number; aFrame?: boolean;
|
|
106
|
+
}
|
|
107
|
+
interface CityOpts {
|
|
108
|
+
y?: number; h?: number; color?: string; lit?: boolean; litColor?: string; density?: number;
|
|
109
|
+
n?: number; seed?: Seed; x0?: number; x1?: number;
|
|
110
|
+
}
|
|
111
|
+
interface PagodaOpts { x?: number; y?: number; h?: number; color?: string; tiers?: number }
|
|
112
|
+
interface PoleOpts { x?: number; y?: number; h?: number; color?: string; wires?: number }
|
|
113
|
+
interface BlockOpts { x0: number; y0: number; x1: number; y1: number; color?: string; edge?: string | null; edgeWidth?: number }
|
|
114
|
+
interface LampOpts { x?: number; y?: number; h?: number; color?: string; light?: string; pool?: boolean }
|
|
115
|
+
interface ShapeOpts { pts: Pt[]; color?: string; smooth?: boolean }
|
|
116
|
+
interface HeartOpts { x?: number; y?: number; s?: number; color?: string }
|
|
117
|
+
interface PersonOpts {
|
|
118
|
+
x?: number; y?: number; h?: number; pose?: string; facing?: string; hair?: string;
|
|
119
|
+
skin?: string; hairColor?: string; top?: string; bottom?: string; shoes?: string;
|
|
120
|
+
robe?: boolean; rim?: string | null; silhouette?: string | null; walk?: number; dx?: number;
|
|
121
|
+
scale?: number; flip?: boolean; build?: number;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/* ---------- the kit a scene receives: (k) => { ... } ---------- */
|
|
125
|
+
|
|
126
|
+
interface Kit {
|
|
127
|
+
g: CanvasRenderingContext2D;
|
|
128
|
+
W: number; H: number; u: number; t: number; p: number;
|
|
129
|
+
r: Rng;
|
|
130
|
+
pal: Palette;
|
|
131
|
+
beat: Beat;
|
|
132
|
+
mix: (a: string, b: string, t: number) => string;
|
|
133
|
+
alpha: (c: string, a: number) => string;
|
|
134
|
+
rng: (seed: number) => Rng;
|
|
135
|
+
noise1: (seed: number) => (x: number) => number;
|
|
136
|
+
X: (x: number) => number;
|
|
137
|
+
Y: (y: number) => number;
|
|
138
|
+
sky: (o?: SkyOpts) => void;
|
|
139
|
+
clouds: (o?: CloudsOpts) => void;
|
|
140
|
+
sun: (o?: SunOpts) => void;
|
|
141
|
+
glow: (o: GlowOpts) => void;
|
|
142
|
+
stars: (o?: StarsOpts) => void;
|
|
143
|
+
rays: (o?: RaysOpts) => void;
|
|
144
|
+
rain: (o?: RainOpts) => void;
|
|
145
|
+
motes: (o?: MotesOpts) => void;
|
|
146
|
+
haze: (o?: HazeOpts) => void;
|
|
147
|
+
ridge: (o?: RidgeOpts) => void;
|
|
148
|
+
ground: (o?: GroundOpts) => void;
|
|
149
|
+
path: (o?: PathOpts) => void;
|
|
150
|
+
water: (o?: WaterOpts) => void;
|
|
151
|
+
waterfall: (o?: WaterfallOpts) => void;
|
|
152
|
+
tree: (o?: TreeOpts) => void;
|
|
153
|
+
trees: (o?: TreesOpts) => void;
|
|
154
|
+
foliage: (o?: FoliageOpts) => void;
|
|
155
|
+
trunks: (o?: TrunksOpts) => void;
|
|
156
|
+
house: (o?: HouseOpts) => void;
|
|
157
|
+
city: (o?: CityOpts) => void;
|
|
158
|
+
pagoda: (o?: PagodaOpts) => void;
|
|
159
|
+
pole: (o?: PoleOpts) => void;
|
|
160
|
+
block: (o: BlockOpts) => void;
|
|
161
|
+
lamp: (o?: LampOpts) => void;
|
|
162
|
+
shape: (o: ShapeOpts) => void;
|
|
163
|
+
heart: (o?: HeartOpts) => void;
|
|
164
|
+
person: (o?: PersonOpts) => void;
|
|
165
|
+
people: (list: PersonOpts[]) => void;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
type SceneFn = (k: Kit) => void;
|
|
169
|
+
|
|
170
|
+
interface ShortsKitApi {
|
|
171
|
+
make: (
|
|
172
|
+
g: CanvasRenderingContext2D, W: number, H: number, t: number, p: number,
|
|
173
|
+
seed: number, pal: Palette | undefined, beat: Beat,
|
|
174
|
+
) => Kit;
|
|
175
|
+
rng: (seed: number) => Rng;
|
|
176
|
+
hashStr: (s: Seed) => number;
|
|
177
|
+
mix: (a: string, b: string, t: number) => string;
|
|
178
|
+
alpha: (c: string, a: number) => string;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
interface Painter {
|
|
182
|
+
paint: (flat: HTMLCanvasElement, look: PaintLook, tick: number) => void;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
interface ShortsPaintApi {
|
|
186
|
+
Painter: (canvas: HTMLCanvasElement) => Painter;
|
|
187
|
+
DEFAULTS: PaintSettings;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
interface Window {
|
|
191
|
+
SHORT: Short;
|
|
192
|
+
IMAGES: Record<string, HTMLImageElement>;
|
|
193
|
+
SCENES: Record<string, SceneFn | undefined>;
|
|
194
|
+
ShortsKit: ShortsKitApi;
|
|
195
|
+
ShortsPaint: ShortsPaintApi;
|
|
196
|
+
drawing: (d: number) => string;
|
|
197
|
+
still: (i: number, at: number) => string;
|
|
198
|
+
ready: boolean;
|
|
199
|
+
}
|