egaki 0.6.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 +140 -9
- 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.js → cli/cli.js} +626 -600
- 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/cli/credentials.d.ts.map +1 -0
- package/dist/{credentials.js → cli/credentials.js} +20 -0
- 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/cli/main.d.ts +3 -0
- package/dist/cli/main.d.ts.map +1 -0
- package/dist/cli/main.js +7 -0
- package/dist/{model-catalog.d.ts → cli/model-catalog.d.ts} +6 -0
- package/dist/cli/model-catalog.d.ts.map +1 -0
- package/dist/{model-catalog.js → cli/model-catalog.js} +714 -4
- package/dist/{models.d.ts → cli/models.d.ts} +12 -5
- package/dist/cli/models.d.ts.map +1 -0
- package/dist/{models.js → cli/models.js} +58 -57
- 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.map +1 -0
- package/dist/cli/xai-auth.d.ts.map +1 -0
- package/dist/{xai-auth.js → cli/xai-auth.js} +1 -1
- 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 +101 -24
- 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.ts → cli/cli.ts} +836 -884
- package/src/cli/clone-generate.ts +202 -0
- package/src/{credentials.ts → cli/credentials.ts} +20 -0
- 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/cli/main.ts +8 -0
- package/src/{model-catalog.ts → cli/model-catalog.ts} +787 -58
- package/src/{models.ts → cli/models.ts} +65 -86
- 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/{xai-auth.ts → cli/xai-auth.ts} +1 -1
- 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 -370
- 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/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.map +0 -1
- package/dist/models.d.ts.map +0 -1
- package/dist/open-browser.d.ts.map +0 -1
- package/dist/subscription.d.ts.map +0 -1
- package/dist/video-model-catalog.d.ts.map +0 -1
- package/dist/xai-auth.d.ts.map +0 -1
- /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/{credentials.d.ts → cli/credentials.d.ts} +0 -0
- /package/dist/{login.d.ts → cli/login.d.ts} +0 -0
- /package/dist/{login.js → cli/login.js} +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/dist/{video-model-catalog.d.ts → cli/video-model-catalog.d.ts} +0 -0
- /package/dist/{video-model-catalog.js → cli/video-model-catalog.js} +0 -0
- /package/dist/{xai-auth.d.ts → cli/xai-auth.d.ts} +0 -0
- /package/src/{chatgpt-auth.ts → cli/chatgpt-auth.ts} +0 -0
- /package/src/{login.ts → cli/login.ts} +0 -0
- /package/src/{open-browser.ts → cli/open-browser.ts} +0 -0
- /package/src/{subscription.ts → cli/subscription.ts} +0 -0
- /package/src/{video-model-catalog.ts → cli/video-model-catalog.ts} +0 -0
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* AngledScreen — CSS 3D perspective wrapper with fake bokeh depth-of-field.
|
|
5
|
+
*
|
|
6
|
+
* Wraps any children (images, videos, divs) in a 3D-transformed plane using
|
|
7
|
+
* CSS `perspective` + `transform: rotateX/Y/Z`. Simulates depth-of-field
|
|
8
|
+
* via `backdrop-filter: blur()` masked with `mask-image: linear-gradient()`
|
|
9
|
+
* so only the far edge of the content gets blurred.
|
|
10
|
+
*
|
|
11
|
+
* The blur direction is auto-detected from the rotation angles: whichever
|
|
12
|
+
* edge is furthest from the camera gets blurred. Override with bokehDirection.
|
|
13
|
+
*
|
|
14
|
+
* IMPORTANT: the blur overlay lives OUTSIDE the 3D transform as a sibling
|
|
15
|
+
* in the outer container. backdrop-filter does not work inside CSS 3D
|
|
16
|
+
* transformed elements (the transform compositing layer breaks backdrop
|
|
17
|
+
* sampling and blurs everything regardless of mask). Placing it outside
|
|
18
|
+
* means it blurs the already-composited 3D scene in normal 2D space.
|
|
19
|
+
*
|
|
20
|
+
* Set `debug` to visualize the mask gradient as green (sharp) / red (blur).
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
import { type CSSProperties, type ReactNode } from 'react'
|
|
24
|
+
import { useTweakpane } from './tweakpane-hook.tsx'
|
|
25
|
+
|
|
26
|
+
export interface AngledScreenProps {
|
|
27
|
+
children: ReactNode
|
|
28
|
+
|
|
29
|
+
// --- 3D transform ---
|
|
30
|
+
/** CSS perspective distance in px. Smaller = more dramatic. Default 1200. */
|
|
31
|
+
perspective?: number
|
|
32
|
+
/** X-axis rotation in degrees (tilts top/bottom). Default 8. */
|
|
33
|
+
rotateX?: number
|
|
34
|
+
/** Y-axis rotation in degrees (angles left/right). Default -12. */
|
|
35
|
+
rotateY?: number
|
|
36
|
+
/** Z-axis rotation in degrees. Default 0. */
|
|
37
|
+
rotateZ?: number
|
|
38
|
+
/** Push the plane forward/back in px. Positive = closer/larger,
|
|
39
|
+
* negative = further/smaller (same visual effect as scale). Default 0. */
|
|
40
|
+
translateZ?: number
|
|
41
|
+
/** Transform origin. Default '50% 50%'. */
|
|
42
|
+
transformOrigin?: string
|
|
43
|
+
|
|
44
|
+
// --- Bokeh (fake depth-of-field) ---
|
|
45
|
+
/** Enable bokeh blur overlay. Default true. */
|
|
46
|
+
bokeh?: boolean
|
|
47
|
+
/** Max blur radius in px at the far edge. Default 12. */
|
|
48
|
+
bokehBlur?: number
|
|
49
|
+
/** Override which edge gets blurred. Auto-detected from rotation if omitted. */
|
|
50
|
+
bokehDirection?: 'top' | 'bottom' | 'left' | 'right'
|
|
51
|
+
/** Where blur starts (0-1). 0 = blur from the very near edge, 1 = no blur.
|
|
52
|
+
* The transition ramp is a fixed 25% after this point. Default 0.3. */
|
|
53
|
+
bokehOffset?: number
|
|
54
|
+
|
|
55
|
+
// --- Background ---
|
|
56
|
+
/** Background color behind the tilted screen. Default '#000000'. */
|
|
57
|
+
backgroundColor?: string
|
|
58
|
+
|
|
59
|
+
// --- Sizing ---
|
|
60
|
+
/** Width of the screen content. Default '80%'. */
|
|
61
|
+
width?: string | number
|
|
62
|
+
/** Height of the screen content. Default 'auto'. */
|
|
63
|
+
height?: string | number
|
|
64
|
+
|
|
65
|
+
/** Show the blur mask as green (sharp) / red (blur) overlay for debugging. */
|
|
66
|
+
debug?: boolean
|
|
67
|
+
|
|
68
|
+
/** Optional inline style on the outer container. */
|
|
69
|
+
style?: CSSProperties
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const GRADIENT_DIRECTIONS: Record<string, string> = {
|
|
73
|
+
top: 'to top',
|
|
74
|
+
bottom: 'to bottom',
|
|
75
|
+
left: 'to left',
|
|
76
|
+
right: 'to right',
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Auto-detect which edge is furthest from camera based on rotation.
|
|
81
|
+
* CSS rotateY(-20deg): right edge comes toward viewer, LEFT is far → blur left.
|
|
82
|
+
* CSS rotateY(+20deg): left edge comes toward viewer, RIGHT is far → blur right.
|
|
83
|
+
* CSS rotateX(+10deg): top edge comes toward viewer, BOTTOM is far → blur bottom.
|
|
84
|
+
* CSS rotateX(-10deg): bottom edge comes toward viewer, TOP is far → blur top.
|
|
85
|
+
* Picks the axis with the larger rotation angle.
|
|
86
|
+
*/
|
|
87
|
+
function autoDirection(rotateX: number, rotateY: number): 'top' | 'bottom' | 'left' | 'right' {
|
|
88
|
+
if (Math.abs(rotateY) >= Math.abs(rotateX)) {
|
|
89
|
+
return rotateY < 0 ? 'left' : 'right'
|
|
90
|
+
}
|
|
91
|
+
return rotateX > 0 ? 'bottom' : 'top'
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export function AngledScreen(props: AngledScreenProps) {
|
|
95
|
+
const {
|
|
96
|
+
children,
|
|
97
|
+
transformOrigin = '50% 50%',
|
|
98
|
+
bokehDirection,
|
|
99
|
+
backgroundColor = '#000000',
|
|
100
|
+
width = '80%',
|
|
101
|
+
height = 'auto',
|
|
102
|
+
style,
|
|
103
|
+
} = props
|
|
104
|
+
|
|
105
|
+
const tp = useTweakpane('AngledScreen', {
|
|
106
|
+
perspective: { value: props.perspective ?? 1200, min: 100, max: 3000, step: 10 },
|
|
107
|
+
rotateX: { value: props.rotateX ?? 8, min: -90, max: 90, step: 0.5 },
|
|
108
|
+
rotateY: { value: props.rotateY ?? -12, min: -90, max: 90, step: 0.5 },
|
|
109
|
+
rotateZ: { value: props.rotateZ ?? 0, min: -180, max: 180, step: 0.5 },
|
|
110
|
+
translateZ: { value: props.translateZ ?? 0, min: -500, max: 500, step: 1 },
|
|
111
|
+
bokeh: props.bokeh ?? true,
|
|
112
|
+
bokehBlur: { value: props.bokehBlur ?? 12, min: 0, max: 50, step: 0.5 },
|
|
113
|
+
bokehOffset: { value: props.bokehOffset ?? 0.3, min: 0, max: 1, step: 0.01 },
|
|
114
|
+
debug: props.debug ?? false,
|
|
115
|
+
})
|
|
116
|
+
|
|
117
|
+
const { perspective, rotateX, rotateY, rotateZ, translateZ } = tp
|
|
118
|
+
const { bokeh, bokehBlur, bokehOffset } = tp
|
|
119
|
+
const showDebug = tp.debug as boolean
|
|
120
|
+
const direction = bokehDirection ?? autoDirection(rotateX, rotateY)
|
|
121
|
+
const gradientDir = GRADIENT_DIRECTIONS[direction] || 'to right'
|
|
122
|
+
const startPct = `${bokehOffset * 100}%`
|
|
123
|
+
const endPct = `${Math.min((bokehOffset + 0.25) * 100, 100)}%`
|
|
124
|
+
|
|
125
|
+
return (
|
|
126
|
+
<div
|
|
127
|
+
style={{
|
|
128
|
+
position: 'relative',
|
|
129
|
+
width: '100%',
|
|
130
|
+
height: '100%',
|
|
131
|
+
overflow: 'hidden',
|
|
132
|
+
}}
|
|
133
|
+
>
|
|
134
|
+
{/* Moving layer: receives user style (translateX, etc.) */}
|
|
135
|
+
<div
|
|
136
|
+
style={{
|
|
137
|
+
position: 'absolute',
|
|
138
|
+
inset: 0,
|
|
139
|
+
backgroundColor,
|
|
140
|
+
display: 'flex',
|
|
141
|
+
alignItems: 'center',
|
|
142
|
+
justifyContent: 'center',
|
|
143
|
+
...style,
|
|
144
|
+
}}
|
|
145
|
+
>
|
|
146
|
+
{/* Perspective container */}
|
|
147
|
+
<div
|
|
148
|
+
style={{
|
|
149
|
+
perspective: `${perspective}px`,
|
|
150
|
+
perspectiveOrigin: '50% 50%',
|
|
151
|
+
display: 'flex',
|
|
152
|
+
alignItems: 'center',
|
|
153
|
+
justifyContent: 'center',
|
|
154
|
+
width: '100%',
|
|
155
|
+
height: '100%',
|
|
156
|
+
}}
|
|
157
|
+
>
|
|
158
|
+
{/* Transformed plane */}
|
|
159
|
+
<div
|
|
160
|
+
style={{
|
|
161
|
+
transform: [
|
|
162
|
+
`rotateX(${rotateX}deg)`,
|
|
163
|
+
`rotateY(${rotateY}deg)`,
|
|
164
|
+
`rotateZ(${rotateZ}deg)`,
|
|
165
|
+
translateZ !== 0 ? `translateZ(${translateZ}px)` : '',
|
|
166
|
+
]
|
|
167
|
+
.filter(Boolean)
|
|
168
|
+
.join(' '),
|
|
169
|
+
transformOrigin,
|
|
170
|
+
width: typeof width === 'number' ? `${width}px` : width,
|
|
171
|
+
height: typeof height === 'number' ? `${height}px` : height,
|
|
172
|
+
position: 'relative',
|
|
173
|
+
borderRadius: '12px',
|
|
174
|
+
overflow: 'hidden',
|
|
175
|
+
}}
|
|
176
|
+
>
|
|
177
|
+
{children}
|
|
178
|
+
</div>
|
|
179
|
+
</div>
|
|
180
|
+
</div>
|
|
181
|
+
|
|
182
|
+
{/* Bokeh blur: fixed in screen space, does NOT move with the image.
|
|
183
|
+
Acts like a camera lens DOF effect. Lives outside the moving layer
|
|
184
|
+
and outside the 3D transform (backdrop-filter is broken inside
|
|
185
|
+
CSS 3D transformed elements). */}
|
|
186
|
+
{bokeh && bokehBlur > 0 && !showDebug && (
|
|
187
|
+
<div
|
|
188
|
+
style={{
|
|
189
|
+
position: 'absolute',
|
|
190
|
+
inset: 0,
|
|
191
|
+
backdropFilter: `blur(${bokehBlur}px)`,
|
|
192
|
+
WebkitBackdropFilter: `blur(${bokehBlur}px)`,
|
|
193
|
+
maskImage: `linear-gradient(${gradientDir}, transparent ${startPct}, black ${endPct})`,
|
|
194
|
+
WebkitMaskImage: `linear-gradient(${gradientDir}, transparent ${startPct}, black ${endPct})`,
|
|
195
|
+
pointerEvents: 'none',
|
|
196
|
+
}}
|
|
197
|
+
/>
|
|
198
|
+
)}
|
|
199
|
+
|
|
200
|
+
{/* Debug: show the mask shape as green (sharp) to red (blur) */}
|
|
201
|
+
{showDebug && (
|
|
202
|
+
<div
|
|
203
|
+
style={{
|
|
204
|
+
position: 'absolute',
|
|
205
|
+
inset: 0,
|
|
206
|
+
background: `linear-gradient(${gradientDir}, rgba(0,255,0,0.4) ${startPct}, rgba(255,0,0,0.4) ${endPct})`,
|
|
207
|
+
pointerEvents: 'none',
|
|
208
|
+
zIndex: 999,
|
|
209
|
+
}}
|
|
210
|
+
/>
|
|
211
|
+
)}
|
|
212
|
+
</div>
|
|
213
|
+
)
|
|
214
|
+
}
|
package/src/vite/app.tsx
ADDED
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Spiceflow entry for the video framework.
|
|
3
|
+
*
|
|
4
|
+
* The server has two jobs:
|
|
5
|
+
*
|
|
6
|
+
* 1. Deliver the MDX source string to the client through the RSC flight
|
|
7
|
+
* payload. All regular MDX processing (parsing, section splitting,
|
|
8
|
+
* expression evaluation, safe-mdx rendering) happens in the browser
|
|
9
|
+
* inside MdxClientApp (mdx-client.tsx, 'use client'), so MDX expression
|
|
10
|
+
* props can be functions and user components don't need 'use client'.
|
|
11
|
+
*
|
|
12
|
+
* 2. Render <Server> slots. <Server> is a reserved MDX element marking a
|
|
13
|
+
* subtree as server components: its children are rendered HERE in the
|
|
14
|
+
* RSC environment (async allowed, promises stream through flight) and
|
|
15
|
+
* passed to the client as serverSlots keyed by the node's start
|
|
16
|
+
* position. The MDX string sent to the client has each <Server> block
|
|
17
|
+
* blanked to a self-closing marker with newline padding, so every
|
|
18
|
+
* position (slot keys, data-markdown-line, sourcemaps) stays aligned
|
|
19
|
+
* with the original file and the client never parses server-only
|
|
20
|
+
* content.
|
|
21
|
+
*
|
|
22
|
+
* NOTE: Relative imports MUST include file extensions (.tsx, .ts) for the
|
|
23
|
+
* RSC module runner to resolve them correctly within noExternal packages.
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
import path from 'node:path'
|
|
27
|
+
import { createRequire } from 'node:module'
|
|
28
|
+
import type { ReactNode } from 'react'
|
|
29
|
+
import { Spiceflow } from 'spiceflow'
|
|
30
|
+
import { SafeMdxRenderer } from 'safe-mdx'
|
|
31
|
+
import { mdxParse } from 'safe-mdx/parse'
|
|
32
|
+
import type { EagerModules } from 'safe-mdx/parse'
|
|
33
|
+
import { entries, entryPaths, defaultRoute, projectRoot, folderName } from 'virtual:egaki-mdx'
|
|
34
|
+
import { Head } from 'spiceflow/react'
|
|
35
|
+
import {
|
|
36
|
+
findServerNodes,
|
|
37
|
+
blankServerContents,
|
|
38
|
+
collectServerImportSources,
|
|
39
|
+
filterImportNodesToModules,
|
|
40
|
+
wrapGenerateNodes,
|
|
41
|
+
} from './server-mdx.ts'
|
|
42
|
+
import { parseFrontmatter, buildMdxScope } from './mdx-parse.ts'
|
|
43
|
+
import { MdxClientApp } from './mdx-client.tsx'
|
|
44
|
+
import * as mdxVideoExports from './mdx-video.tsx'
|
|
45
|
+
import {
|
|
46
|
+
GeneratedImage,
|
|
47
|
+
GeneratedVideo,
|
|
48
|
+
GeneratedSpeech,
|
|
49
|
+
} from './server-components.tsx'
|
|
50
|
+
import {
|
|
51
|
+
getGenerationProgress,
|
|
52
|
+
onProgressChange,
|
|
53
|
+
type GenerationProgressEvent,
|
|
54
|
+
} from '../cli/cached-generate.js'
|
|
55
|
+
|
|
56
|
+
/** Dynamically import the modules referenced inside <Server> blocks. */
|
|
57
|
+
async function importServerModules(ast: any): Promise<EagerModules> {
|
|
58
|
+
const requireFromRoot = createRequire(path.join(projectRoot, 'package.json'))
|
|
59
|
+
|
|
60
|
+
const modules: EagerModules = {}
|
|
61
|
+
for (const source of collectServerImportSources(ast)) {
|
|
62
|
+
if (/\.mdx?$/.test(source)) {
|
|
63
|
+
console.warn(`[egaki] imported .mdx files inside <Server> are not supported yet: ${source}`)
|
|
64
|
+
continue
|
|
65
|
+
}
|
|
66
|
+
try {
|
|
67
|
+
const isPathLike = source.startsWith('.') || source.startsWith('/')
|
|
68
|
+
const id = isPathLike
|
|
69
|
+
? path.resolve(projectRoot, source)
|
|
70
|
+
: requireFromRoot.resolve(source)
|
|
71
|
+
modules[source] = await import(/* @vite-ignore */ id)
|
|
72
|
+
} catch (e) {
|
|
73
|
+
console.warn(`[egaki] failed to import <Server> module ${source}:`, (e as Error).message)
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
return modules
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/** Build the server components map from the mdx-video namespace import.
|
|
80
|
+
* mdx-video.tsx has 'use client', so each named export in the namespace
|
|
81
|
+
* is an individual client reference. */
|
|
82
|
+
const serverComponents: Record<string, any> = {
|
|
83
|
+
...(mdxVideoExports as any),
|
|
84
|
+
GeneratedImage,
|
|
85
|
+
GeneratedVideo,
|
|
86
|
+
GeneratedSpeech,
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/** All route paths available for navigation, sorted. */
|
|
90
|
+
const availableEntries = Object.keys(entries ||{}).sort()
|
|
91
|
+
|
|
92
|
+
/** Render a single MDX entry as a page. Shared by all route handlers. */
|
|
93
|
+
async function renderMdxPage(routePath: string) {
|
|
94
|
+
const mdxSource = (entries as Record<string, string>)[routePath]
|
|
95
|
+
const currentEntryPath = (entryPaths as Record<string, string>)[routePath]
|
|
96
|
+
if (mdxSource == null || currentEntryPath == null) return null
|
|
97
|
+
|
|
98
|
+
const title = routePath
|
|
99
|
+
? `${routePath} · ${folderName} · egaki`
|
|
100
|
+
: `${folderName} · egaki`
|
|
101
|
+
|
|
102
|
+
let ast: ReturnType<typeof mdxParse>
|
|
103
|
+
try {
|
|
104
|
+
ast = mdxParse(mdxSource)
|
|
105
|
+
} catch (e) {
|
|
106
|
+
console.error('[egaki] MDX parse error (server):', e)
|
|
107
|
+
return <>
|
|
108
|
+
<Head><Head.Title>{title}</Head.Title></Head>
|
|
109
|
+
<MdxClientApp mdx={mdxSource} serverSlots={{}} entryPath={currentEntryPath} availableEntries={availableEntries} currentRoute={routePath} />
|
|
110
|
+
</>
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
wrapGenerateNodes(ast)
|
|
114
|
+
const serverNodes = findServerNodes(ast)
|
|
115
|
+
|
|
116
|
+
if (serverNodes.length === 0) {
|
|
117
|
+
return <>
|
|
118
|
+
<Head><Head.Title>{title}</Head.Title></Head>
|
|
119
|
+
<MdxClientApp mdx={mdxSource} serverSlots={{}} entryPath={currentEntryPath} availableEntries={availableEntries} currentRoute={routePath} />
|
|
120
|
+
</>
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
const eagerModules = await importServerModules(ast)
|
|
124
|
+
const importNodes = filterImportNodesToModules(
|
|
125
|
+
ast.children.filter((node: any) => node.type === 'mdxjsEsm'),
|
|
126
|
+
Object.keys(eagerModules),
|
|
127
|
+
)
|
|
128
|
+
|
|
129
|
+
const { fps, bpm } = parseFrontmatter(ast)
|
|
130
|
+
const serverScope = buildMdxScope(fps, bpm)
|
|
131
|
+
const serverEvaluateOptions = { functions: true }
|
|
132
|
+
|
|
133
|
+
const serverSlots: Record<string, ReactNode> = {}
|
|
134
|
+
for (const { key, node } of serverNodes) {
|
|
135
|
+
if (key in serverSlots) {
|
|
136
|
+
console.warn(
|
|
137
|
+
`[egaki] multiple <Server> elements start on line ${key}; ` +
|
|
138
|
+
`only the first one renders. Put each <Server> on its own line.`,
|
|
139
|
+
)
|
|
140
|
+
continue
|
|
141
|
+
}
|
|
142
|
+
serverSlots[key] = (
|
|
143
|
+
<SafeMdxRenderer
|
|
144
|
+
markdown={mdxSource}
|
|
145
|
+
mdast={{ type: 'root', children: [...importNodes, ...node.children] } as any}
|
|
146
|
+
components={serverComponents}
|
|
147
|
+
modules={eagerModules}
|
|
148
|
+
baseUrl="./"
|
|
149
|
+
scope={serverScope}
|
|
150
|
+
evaluateOptions={serverEvaluateOptions}
|
|
151
|
+
onError={(e) => console.warn('[egaki] <Server> slot:', e.message)}
|
|
152
|
+
/>
|
|
153
|
+
)
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
const clientMdx = blankServerContents(mdxSource, serverNodes)
|
|
157
|
+
return <>
|
|
158
|
+
<Head><Head.Title>{title}</Head.Title></Head>
|
|
159
|
+
<MdxClientApp mdx={clientMdx} serverSlots={serverSlots} entryPath={currentEntryPath} availableEntries={availableEntries} currentRoute={routePath} />
|
|
160
|
+
</>
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// Build the Spiceflow app with explicit routes for each discovered entry.
|
|
164
|
+
// Using explicit routes instead of a wildcard (/*) avoids intercepting
|
|
165
|
+
// the /api/generation-progress GET endpoint. Spiceflow's .page() registers
|
|
166
|
+
// both GET and POST, so a wildcard would shadow the API route.
|
|
167
|
+
function buildApp() {
|
|
168
|
+
let app = new Spiceflow()
|
|
169
|
+
.get('/api/generation-progress', async function* (): AsyncGenerator<GenerationProgressEvent> {
|
|
170
|
+
let resolve: (() => void) | null = null
|
|
171
|
+
const unsubscribe = onProgressChange(() => resolve?.())
|
|
172
|
+
|
|
173
|
+
try {
|
|
174
|
+
while (true) {
|
|
175
|
+
const progress = getGenerationProgress()
|
|
176
|
+
yield progress
|
|
177
|
+
if (progress.summary.total === 0) return
|
|
178
|
+
|
|
179
|
+
await new Promise<void>((r) => { resolve = r })
|
|
180
|
+
resolve = null
|
|
181
|
+
}
|
|
182
|
+
} finally {
|
|
183
|
+
unsubscribe()
|
|
184
|
+
}
|
|
185
|
+
})
|
|
186
|
+
.page('/', () => renderMdxPage(defaultRoute))
|
|
187
|
+
|
|
188
|
+
// Register each entry at its route path. The default also gets its
|
|
189
|
+
// own /<name> route so the dropdown can navigate to it from other pages.
|
|
190
|
+
for (const route of availableEntries) {
|
|
191
|
+
app = app.page(`/${route}`, () => renderMdxPage(route)) as any
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
return app
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
export const app = buildApp()
|