egaki 0.5.0 → 0.7.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/CHANGELOG.md +188 -0
- package/dist/cli/cache-utils.d.ts +33 -0
- package/dist/cli/cache-utils.d.ts.map +1 -0
- package/dist/cli/cache-utils.js +157 -0
- package/dist/cli/cached-generate.d.ts +105 -0
- package/dist/cli/cached-generate.d.ts.map +1 -0
- package/dist/cli/cached-generate.js +208 -0
- package/dist/cli/cached-generate.test.d.ts +11 -0
- package/dist/cli/cached-generate.test.d.ts.map +1 -0
- package/dist/cli/cached-generate.test.js +431 -0
- package/dist/cli/cartesia-provider.d.ts +16 -0
- package/dist/cli/cartesia-provider.d.ts.map +1 -0
- package/dist/cli/cartesia-provider.js +184 -0
- package/dist/cli/chatgpt-auth.d.ts.map +1 -0
- package/dist/cli/cli.d.ts +4 -0
- package/dist/cli/cli.d.ts.map +1 -0
- package/dist/cli/cli.js +1329 -0
- package/dist/cli/clone-generate.d.ts +26 -0
- package/dist/cli/clone-generate.d.ts.map +1 -0
- package/dist/cli/clone-generate.js +148 -0
- package/dist/{credentials.d.ts → cli/credentials.d.ts} +11 -2
- package/dist/cli/credentials.d.ts.map +1 -0
- package/dist/{credentials.js → cli/credentials.js} +88 -5
- package/dist/cli/elevenlabs-speech-provider.d.ts +26 -0
- package/dist/cli/elevenlabs-speech-provider.d.ts.map +1 -0
- package/dist/cli/elevenlabs-speech-provider.js +199 -0
- package/dist/cli/generate.d.ts +140 -0
- package/dist/cli/generate.d.ts.map +1 -0
- package/dist/cli/generate.js +604 -0
- package/dist/cli/generate.test.d.ts +2 -0
- package/dist/cli/generate.test.d.ts.map +1 -0
- package/dist/cli/generate.test.js +131 -0
- package/dist/cli/login.d.ts.map +1 -0
- package/dist/{login.js → cli/login.js} +45 -4
- package/dist/cli/main.d.ts +3 -0
- package/dist/cli/main.d.ts.map +1 -0
- package/dist/cli/main.js +7 -0
- package/dist/cli/model-catalog.d.ts +141 -0
- package/dist/cli/model-catalog.d.ts.map +1 -0
- package/dist/cli/model-catalog.js +2135 -0
- package/dist/{models.d.ts → cli/models.d.ts} +16 -9
- package/dist/cli/models.d.ts.map +1 -0
- package/dist/cli/models.js +313 -0
- package/dist/cli/open-browser.d.ts.map +1 -0
- package/dist/cli/openai-speech-provider.d.ts +3 -0
- package/dist/cli/openai-speech-provider.d.ts.map +1 -0
- package/dist/cli/openai-speech-provider.js +28 -0
- package/dist/cli/separation-generate.d.ts +34 -0
- package/dist/cli/separation-generate.d.ts.map +1 -0
- package/dist/cli/separation-generate.js +117 -0
- package/dist/cli/speech-catalog.d.ts +35 -0
- package/dist/cli/speech-catalog.d.ts.map +1 -0
- package/dist/cli/speech-catalog.js +195 -0
- package/dist/cli/speech-generate.d.ts +86 -0
- package/dist/cli/speech-generate.d.ts.map +1 -0
- package/dist/cli/speech-generate.js +156 -0
- package/dist/cli/speech-models.d.ts +8 -0
- package/dist/cli/speech-models.d.ts.map +1 -0
- package/dist/cli/speech-models.js +109 -0
- package/dist/cli/speech-provider.test.d.ts +2 -0
- package/dist/cli/speech-provider.test.d.ts.map +1 -0
- package/dist/cli/speech-provider.test.js +197 -0
- package/dist/cli/subscription.d.ts.map +1 -0
- package/dist/cli/transcription-catalog.d.ts +35 -0
- package/dist/cli/transcription-catalog.d.ts.map +1 -0
- package/dist/cli/transcription-catalog.js +192 -0
- package/dist/cli/transcription-generate.d.ts +62 -0
- package/dist/cli/transcription-generate.d.ts.map +1 -0
- package/dist/cli/transcription-generate.js +144 -0
- package/dist/cli/transcription-models.d.ts +9 -0
- package/dist/cli/transcription-models.d.ts.map +1 -0
- package/dist/cli/transcription-models.js +100 -0
- package/dist/cli/transcription-providers.d.ts +22 -0
- package/dist/cli/transcription-providers.d.ts.map +1 -0
- package/dist/cli/transcription-providers.js +290 -0
- package/dist/cli/video-model-catalog.d.ts +2 -0
- package/dist/cli/video-model-catalog.d.ts.map +1 -0
- package/dist/cli/video-model-catalog.js +5 -0
- package/dist/cli/xai-auth.d.ts +29 -0
- package/dist/cli/xai-auth.d.ts.map +1 -0
- package/dist/cli/xai-auth.js +325 -0
- package/dist/vite/angled-screen.d.ts +57 -0
- package/dist/vite/angled-screen.d.ts.map +1 -0
- package/dist/vite/angled-screen.js +115 -0
- package/dist/vite/app.d.ts +51 -0
- package/dist/vite/app.d.ts.map +1 -0
- package/dist/vite/app.js +150 -0
- package/dist/vite/bands-shader.d.ts +47 -0
- package/dist/vite/bands-shader.d.ts.map +1 -0
- package/dist/vite/bands-shader.js +267 -0
- package/dist/vite/code-block.d.ts +107 -0
- package/dist/vite/code-block.d.ts.map +1 -0
- package/dist/vite/code-block.js +641 -0
- package/dist/vite/components.d.ts +36 -0
- package/dist/vite/components.d.ts.map +1 -0
- package/dist/vite/components.js +113 -0
- package/dist/vite/dispersion-rings-shader.d.ts +44 -0
- package/dist/vite/dispersion-rings-shader.d.ts.map +1 -0
- package/dist/vite/dispersion-rings-shader.js +282 -0
- package/dist/vite/easing-curves.d.ts +129 -0
- package/dist/vite/easing-curves.d.ts.map +1 -0
- package/dist/vite/easing-curves.js +584 -0
- package/dist/vite/easing-curves.test.d.ts +2 -0
- package/dist/vite/easing-curves.test.d.ts.map +1 -0
- package/dist/vite/easing-curves.test.js +200 -0
- package/dist/vite/generated-media-client.d.ts +35 -0
- package/dist/vite/generated-media-client.d.ts.map +1 -0
- package/dist/vite/generated-media-client.js +76 -0
- package/dist/vite/keyframes.d.ts +129 -0
- package/dist/vite/keyframes.d.ts.map +1 -0
- package/dist/vite/keyframes.js +194 -0
- package/dist/vite/layout-editor.d.ts +45 -0
- package/dist/vite/layout-editor.d.ts.map +1 -0
- package/dist/vite/layout-editor.js +587 -0
- package/dist/vite/layout-transition.d.ts +136 -0
- package/dist/vite/layout-transition.d.ts.map +1 -0
- package/dist/vite/layout-transition.js +581 -0
- package/dist/vite/liquid-gradient-shader.d.ts +62 -0
- package/dist/vite/liquid-gradient-shader.d.ts.map +1 -0
- package/dist/vite/liquid-gradient-shader.js +413 -0
- package/dist/vite/mdx-client.d.ts +15 -0
- package/dist/vite/mdx-client.d.ts.map +1 -0
- package/dist/vite/mdx-client.js +265 -0
- package/dist/vite/mdx-parse.d.ts +102 -0
- package/dist/vite/mdx-parse.d.ts.map +1 -0
- package/dist/vite/mdx-parse.js +251 -0
- package/dist/vite/mdx-provided-components.d.ts +18 -0
- package/dist/vite/mdx-provided-components.d.ts.map +1 -0
- package/dist/vite/mdx-provided-components.js +14 -0
- package/dist/vite/mdx-video.d.ts +327 -0
- package/dist/vite/mdx-video.d.ts.map +1 -0
- package/dist/vite/mdx-video.js +596 -0
- package/dist/vite/mdx-video.test.d.ts +9 -0
- package/dist/vite/mdx-video.test.d.ts.map +1 -0
- package/dist/vite/mdx-video.test.js +1945 -0
- package/dist/vite/media-components.d.ts +45 -0
- package/dist/vite/media-components.d.ts.map +1 -0
- package/dist/vite/media-components.js +355 -0
- package/dist/vite/media-duration-store.d.ts +66 -0
- package/dist/vite/media-duration-store.d.ts.map +1 -0
- package/dist/vite/media-duration-store.js +204 -0
- package/dist/vite/player-page.d.ts +35 -0
- package/dist/vite/player-page.d.ts.map +1 -0
- package/dist/vite/player-page.js +720 -0
- package/dist/vite/render-client.d.ts +18 -0
- package/dist/vite/render-client.d.ts.map +1 -0
- package/dist/vite/render-client.js +67 -0
- package/dist/vite/sdk.d.ts +221 -0
- package/dist/vite/sdk.d.ts.map +1 -0
- package/dist/vite/sdk.js +355 -0
- package/dist/vite/server-components.d.ts +55 -0
- package/dist/vite/server-components.d.ts.map +1 -0
- package/dist/vite/server-components.js +156 -0
- package/dist/vite/server-mdx.d.ts +74 -0
- package/dist/vite/server-mdx.d.ts.map +1 -0
- package/dist/vite/server-mdx.js +259 -0
- package/dist/vite/shader-renderer.d.ts +48 -0
- package/dist/vite/shader-renderer.d.ts.map +1 -0
- package/dist/vite/shader-renderer.js +411 -0
- package/dist/vite/store-hooks.d.ts +12 -0
- package/dist/vite/store-hooks.d.ts.map +1 -0
- package/dist/vite/store-hooks.js +93 -0
- package/dist/vite/store.d.ts +75 -0
- package/dist/vite/store.d.ts.map +1 -0
- package/dist/vite/store.js +52 -0
- package/dist/vite/store.test.d.ts +6 -0
- package/dist/vite/store.test.d.ts.map +1 -0
- package/dist/vite/store.test.js +172 -0
- package/dist/vite/tweakpane-hook.d.ts +94 -0
- package/dist/vite/tweakpane-hook.d.ts.map +1 -0
- package/dist/vite/tweakpane-hook.js +450 -0
- package/dist/vite/vite-plugin.d.ts +28 -0
- package/dist/vite/vite-plugin.d.ts.map +1 -0
- package/dist/vite/vite-plugin.js +538 -0
- package/dist/vite/wave-gradient-shader.d.ts +41 -0
- package/dist/vite/wave-gradient-shader.d.ts.map +1 -0
- package/dist/vite/wave-gradient-shader.js +207 -0
- package/package.json +103 -25
- package/skills/egaki/SKILL.md +162 -1
- package/src/cli/cache-utils.ts +169 -0
- package/src/cli/cached-generate.test.ts +531 -0
- package/src/cli/cached-generate.ts +348 -0
- package/src/cli/cartesia-provider.ts +249 -0
- package/src/cli/cli.ts +1818 -0
- package/src/cli/clone-generate.ts +202 -0
- package/src/{credentials.ts → cli/credentials.ts} +90 -7
- package/src/cli/elevenlabs-speech-provider.ts +243 -0
- package/src/cli/generate.test.ts +166 -0
- package/src/cli/generate.ts +912 -0
- package/src/{login.ts → cli/login.ts} +48 -3
- package/src/cli/main.ts +8 -0
- package/src/cli/model-catalog.ts +2336 -0
- package/src/cli/models.ts +408 -0
- package/src/cli/openai-speech-provider.ts +34 -0
- package/src/cli/separation-generate.ts +187 -0
- package/src/cli/speech-catalog.ts +253 -0
- package/src/cli/speech-generate.ts +275 -0
- package/src/cli/speech-models.ts +146 -0
- package/src/cli/speech-provider.test.ts +226 -0
- package/src/cli/transcription-catalog.ts +250 -0
- package/src/cli/transcription-generate.ts +214 -0
- package/src/cli/transcription-models.ts +136 -0
- package/src/cli/transcription-providers.ts +427 -0
- package/src/cli/video-model-catalog.ts +14 -0
- package/src/cli/xai-auth.ts +396 -0
- package/src/vite/angled-screen.tsx +214 -0
- package/src/vite/app.tsx +197 -0
- package/src/vite/bands-shader.tsx +319 -0
- package/src/vite/code-block.tsx +939 -0
- package/src/vite/components.tsx +278 -0
- package/src/vite/dispersion-rings-shader.tsx +331 -0
- package/src/vite/easing-curves.test.ts +232 -0
- package/src/vite/easing-curves.ts +743 -0
- package/src/vite/generated-media-client.tsx +121 -0
- package/src/vite/keyframes.tsx +347 -0
- package/src/vite/layout-editor.tsx +709 -0
- package/src/vite/layout-transition.tsx +706 -0
- package/src/vite/liquid-gradient-shader.tsx +474 -0
- package/src/vite/mdx-client.tsx +420 -0
- package/src/vite/mdx-parse.ts +343 -0
- package/src/vite/mdx-provided-components.ts +19 -0
- package/src/vite/mdx-video.test.tsx +2219 -0
- package/src/vite/mdx-video.tsx +976 -0
- package/src/vite/media-components.tsx +473 -0
- package/src/vite/media-duration-store.ts +225 -0
- package/src/vite/motion-timing.ts +194 -0
- package/src/vite/player-page.tsx +1107 -0
- package/src/vite/render-client.ts +84 -0
- package/src/vite/sdk.ts +598 -0
- package/src/vite/server-components.tsx +241 -0
- package/src/vite/server-mdx.ts +285 -0
- package/src/vite/shader-renderer.tsx +535 -0
- package/src/vite/store-hooks.ts +117 -0
- package/src/vite/store.test.ts +202 -0
- package/src/vite/store.ts +115 -0
- package/src/vite/styles.css +21 -0
- package/src/vite/tweakpane-hook.tsx +593 -0
- package/src/vite/virtual-modules.d.ts +30 -0
- package/src/vite/vite-plugin.ts +584 -0
- package/src/vite/wave-gradient-shader.tsx +253 -0
- package/README.md +0 -266
- package/dist/antigravity-auth.d.ts +0 -15
- package/dist/antigravity-auth.d.ts.map +0 -1
- package/dist/antigravity-auth.js +0 -544
- package/dist/antigravity-image.d.ts +0 -11
- package/dist/antigravity-image.d.ts.map +0 -1
- package/dist/antigravity-image.js +0 -285
- package/dist/chatgpt-auth.d.ts.map +0 -1
- package/dist/cli.d.ts +0 -3
- package/dist/cli.d.ts.map +0 -1
- package/dist/cli.js +0 -968
- package/dist/credentials.d.ts.map +0 -1
- package/dist/gateway-model-candidates.generated.d.ts +0 -11
- package/dist/gateway-model-candidates.generated.d.ts.map +0 -1
- package/dist/gateway-model-candidates.generated.js +0 -64
- package/dist/login.d.ts.map +0 -1
- package/dist/model-catalog.d.ts +0 -42
- package/dist/model-catalog.d.ts.map +0 -1
- package/dist/model-catalog.js +0 -1001
- package/dist/models.d.ts.map +0 -1
- package/dist/models.js +0 -246
- package/dist/open-browser.d.ts.map +0 -1
- package/dist/subscription.d.ts.map +0 -1
- package/dist/video-model-catalog.d.ts +0 -52
- package/dist/video-model-catalog.d.ts.map +0 -1
- package/dist/video-model-catalog.js +0 -306
- package/src/cli.ts +0 -1378
- package/src/model-catalog.ts +0 -1077
- package/src/models.ts +0 -309
- package/src/video-model-catalog.ts +0 -363
- /package/dist/{chatgpt-auth.d.ts → cli/chatgpt-auth.d.ts} +0 -0
- /package/dist/{chatgpt-auth.js → cli/chatgpt-auth.js} +0 -0
- /package/dist/{login.d.ts → cli/login.d.ts} +0 -0
- /package/dist/{open-browser.d.ts → cli/open-browser.d.ts} +0 -0
- /package/dist/{open-browser.js → cli/open-browser.js} +0 -0
- /package/dist/{subscription.d.ts → cli/subscription.d.ts} +0 -0
- /package/dist/{subscription.js → cli/subscription.js} +0 -0
- /package/src/{chatgpt-auth.ts → cli/chatgpt-auth.ts} +0 -0
- /package/src/{open-browser.ts → cli/open-browser.ts} +0 -0
- /package/src/{subscription.ts → cli/subscription.ts} +0 -0
|
@@ -0,0 +1,343 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Server-safe MDX parsing utilities for the video framework.
|
|
3
|
+
*
|
|
4
|
+
* This module contains ONLY parsing logic (frontmatter, section splitting,
|
|
5
|
+
* duration calculation). It does NOT import remotion or any client-only
|
|
6
|
+
* modules, so it can safely run in the RSC server environment.
|
|
7
|
+
*
|
|
8
|
+
* The rendering counterpart is mdx-video.tsx which re-exports these
|
|
9
|
+
* functions alongside the Remotion components.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import YAML from 'yaml'
|
|
13
|
+
|
|
14
|
+
// Inline mdast types to avoid requiring @types/mdast as a dependency
|
|
15
|
+
type RootContent = any
|
|
16
|
+
type Root = { type: 'root'; children: RootContent[] }
|
|
17
|
+
|
|
18
|
+
// ---------------------------------------------------------------------------
|
|
19
|
+
// Constants
|
|
20
|
+
// ---------------------------------------------------------------------------
|
|
21
|
+
|
|
22
|
+
const DEFAULT_FPS = 30
|
|
23
|
+
const DEFAULT_BPM = 120
|
|
24
|
+
const DEFAULT_SECTION_BEATS = 10
|
|
25
|
+
|
|
26
|
+
// ---------------------------------------------------------------------------
|
|
27
|
+
// Frontmatter parsing
|
|
28
|
+
// ---------------------------------------------------------------------------
|
|
29
|
+
|
|
30
|
+
const DEFAULT_WIDTH = 1920
|
|
31
|
+
const DEFAULT_HEIGHT = 1080
|
|
32
|
+
|
|
33
|
+
export interface VideoFrontmatter {
|
|
34
|
+
fps: number
|
|
35
|
+
bpm: number
|
|
36
|
+
width: number
|
|
37
|
+
height: number
|
|
38
|
+
/** Pixel density / scale multiplier for rendering. Default 1.
|
|
39
|
+
* Setting scale=2 renders at 2x resolution (e.g. 3840×2160 for a 1920×1080 comp). */
|
|
40
|
+
scale: number
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/** Scope variables injected into MDX expressions via safe-mdx's `scope` prop.
|
|
44
|
+
* Both server (app.tsx) and client (mdx-client.tsx) must pass the same shape. */
|
|
45
|
+
export interface MdxScope {
|
|
46
|
+
FPS: number
|
|
47
|
+
BEAT: number
|
|
48
|
+
// JS globals available in MDX expressions (safe, side-effect-free)
|
|
49
|
+
Math: typeof Math
|
|
50
|
+
Number: typeof Number
|
|
51
|
+
parseInt: typeof parseInt
|
|
52
|
+
parseFloat: typeof parseFloat
|
|
53
|
+
Infinity: typeof Infinity
|
|
54
|
+
NaN: typeof NaN
|
|
55
|
+
Boolean: typeof Boolean
|
|
56
|
+
String: typeof String
|
|
57
|
+
Array: typeof Array
|
|
58
|
+
Object: typeof Object
|
|
59
|
+
JSON: typeof JSON
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/** Build the MDX scope from frontmatter values.
|
|
63
|
+
* Includes JS globals so MDX expressions can use Math.round(), parseInt(), etc. */
|
|
64
|
+
export function buildMdxScope(fps: number, bpm: number): MdxScope {
|
|
65
|
+
return {
|
|
66
|
+
FPS: fps,
|
|
67
|
+
BEAT: fps / (bpm / 60),
|
|
68
|
+
Math,
|
|
69
|
+
Number,
|
|
70
|
+
parseInt,
|
|
71
|
+
parseFloat,
|
|
72
|
+
Infinity,
|
|
73
|
+
NaN,
|
|
74
|
+
Boolean,
|
|
75
|
+
String,
|
|
76
|
+
Array,
|
|
77
|
+
Object,
|
|
78
|
+
JSON,
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/** Parse YAML frontmatter from mdast. Extracts fps, bpm, width, height, scale. */
|
|
83
|
+
export function parseFrontmatter(mdast: Root): VideoFrontmatter {
|
|
84
|
+
const result: VideoFrontmatter = {
|
|
85
|
+
fps: DEFAULT_FPS,
|
|
86
|
+
bpm: DEFAULT_BPM,
|
|
87
|
+
width: DEFAULT_WIDTH,
|
|
88
|
+
height: DEFAULT_HEIGHT,
|
|
89
|
+
scale: 1,
|
|
90
|
+
}
|
|
91
|
+
for (const node of mdast.children) {
|
|
92
|
+
if (node.type !== 'yaml') continue
|
|
93
|
+
const text = (node as any).value as string
|
|
94
|
+
if (!text) continue
|
|
95
|
+
const parsed = YAML.parse(text)
|
|
96
|
+
if (parsed && typeof parsed === 'object') {
|
|
97
|
+
if (typeof parsed.fps === 'number' && parsed.fps > 0) result.fps = parsed.fps
|
|
98
|
+
if (typeof parsed.bpm === 'number' && parsed.bpm > 0) result.bpm = parsed.bpm
|
|
99
|
+
if (typeof parsed.width === 'number') {
|
|
100
|
+
const w = Math.round(parsed.width)
|
|
101
|
+
if (w > 0) result.width = w
|
|
102
|
+
}
|
|
103
|
+
if (typeof parsed.height === 'number') {
|
|
104
|
+
const h = Math.round(parsed.height)
|
|
105
|
+
if (h > 0) result.height = h
|
|
106
|
+
}
|
|
107
|
+
if (typeof parsed.scale === 'number' && parsed.scale > 0) result.scale = parsed.scale
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
return result
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// ---------------------------------------------------------------------------
|
|
114
|
+
// Aspect ratio from composition dimensions
|
|
115
|
+
// ---------------------------------------------------------------------------
|
|
116
|
+
|
|
117
|
+
const STANDARD_RATIOS: [number, number][] = [
|
|
118
|
+
[1, 1], [3, 4], [4, 3], [9, 16], [16, 9],
|
|
119
|
+
[2, 3], [3, 2], [4, 5], [5, 4], [9, 21], [21, 9],
|
|
120
|
+
]
|
|
121
|
+
|
|
122
|
+
function gcd(a: number, b: number): number {
|
|
123
|
+
while (b) { [a, b] = [b, a % b] }
|
|
124
|
+
return a
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
function parseRatio(s: string): [number, number] | undefined {
|
|
128
|
+
const [a, b] = s.split(':').map(Number)
|
|
129
|
+
if (a && b && a > 0 && b > 0) return [a, b]
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/** Compute the closest aspect ratio string from pixel dimensions.
|
|
133
|
+
* When `allowedRatios` is provided (e.g. from a model's supported list),
|
|
134
|
+
* picks the best match from that list. Otherwise uses STANDARD_RATIOS.
|
|
135
|
+
* Reduces to the exact ratio first (e.g. 1920×1080 → 16:9). If the exact
|
|
136
|
+
* ratio doesn't match, picks the closest by comparing decimal values.
|
|
137
|
+
* Returns '16:9' for invalid inputs. */
|
|
138
|
+
export function aspectRatioFromDimensions(width: number, height: number, allowedRatios?: string[]): string {
|
|
139
|
+
if (!Number.isFinite(width) || !Number.isFinite(height) || width <= 0 || height <= 0) {
|
|
140
|
+
return '16:9'
|
|
141
|
+
}
|
|
142
|
+
const candidates: [number, number][] = allowedRatios?.length
|
|
143
|
+
? allowedRatios.map(parseRatio).filter((r): r is [number, number] => r !== undefined)
|
|
144
|
+
: STANDARD_RATIOS
|
|
145
|
+
if (candidates.length === 0) return '16:9'
|
|
146
|
+
|
|
147
|
+
const d = gcd(width, height)
|
|
148
|
+
const rw = width / d
|
|
149
|
+
const rh = height / d
|
|
150
|
+
// Check exact match first
|
|
151
|
+
if (candidates.some(([w, h]) => w === rw && h === rh)) {
|
|
152
|
+
return `${rw}:${rh}`
|
|
153
|
+
}
|
|
154
|
+
// Find closest by decimal value
|
|
155
|
+
const target = width / height
|
|
156
|
+
let best = candidates[0]!
|
|
157
|
+
let bestDiff = Infinity
|
|
158
|
+
for (const ratio of candidates) {
|
|
159
|
+
const diff = Math.abs(ratio[0] / ratio[1] - target)
|
|
160
|
+
if (diff < bestDiff) {
|
|
161
|
+
bestDiff = diff
|
|
162
|
+
best = ratio
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
return `${best[0]}:${best[1]}`
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// ---------------------------------------------------------------------------
|
|
169
|
+
// Duration parsing from heading text
|
|
170
|
+
// ---------------------------------------------------------------------------
|
|
171
|
+
|
|
172
|
+
const HEADING_PROP_RE = /\s+(duration)=(\d+(?:\.\d+)?)(s|fps|frames?|f|beats?)?/gi
|
|
173
|
+
|
|
174
|
+
interface ParsedHeading {
|
|
175
|
+
label: string
|
|
176
|
+
durationInFrames: number | null
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
function parseHeadingProps(
|
|
180
|
+
rawText: string,
|
|
181
|
+
fps: number,
|
|
182
|
+
bpm: number,
|
|
183
|
+
): ParsedHeading {
|
|
184
|
+
let label = rawText
|
|
185
|
+
let durationInFrames: number | null = null
|
|
186
|
+
const framesPerBeat = fps / (bpm / 60)
|
|
187
|
+
|
|
188
|
+
// Strip all key=value props from the heading text
|
|
189
|
+
label = label.replace(HEADING_PROP_RE, (_match, key, value, unit) => {
|
|
190
|
+
const v = Number(value)
|
|
191
|
+
const u = (unit || '').toLowerCase()
|
|
192
|
+
let frames: number
|
|
193
|
+
if (u === 's') {
|
|
194
|
+
frames = Math.round(v * fps)
|
|
195
|
+
} else if (u === 'beat' || u === 'beats') {
|
|
196
|
+
frames = Math.round(v * framesPerBeat)
|
|
197
|
+
} else {
|
|
198
|
+
// bare number, fps, f, frame, frames — all mean raw frames
|
|
199
|
+
frames = Math.round(v)
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
if (key.toLowerCase() === 'duration') {
|
|
203
|
+
durationInFrames = frames
|
|
204
|
+
}
|
|
205
|
+
return ''
|
|
206
|
+
}).trim()
|
|
207
|
+
|
|
208
|
+
return { label: label || 'Untitled', durationInFrames }
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
// ---------------------------------------------------------------------------
|
|
212
|
+
// Section splitting
|
|
213
|
+
// ---------------------------------------------------------------------------
|
|
214
|
+
|
|
215
|
+
export interface MdxSection {
|
|
216
|
+
heading: string | null
|
|
217
|
+
nodes: RootContent[]
|
|
218
|
+
/** Duration in frames. `null` means "auto-infer from media content". */
|
|
219
|
+
durationInFrames: number | null
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
export interface SplitResult {
|
|
223
|
+
sections: MdxSection[]
|
|
224
|
+
frontmatter: VideoFrontmatter
|
|
225
|
+
/** ESM import nodes from the document, needed by SafeMdxRenderer to resolve modules */
|
|
226
|
+
imports: RootContent[]
|
|
227
|
+
/** Content nodes before the first heading. Rendered at composition level,
|
|
228
|
+
* outside the Series, spanning the full video duration. Use for soundtracks,
|
|
229
|
+
* ambient background videos, or any component that should persist across
|
|
230
|
+
* all sections. */
|
|
231
|
+
preamble: RootContent[]
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
function extractHeadingText(node: RootContent): string {
|
|
235
|
+
if (node.type !== 'heading') return ''
|
|
236
|
+
const parts: string[] = []
|
|
237
|
+
for (const child of (node as any).children || []) {
|
|
238
|
+
if (child.type === 'text') parts.push(child.value)
|
|
239
|
+
}
|
|
240
|
+
return parts.join('') || 'Untitled'
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
export function splitIntoSections(mdast: Root): SplitResult {
|
|
244
|
+
const frontmatter = parseFrontmatter(mdast)
|
|
245
|
+
const { fps, bpm } = frontmatter
|
|
246
|
+
const framesPerBeat = fps / (bpm / 60)
|
|
247
|
+
const defaultDuration = Math.round(DEFAULT_SECTION_BEATS * framesPerBeat)
|
|
248
|
+
|
|
249
|
+
const sections: MdxSection[] = []
|
|
250
|
+
const imports: RootContent[] = []
|
|
251
|
+
const preamble: RootContent[] = []
|
|
252
|
+
let current: MdxSection | null = null
|
|
253
|
+
let beforeFirstHeading = true
|
|
254
|
+
|
|
255
|
+
for (const node of mdast.children) {
|
|
256
|
+
if (node.type === 'yaml' || node.type === 'toml') {
|
|
257
|
+
continue
|
|
258
|
+
}
|
|
259
|
+
if (node.type === 'mdxjsEsm') {
|
|
260
|
+
imports.push(node)
|
|
261
|
+
continue
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
if (node.type === 'heading') {
|
|
265
|
+
beforeFirstHeading = false
|
|
266
|
+
const rawText = extractHeadingText(node)
|
|
267
|
+
const parsed = parseHeadingProps(rawText, fps, bpm)
|
|
268
|
+
current = {
|
|
269
|
+
heading: parsed.label,
|
|
270
|
+
nodes: [],
|
|
271
|
+
durationInFrames: parsed.durationInFrames ?? null,
|
|
272
|
+
}
|
|
273
|
+
sections.push(current)
|
|
274
|
+
continue
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
// Content before the first heading goes into the preamble, which is
|
|
278
|
+
// rendered at composition level (outside Series) so it spans the
|
|
279
|
+
// full video duration.
|
|
280
|
+
if (beforeFirstHeading) {
|
|
281
|
+
preamble.push(node)
|
|
282
|
+
} else if (current) {
|
|
283
|
+
current.nodes.push(node)
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
return { sections, frontmatter, imports, preamble }
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
/** Calculate total composition duration.
|
|
291
|
+
* All sections must have resolved (non-null) durations. */
|
|
292
|
+
export function calculateTotalDuration(sections: { durationInFrames: number }[]): number {
|
|
293
|
+
let total = 0
|
|
294
|
+
for (const s of sections) {
|
|
295
|
+
total += s.durationInFrames
|
|
296
|
+
}
|
|
297
|
+
return total
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
// ---------------------------------------------------------------------------
|
|
301
|
+
// Auto-duration resolution
|
|
302
|
+
// ---------------------------------------------------------------------------
|
|
303
|
+
|
|
304
|
+
/** MdxSection with a guaranteed non-null durationInFrames. */
|
|
305
|
+
export type ResolvedMdxSection = MdxSection & { durationInFrames: number }
|
|
306
|
+
|
|
307
|
+
/**
|
|
308
|
+
* Resolve null (auto) durations using per-section media durations.
|
|
309
|
+
*
|
|
310
|
+
* `sectionDurations` is keyed by section index (as string) → max media
|
|
311
|
+
* duration in seconds. Audio/Video components populate this at runtime
|
|
312
|
+
* via the media duration store after fetching metadata with mediabunny.
|
|
313
|
+
*
|
|
314
|
+
* For each section with `durationInFrames === null`:
|
|
315
|
+
* 1. Look up the section's index in `sectionDurations`.
|
|
316
|
+
* 2. If found, set duration = Math.round(seconds * fps).
|
|
317
|
+
* 3. Otherwise fall back to DEFAULT_SECTION_BEATS * framesPerBeat.
|
|
318
|
+
*
|
|
319
|
+
* Sections with explicit durations are returned unchanged.
|
|
320
|
+
* Generic so extra fields (like `jsx`) are preserved in the return type.
|
|
321
|
+
*/
|
|
322
|
+
export function resolveAutoDurations<T extends { durationInFrames: number | null }>(
|
|
323
|
+
sections: T[],
|
|
324
|
+
fps: number,
|
|
325
|
+
bpm: number,
|
|
326
|
+
sectionDurations: Record<string, number> = {},
|
|
327
|
+
): (T & { durationInFrames: number })[] {
|
|
328
|
+
const framesPerBeat = fps / (bpm / 60)
|
|
329
|
+
const defaultDuration = Math.round(DEFAULT_SECTION_BEATS * framesPerBeat)
|
|
330
|
+
|
|
331
|
+
return sections.map((section, i): T & { durationInFrames: number } => {
|
|
332
|
+
if (section.durationInFrames !== null) {
|
|
333
|
+
return section as T & { durationInFrames: number }
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
const seconds = sectionDurations[String(i)]
|
|
337
|
+
const durationInFrames = seconds !== undefined && seconds > 0
|
|
338
|
+
? Math.round(seconds * fps)
|
|
339
|
+
: defaultDuration
|
|
340
|
+
|
|
341
|
+
return { ...section, durationInFrames }
|
|
342
|
+
})
|
|
343
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Global MDXProvidedComponents type for MDX LSP support.
|
|
3
|
+
*
|
|
4
|
+
* The MDX analyzer (mdx-analyzer / vscode-mdx) uses the special global type
|
|
5
|
+
* `MDXProvidedComponents` to know which components are available in MDX files
|
|
6
|
+
* without explicit imports. This file declares that type from the same
|
|
7
|
+
* MDX_BUILTIN_COMPONENTS map that the runtime uses, keeping them in sync.
|
|
8
|
+
*
|
|
9
|
+
* Projects opt in by having an `egaki-env.d.ts` with:
|
|
10
|
+
* /// <reference types="egaki/mdx-components" />
|
|
11
|
+
*
|
|
12
|
+
* The egaki vite plugin auto-generates this file on first run.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import { MDX_BUILTIN_COMPONENTS } from './mdx-video.tsx'
|
|
16
|
+
|
|
17
|
+
declare global {
|
|
18
|
+
type MDXProvidedComponents = typeof MDX_BUILTIN_COMPONENTS
|
|
19
|
+
}
|