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.
Files changed (271) hide show
  1. package/CHANGELOG.md +140 -9
  2. package/dist/cli/cache-utils.d.ts +33 -0
  3. package/dist/cli/cache-utils.d.ts.map +1 -0
  4. package/dist/cli/cache-utils.js +157 -0
  5. package/dist/cli/cached-generate.d.ts +105 -0
  6. package/dist/cli/cached-generate.d.ts.map +1 -0
  7. package/dist/cli/cached-generate.js +208 -0
  8. package/dist/cli/cached-generate.test.d.ts +11 -0
  9. package/dist/cli/cached-generate.test.d.ts.map +1 -0
  10. package/dist/cli/cached-generate.test.js +431 -0
  11. package/dist/cli/cartesia-provider.d.ts +16 -0
  12. package/dist/cli/cartesia-provider.d.ts.map +1 -0
  13. package/dist/cli/cartesia-provider.js +184 -0
  14. package/dist/cli/chatgpt-auth.d.ts.map +1 -0
  15. package/dist/cli/cli.d.ts +4 -0
  16. package/dist/cli/cli.d.ts.map +1 -0
  17. package/dist/{cli.js → cli/cli.js} +626 -600
  18. package/dist/cli/clone-generate.d.ts +26 -0
  19. package/dist/cli/clone-generate.d.ts.map +1 -0
  20. package/dist/cli/clone-generate.js +148 -0
  21. package/dist/cli/credentials.d.ts.map +1 -0
  22. package/dist/{credentials.js → cli/credentials.js} +20 -0
  23. package/dist/cli/elevenlabs-speech-provider.d.ts +26 -0
  24. package/dist/cli/elevenlabs-speech-provider.d.ts.map +1 -0
  25. package/dist/cli/elevenlabs-speech-provider.js +199 -0
  26. package/dist/cli/generate.d.ts +140 -0
  27. package/dist/cli/generate.d.ts.map +1 -0
  28. package/dist/cli/generate.js +604 -0
  29. package/dist/cli/generate.test.d.ts +2 -0
  30. package/dist/cli/generate.test.d.ts.map +1 -0
  31. package/dist/cli/generate.test.js +131 -0
  32. package/dist/cli/login.d.ts.map +1 -0
  33. package/dist/cli/main.d.ts +3 -0
  34. package/dist/cli/main.d.ts.map +1 -0
  35. package/dist/cli/main.js +7 -0
  36. package/dist/{model-catalog.d.ts → cli/model-catalog.d.ts} +6 -0
  37. package/dist/cli/model-catalog.d.ts.map +1 -0
  38. package/dist/{model-catalog.js → cli/model-catalog.js} +714 -4
  39. package/dist/{models.d.ts → cli/models.d.ts} +12 -5
  40. package/dist/cli/models.d.ts.map +1 -0
  41. package/dist/{models.js → cli/models.js} +58 -57
  42. package/dist/cli/open-browser.d.ts.map +1 -0
  43. package/dist/cli/openai-speech-provider.d.ts +3 -0
  44. package/dist/cli/openai-speech-provider.d.ts.map +1 -0
  45. package/dist/cli/openai-speech-provider.js +28 -0
  46. package/dist/cli/separation-generate.d.ts +34 -0
  47. package/dist/cli/separation-generate.d.ts.map +1 -0
  48. package/dist/cli/separation-generate.js +117 -0
  49. package/dist/cli/speech-catalog.d.ts +35 -0
  50. package/dist/cli/speech-catalog.d.ts.map +1 -0
  51. package/dist/cli/speech-catalog.js +195 -0
  52. package/dist/cli/speech-generate.d.ts +86 -0
  53. package/dist/cli/speech-generate.d.ts.map +1 -0
  54. package/dist/cli/speech-generate.js +156 -0
  55. package/dist/cli/speech-models.d.ts +8 -0
  56. package/dist/cli/speech-models.d.ts.map +1 -0
  57. package/dist/cli/speech-models.js +109 -0
  58. package/dist/cli/speech-provider.test.d.ts +2 -0
  59. package/dist/cli/speech-provider.test.d.ts.map +1 -0
  60. package/dist/cli/speech-provider.test.js +197 -0
  61. package/dist/cli/subscription.d.ts.map +1 -0
  62. package/dist/cli/transcription-catalog.d.ts +35 -0
  63. package/dist/cli/transcription-catalog.d.ts.map +1 -0
  64. package/dist/cli/transcription-catalog.js +192 -0
  65. package/dist/cli/transcription-generate.d.ts +62 -0
  66. package/dist/cli/transcription-generate.d.ts.map +1 -0
  67. package/dist/cli/transcription-generate.js +144 -0
  68. package/dist/cli/transcription-models.d.ts +9 -0
  69. package/dist/cli/transcription-models.d.ts.map +1 -0
  70. package/dist/cli/transcription-models.js +100 -0
  71. package/dist/cli/transcription-providers.d.ts +22 -0
  72. package/dist/cli/transcription-providers.d.ts.map +1 -0
  73. package/dist/cli/transcription-providers.js +290 -0
  74. package/dist/cli/video-model-catalog.d.ts.map +1 -0
  75. package/dist/cli/xai-auth.d.ts.map +1 -0
  76. package/dist/{xai-auth.js → cli/xai-auth.js} +1 -1
  77. package/dist/vite/angled-screen.d.ts +57 -0
  78. package/dist/vite/angled-screen.d.ts.map +1 -0
  79. package/dist/vite/angled-screen.js +115 -0
  80. package/dist/vite/app.d.ts +51 -0
  81. package/dist/vite/app.d.ts.map +1 -0
  82. package/dist/vite/app.js +150 -0
  83. package/dist/vite/bands-shader.d.ts +47 -0
  84. package/dist/vite/bands-shader.d.ts.map +1 -0
  85. package/dist/vite/bands-shader.js +267 -0
  86. package/dist/vite/code-block.d.ts +107 -0
  87. package/dist/vite/code-block.d.ts.map +1 -0
  88. package/dist/vite/code-block.js +641 -0
  89. package/dist/vite/components.d.ts +36 -0
  90. package/dist/vite/components.d.ts.map +1 -0
  91. package/dist/vite/components.js +113 -0
  92. package/dist/vite/dispersion-rings-shader.d.ts +44 -0
  93. package/dist/vite/dispersion-rings-shader.d.ts.map +1 -0
  94. package/dist/vite/dispersion-rings-shader.js +282 -0
  95. package/dist/vite/easing-curves.d.ts +129 -0
  96. package/dist/vite/easing-curves.d.ts.map +1 -0
  97. package/dist/vite/easing-curves.js +584 -0
  98. package/dist/vite/easing-curves.test.d.ts +2 -0
  99. package/dist/vite/easing-curves.test.d.ts.map +1 -0
  100. package/dist/vite/easing-curves.test.js +200 -0
  101. package/dist/vite/generated-media-client.d.ts +35 -0
  102. package/dist/vite/generated-media-client.d.ts.map +1 -0
  103. package/dist/vite/generated-media-client.js +76 -0
  104. package/dist/vite/keyframes.d.ts +129 -0
  105. package/dist/vite/keyframes.d.ts.map +1 -0
  106. package/dist/vite/keyframes.js +194 -0
  107. package/dist/vite/layout-editor.d.ts +45 -0
  108. package/dist/vite/layout-editor.d.ts.map +1 -0
  109. package/dist/vite/layout-editor.js +587 -0
  110. package/dist/vite/layout-transition.d.ts +136 -0
  111. package/dist/vite/layout-transition.d.ts.map +1 -0
  112. package/dist/vite/layout-transition.js +581 -0
  113. package/dist/vite/liquid-gradient-shader.d.ts +62 -0
  114. package/dist/vite/liquid-gradient-shader.d.ts.map +1 -0
  115. package/dist/vite/liquid-gradient-shader.js +413 -0
  116. package/dist/vite/mdx-client.d.ts +15 -0
  117. package/dist/vite/mdx-client.d.ts.map +1 -0
  118. package/dist/vite/mdx-client.js +265 -0
  119. package/dist/vite/mdx-parse.d.ts +102 -0
  120. package/dist/vite/mdx-parse.d.ts.map +1 -0
  121. package/dist/vite/mdx-parse.js +251 -0
  122. package/dist/vite/mdx-provided-components.d.ts +18 -0
  123. package/dist/vite/mdx-provided-components.d.ts.map +1 -0
  124. package/dist/vite/mdx-provided-components.js +14 -0
  125. package/dist/vite/mdx-video.d.ts +327 -0
  126. package/dist/vite/mdx-video.d.ts.map +1 -0
  127. package/dist/vite/mdx-video.js +596 -0
  128. package/dist/vite/mdx-video.test.d.ts +9 -0
  129. package/dist/vite/mdx-video.test.d.ts.map +1 -0
  130. package/dist/vite/mdx-video.test.js +1945 -0
  131. package/dist/vite/media-components.d.ts +45 -0
  132. package/dist/vite/media-components.d.ts.map +1 -0
  133. package/dist/vite/media-components.js +355 -0
  134. package/dist/vite/media-duration-store.d.ts +66 -0
  135. package/dist/vite/media-duration-store.d.ts.map +1 -0
  136. package/dist/vite/media-duration-store.js +204 -0
  137. package/dist/vite/player-page.d.ts +35 -0
  138. package/dist/vite/player-page.d.ts.map +1 -0
  139. package/dist/vite/player-page.js +720 -0
  140. package/dist/vite/render-client.d.ts +18 -0
  141. package/dist/vite/render-client.d.ts.map +1 -0
  142. package/dist/vite/render-client.js +67 -0
  143. package/dist/vite/sdk.d.ts +221 -0
  144. package/dist/vite/sdk.d.ts.map +1 -0
  145. package/dist/vite/sdk.js +355 -0
  146. package/dist/vite/server-components.d.ts +55 -0
  147. package/dist/vite/server-components.d.ts.map +1 -0
  148. package/dist/vite/server-components.js +156 -0
  149. package/dist/vite/server-mdx.d.ts +74 -0
  150. package/dist/vite/server-mdx.d.ts.map +1 -0
  151. package/dist/vite/server-mdx.js +259 -0
  152. package/dist/vite/shader-renderer.d.ts +48 -0
  153. package/dist/vite/shader-renderer.d.ts.map +1 -0
  154. package/dist/vite/shader-renderer.js +411 -0
  155. package/dist/vite/store-hooks.d.ts +12 -0
  156. package/dist/vite/store-hooks.d.ts.map +1 -0
  157. package/dist/vite/store-hooks.js +93 -0
  158. package/dist/vite/store.d.ts +75 -0
  159. package/dist/vite/store.d.ts.map +1 -0
  160. package/dist/vite/store.js +52 -0
  161. package/dist/vite/store.test.d.ts +6 -0
  162. package/dist/vite/store.test.d.ts.map +1 -0
  163. package/dist/vite/store.test.js +172 -0
  164. package/dist/vite/tweakpane-hook.d.ts +94 -0
  165. package/dist/vite/tweakpane-hook.d.ts.map +1 -0
  166. package/dist/vite/tweakpane-hook.js +450 -0
  167. package/dist/vite/vite-plugin.d.ts +28 -0
  168. package/dist/vite/vite-plugin.d.ts.map +1 -0
  169. package/dist/vite/vite-plugin.js +538 -0
  170. package/dist/vite/wave-gradient-shader.d.ts +41 -0
  171. package/dist/vite/wave-gradient-shader.d.ts.map +1 -0
  172. package/dist/vite/wave-gradient-shader.js +207 -0
  173. package/package.json +101 -24
  174. package/skills/egaki/SKILL.md +162 -1
  175. package/src/cli/cache-utils.ts +169 -0
  176. package/src/cli/cached-generate.test.ts +531 -0
  177. package/src/cli/cached-generate.ts +348 -0
  178. package/src/cli/cartesia-provider.ts +249 -0
  179. package/src/{cli.ts → cli/cli.ts} +836 -884
  180. package/src/cli/clone-generate.ts +202 -0
  181. package/src/{credentials.ts → cli/credentials.ts} +20 -0
  182. package/src/cli/elevenlabs-speech-provider.ts +243 -0
  183. package/src/cli/generate.test.ts +166 -0
  184. package/src/cli/generate.ts +912 -0
  185. package/src/cli/main.ts +8 -0
  186. package/src/{model-catalog.ts → cli/model-catalog.ts} +787 -58
  187. package/src/{models.ts → cli/models.ts} +65 -86
  188. package/src/cli/openai-speech-provider.ts +34 -0
  189. package/src/cli/separation-generate.ts +187 -0
  190. package/src/cli/speech-catalog.ts +253 -0
  191. package/src/cli/speech-generate.ts +275 -0
  192. package/src/cli/speech-models.ts +146 -0
  193. package/src/cli/speech-provider.test.ts +226 -0
  194. package/src/cli/transcription-catalog.ts +250 -0
  195. package/src/cli/transcription-generate.ts +214 -0
  196. package/src/cli/transcription-models.ts +136 -0
  197. package/src/cli/transcription-providers.ts +427 -0
  198. package/src/{xai-auth.ts → cli/xai-auth.ts} +1 -1
  199. package/src/vite/angled-screen.tsx +214 -0
  200. package/src/vite/app.tsx +197 -0
  201. package/src/vite/bands-shader.tsx +319 -0
  202. package/src/vite/code-block.tsx +939 -0
  203. package/src/vite/components.tsx +278 -0
  204. package/src/vite/dispersion-rings-shader.tsx +331 -0
  205. package/src/vite/easing-curves.test.ts +232 -0
  206. package/src/vite/easing-curves.ts +743 -0
  207. package/src/vite/generated-media-client.tsx +121 -0
  208. package/src/vite/keyframes.tsx +347 -0
  209. package/src/vite/layout-editor.tsx +709 -0
  210. package/src/vite/layout-transition.tsx +706 -0
  211. package/src/vite/liquid-gradient-shader.tsx +474 -0
  212. package/src/vite/mdx-client.tsx +420 -0
  213. package/src/vite/mdx-parse.ts +343 -0
  214. package/src/vite/mdx-provided-components.ts +19 -0
  215. package/src/vite/mdx-video.test.tsx +2219 -0
  216. package/src/vite/mdx-video.tsx +976 -0
  217. package/src/vite/media-components.tsx +473 -0
  218. package/src/vite/media-duration-store.ts +225 -0
  219. package/src/vite/motion-timing.ts +194 -0
  220. package/src/vite/player-page.tsx +1107 -0
  221. package/src/vite/render-client.ts +84 -0
  222. package/src/vite/sdk.ts +598 -0
  223. package/src/vite/server-components.tsx +241 -0
  224. package/src/vite/server-mdx.ts +285 -0
  225. package/src/vite/shader-renderer.tsx +535 -0
  226. package/src/vite/store-hooks.ts +117 -0
  227. package/src/vite/store.test.ts +202 -0
  228. package/src/vite/store.ts +115 -0
  229. package/src/vite/styles.css +21 -0
  230. package/src/vite/tweakpane-hook.tsx +593 -0
  231. package/src/vite/virtual-modules.d.ts +30 -0
  232. package/src/vite/vite-plugin.ts +584 -0
  233. package/src/vite/wave-gradient-shader.tsx +253 -0
  234. package/README.md +0 -370
  235. package/dist/antigravity-auth.d.ts +0 -15
  236. package/dist/antigravity-auth.d.ts.map +0 -1
  237. package/dist/antigravity-auth.js +0 -544
  238. package/dist/antigravity-image.d.ts +0 -11
  239. package/dist/antigravity-image.d.ts.map +0 -1
  240. package/dist/antigravity-image.js +0 -285
  241. package/dist/chatgpt-auth.d.ts.map +0 -1
  242. package/dist/cli.d.ts +0 -3
  243. package/dist/cli.d.ts.map +0 -1
  244. package/dist/credentials.d.ts.map +0 -1
  245. package/dist/gateway-model-candidates.generated.d.ts +0 -11
  246. package/dist/gateway-model-candidates.generated.d.ts.map +0 -1
  247. package/dist/gateway-model-candidates.generated.js +0 -64
  248. package/dist/login.d.ts.map +0 -1
  249. package/dist/model-catalog.d.ts.map +0 -1
  250. package/dist/models.d.ts.map +0 -1
  251. package/dist/open-browser.d.ts.map +0 -1
  252. package/dist/subscription.d.ts.map +0 -1
  253. package/dist/video-model-catalog.d.ts.map +0 -1
  254. package/dist/xai-auth.d.ts.map +0 -1
  255. /package/dist/{chatgpt-auth.d.ts → cli/chatgpt-auth.d.ts} +0 -0
  256. /package/dist/{chatgpt-auth.js → cli/chatgpt-auth.js} +0 -0
  257. /package/dist/{credentials.d.ts → cli/credentials.d.ts} +0 -0
  258. /package/dist/{login.d.ts → cli/login.d.ts} +0 -0
  259. /package/dist/{login.js → cli/login.js} +0 -0
  260. /package/dist/{open-browser.d.ts → cli/open-browser.d.ts} +0 -0
  261. /package/dist/{open-browser.js → cli/open-browser.js} +0 -0
  262. /package/dist/{subscription.d.ts → cli/subscription.d.ts} +0 -0
  263. /package/dist/{subscription.js → cli/subscription.js} +0 -0
  264. /package/dist/{video-model-catalog.d.ts → cli/video-model-catalog.d.ts} +0 -0
  265. /package/dist/{video-model-catalog.js → cli/video-model-catalog.js} +0 -0
  266. /package/dist/{xai-auth.d.ts → cli/xai-auth.d.ts} +0 -0
  267. /package/src/{chatgpt-auth.ts → cli/chatgpt-auth.ts} +0 -0
  268. /package/src/{login.ts → cli/login.ts} +0 -0
  269. /package/src/{open-browser.ts → cli/open-browser.ts} +0 -0
  270. /package/src/{subscription.ts → cli/subscription.ts} +0 -0
  271. /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
+ }
@@ -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()