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.
Files changed (280) hide show
  1. package/CHANGELOG.md +188 -0
  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/cli.js +1329 -0
  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/{credentials.d.ts → cli/credentials.d.ts} +11 -2
  22. package/dist/cli/credentials.d.ts.map +1 -0
  23. package/dist/{credentials.js → cli/credentials.js} +88 -5
  24. package/dist/cli/elevenlabs-speech-provider.d.ts +26 -0
  25. package/dist/cli/elevenlabs-speech-provider.d.ts.map +1 -0
  26. package/dist/cli/elevenlabs-speech-provider.js +199 -0
  27. package/dist/cli/generate.d.ts +140 -0
  28. package/dist/cli/generate.d.ts.map +1 -0
  29. package/dist/cli/generate.js +604 -0
  30. package/dist/cli/generate.test.d.ts +2 -0
  31. package/dist/cli/generate.test.d.ts.map +1 -0
  32. package/dist/cli/generate.test.js +131 -0
  33. package/dist/cli/login.d.ts.map +1 -0
  34. package/dist/{login.js → cli/login.js} +45 -4
  35. package/dist/cli/main.d.ts +3 -0
  36. package/dist/cli/main.d.ts.map +1 -0
  37. package/dist/cli/main.js +7 -0
  38. package/dist/cli/model-catalog.d.ts +141 -0
  39. package/dist/cli/model-catalog.d.ts.map +1 -0
  40. package/dist/cli/model-catalog.js +2135 -0
  41. package/dist/{models.d.ts → cli/models.d.ts} +16 -9
  42. package/dist/cli/models.d.ts.map +1 -0
  43. package/dist/cli/models.js +313 -0
  44. package/dist/cli/open-browser.d.ts.map +1 -0
  45. package/dist/cli/openai-speech-provider.d.ts +3 -0
  46. package/dist/cli/openai-speech-provider.d.ts.map +1 -0
  47. package/dist/cli/openai-speech-provider.js +28 -0
  48. package/dist/cli/separation-generate.d.ts +34 -0
  49. package/dist/cli/separation-generate.d.ts.map +1 -0
  50. package/dist/cli/separation-generate.js +117 -0
  51. package/dist/cli/speech-catalog.d.ts +35 -0
  52. package/dist/cli/speech-catalog.d.ts.map +1 -0
  53. package/dist/cli/speech-catalog.js +195 -0
  54. package/dist/cli/speech-generate.d.ts +86 -0
  55. package/dist/cli/speech-generate.d.ts.map +1 -0
  56. package/dist/cli/speech-generate.js +156 -0
  57. package/dist/cli/speech-models.d.ts +8 -0
  58. package/dist/cli/speech-models.d.ts.map +1 -0
  59. package/dist/cli/speech-models.js +109 -0
  60. package/dist/cli/speech-provider.test.d.ts +2 -0
  61. package/dist/cli/speech-provider.test.d.ts.map +1 -0
  62. package/dist/cli/speech-provider.test.js +197 -0
  63. package/dist/cli/subscription.d.ts.map +1 -0
  64. package/dist/cli/transcription-catalog.d.ts +35 -0
  65. package/dist/cli/transcription-catalog.d.ts.map +1 -0
  66. package/dist/cli/transcription-catalog.js +192 -0
  67. package/dist/cli/transcription-generate.d.ts +62 -0
  68. package/dist/cli/transcription-generate.d.ts.map +1 -0
  69. package/dist/cli/transcription-generate.js +144 -0
  70. package/dist/cli/transcription-models.d.ts +9 -0
  71. package/dist/cli/transcription-models.d.ts.map +1 -0
  72. package/dist/cli/transcription-models.js +100 -0
  73. package/dist/cli/transcription-providers.d.ts +22 -0
  74. package/dist/cli/transcription-providers.d.ts.map +1 -0
  75. package/dist/cli/transcription-providers.js +290 -0
  76. package/dist/cli/video-model-catalog.d.ts +2 -0
  77. package/dist/cli/video-model-catalog.d.ts.map +1 -0
  78. package/dist/cli/video-model-catalog.js +5 -0
  79. package/dist/cli/xai-auth.d.ts +29 -0
  80. package/dist/cli/xai-auth.d.ts.map +1 -0
  81. package/dist/cli/xai-auth.js +325 -0
  82. package/dist/vite/angled-screen.d.ts +57 -0
  83. package/dist/vite/angled-screen.d.ts.map +1 -0
  84. package/dist/vite/angled-screen.js +115 -0
  85. package/dist/vite/app.d.ts +51 -0
  86. package/dist/vite/app.d.ts.map +1 -0
  87. package/dist/vite/app.js +150 -0
  88. package/dist/vite/bands-shader.d.ts +47 -0
  89. package/dist/vite/bands-shader.d.ts.map +1 -0
  90. package/dist/vite/bands-shader.js +267 -0
  91. package/dist/vite/code-block.d.ts +107 -0
  92. package/dist/vite/code-block.d.ts.map +1 -0
  93. package/dist/vite/code-block.js +641 -0
  94. package/dist/vite/components.d.ts +36 -0
  95. package/dist/vite/components.d.ts.map +1 -0
  96. package/dist/vite/components.js +113 -0
  97. package/dist/vite/dispersion-rings-shader.d.ts +44 -0
  98. package/dist/vite/dispersion-rings-shader.d.ts.map +1 -0
  99. package/dist/vite/dispersion-rings-shader.js +282 -0
  100. package/dist/vite/easing-curves.d.ts +129 -0
  101. package/dist/vite/easing-curves.d.ts.map +1 -0
  102. package/dist/vite/easing-curves.js +584 -0
  103. package/dist/vite/easing-curves.test.d.ts +2 -0
  104. package/dist/vite/easing-curves.test.d.ts.map +1 -0
  105. package/dist/vite/easing-curves.test.js +200 -0
  106. package/dist/vite/generated-media-client.d.ts +35 -0
  107. package/dist/vite/generated-media-client.d.ts.map +1 -0
  108. package/dist/vite/generated-media-client.js +76 -0
  109. package/dist/vite/keyframes.d.ts +129 -0
  110. package/dist/vite/keyframes.d.ts.map +1 -0
  111. package/dist/vite/keyframes.js +194 -0
  112. package/dist/vite/layout-editor.d.ts +45 -0
  113. package/dist/vite/layout-editor.d.ts.map +1 -0
  114. package/dist/vite/layout-editor.js +587 -0
  115. package/dist/vite/layout-transition.d.ts +136 -0
  116. package/dist/vite/layout-transition.d.ts.map +1 -0
  117. package/dist/vite/layout-transition.js +581 -0
  118. package/dist/vite/liquid-gradient-shader.d.ts +62 -0
  119. package/dist/vite/liquid-gradient-shader.d.ts.map +1 -0
  120. package/dist/vite/liquid-gradient-shader.js +413 -0
  121. package/dist/vite/mdx-client.d.ts +15 -0
  122. package/dist/vite/mdx-client.d.ts.map +1 -0
  123. package/dist/vite/mdx-client.js +265 -0
  124. package/dist/vite/mdx-parse.d.ts +102 -0
  125. package/dist/vite/mdx-parse.d.ts.map +1 -0
  126. package/dist/vite/mdx-parse.js +251 -0
  127. package/dist/vite/mdx-provided-components.d.ts +18 -0
  128. package/dist/vite/mdx-provided-components.d.ts.map +1 -0
  129. package/dist/vite/mdx-provided-components.js +14 -0
  130. package/dist/vite/mdx-video.d.ts +327 -0
  131. package/dist/vite/mdx-video.d.ts.map +1 -0
  132. package/dist/vite/mdx-video.js +596 -0
  133. package/dist/vite/mdx-video.test.d.ts +9 -0
  134. package/dist/vite/mdx-video.test.d.ts.map +1 -0
  135. package/dist/vite/mdx-video.test.js +1945 -0
  136. package/dist/vite/media-components.d.ts +45 -0
  137. package/dist/vite/media-components.d.ts.map +1 -0
  138. package/dist/vite/media-components.js +355 -0
  139. package/dist/vite/media-duration-store.d.ts +66 -0
  140. package/dist/vite/media-duration-store.d.ts.map +1 -0
  141. package/dist/vite/media-duration-store.js +204 -0
  142. package/dist/vite/player-page.d.ts +35 -0
  143. package/dist/vite/player-page.d.ts.map +1 -0
  144. package/dist/vite/player-page.js +720 -0
  145. package/dist/vite/render-client.d.ts +18 -0
  146. package/dist/vite/render-client.d.ts.map +1 -0
  147. package/dist/vite/render-client.js +67 -0
  148. package/dist/vite/sdk.d.ts +221 -0
  149. package/dist/vite/sdk.d.ts.map +1 -0
  150. package/dist/vite/sdk.js +355 -0
  151. package/dist/vite/server-components.d.ts +55 -0
  152. package/dist/vite/server-components.d.ts.map +1 -0
  153. package/dist/vite/server-components.js +156 -0
  154. package/dist/vite/server-mdx.d.ts +74 -0
  155. package/dist/vite/server-mdx.d.ts.map +1 -0
  156. package/dist/vite/server-mdx.js +259 -0
  157. package/dist/vite/shader-renderer.d.ts +48 -0
  158. package/dist/vite/shader-renderer.d.ts.map +1 -0
  159. package/dist/vite/shader-renderer.js +411 -0
  160. package/dist/vite/store-hooks.d.ts +12 -0
  161. package/dist/vite/store-hooks.d.ts.map +1 -0
  162. package/dist/vite/store-hooks.js +93 -0
  163. package/dist/vite/store.d.ts +75 -0
  164. package/dist/vite/store.d.ts.map +1 -0
  165. package/dist/vite/store.js +52 -0
  166. package/dist/vite/store.test.d.ts +6 -0
  167. package/dist/vite/store.test.d.ts.map +1 -0
  168. package/dist/vite/store.test.js +172 -0
  169. package/dist/vite/tweakpane-hook.d.ts +94 -0
  170. package/dist/vite/tweakpane-hook.d.ts.map +1 -0
  171. package/dist/vite/tweakpane-hook.js +450 -0
  172. package/dist/vite/vite-plugin.d.ts +28 -0
  173. package/dist/vite/vite-plugin.d.ts.map +1 -0
  174. package/dist/vite/vite-plugin.js +538 -0
  175. package/dist/vite/wave-gradient-shader.d.ts +41 -0
  176. package/dist/vite/wave-gradient-shader.d.ts.map +1 -0
  177. package/dist/vite/wave-gradient-shader.js +207 -0
  178. package/package.json +103 -25
  179. package/skills/egaki/SKILL.md +162 -1
  180. package/src/cli/cache-utils.ts +169 -0
  181. package/src/cli/cached-generate.test.ts +531 -0
  182. package/src/cli/cached-generate.ts +348 -0
  183. package/src/cli/cartesia-provider.ts +249 -0
  184. package/src/cli/cli.ts +1818 -0
  185. package/src/cli/clone-generate.ts +202 -0
  186. package/src/{credentials.ts → cli/credentials.ts} +90 -7
  187. package/src/cli/elevenlabs-speech-provider.ts +243 -0
  188. package/src/cli/generate.test.ts +166 -0
  189. package/src/cli/generate.ts +912 -0
  190. package/src/{login.ts → cli/login.ts} +48 -3
  191. package/src/cli/main.ts +8 -0
  192. package/src/cli/model-catalog.ts +2336 -0
  193. package/src/cli/models.ts +408 -0
  194. package/src/cli/openai-speech-provider.ts +34 -0
  195. package/src/cli/separation-generate.ts +187 -0
  196. package/src/cli/speech-catalog.ts +253 -0
  197. package/src/cli/speech-generate.ts +275 -0
  198. package/src/cli/speech-models.ts +146 -0
  199. package/src/cli/speech-provider.test.ts +226 -0
  200. package/src/cli/transcription-catalog.ts +250 -0
  201. package/src/cli/transcription-generate.ts +214 -0
  202. package/src/cli/transcription-models.ts +136 -0
  203. package/src/cli/transcription-providers.ts +427 -0
  204. package/src/cli/video-model-catalog.ts +14 -0
  205. package/src/cli/xai-auth.ts +396 -0
  206. package/src/vite/angled-screen.tsx +214 -0
  207. package/src/vite/app.tsx +197 -0
  208. package/src/vite/bands-shader.tsx +319 -0
  209. package/src/vite/code-block.tsx +939 -0
  210. package/src/vite/components.tsx +278 -0
  211. package/src/vite/dispersion-rings-shader.tsx +331 -0
  212. package/src/vite/easing-curves.test.ts +232 -0
  213. package/src/vite/easing-curves.ts +743 -0
  214. package/src/vite/generated-media-client.tsx +121 -0
  215. package/src/vite/keyframes.tsx +347 -0
  216. package/src/vite/layout-editor.tsx +709 -0
  217. package/src/vite/layout-transition.tsx +706 -0
  218. package/src/vite/liquid-gradient-shader.tsx +474 -0
  219. package/src/vite/mdx-client.tsx +420 -0
  220. package/src/vite/mdx-parse.ts +343 -0
  221. package/src/vite/mdx-provided-components.ts +19 -0
  222. package/src/vite/mdx-video.test.tsx +2219 -0
  223. package/src/vite/mdx-video.tsx +976 -0
  224. package/src/vite/media-components.tsx +473 -0
  225. package/src/vite/media-duration-store.ts +225 -0
  226. package/src/vite/motion-timing.ts +194 -0
  227. package/src/vite/player-page.tsx +1107 -0
  228. package/src/vite/render-client.ts +84 -0
  229. package/src/vite/sdk.ts +598 -0
  230. package/src/vite/server-components.tsx +241 -0
  231. package/src/vite/server-mdx.ts +285 -0
  232. package/src/vite/shader-renderer.tsx +535 -0
  233. package/src/vite/store-hooks.ts +117 -0
  234. package/src/vite/store.test.ts +202 -0
  235. package/src/vite/store.ts +115 -0
  236. package/src/vite/styles.css +21 -0
  237. package/src/vite/tweakpane-hook.tsx +593 -0
  238. package/src/vite/virtual-modules.d.ts +30 -0
  239. package/src/vite/vite-plugin.ts +584 -0
  240. package/src/vite/wave-gradient-shader.tsx +253 -0
  241. package/README.md +0 -266
  242. package/dist/antigravity-auth.d.ts +0 -15
  243. package/dist/antigravity-auth.d.ts.map +0 -1
  244. package/dist/antigravity-auth.js +0 -544
  245. package/dist/antigravity-image.d.ts +0 -11
  246. package/dist/antigravity-image.d.ts.map +0 -1
  247. package/dist/antigravity-image.js +0 -285
  248. package/dist/chatgpt-auth.d.ts.map +0 -1
  249. package/dist/cli.d.ts +0 -3
  250. package/dist/cli.d.ts.map +0 -1
  251. package/dist/cli.js +0 -968
  252. package/dist/credentials.d.ts.map +0 -1
  253. package/dist/gateway-model-candidates.generated.d.ts +0 -11
  254. package/dist/gateway-model-candidates.generated.d.ts.map +0 -1
  255. package/dist/gateway-model-candidates.generated.js +0 -64
  256. package/dist/login.d.ts.map +0 -1
  257. package/dist/model-catalog.d.ts +0 -42
  258. package/dist/model-catalog.d.ts.map +0 -1
  259. package/dist/model-catalog.js +0 -1001
  260. package/dist/models.d.ts.map +0 -1
  261. package/dist/models.js +0 -246
  262. package/dist/open-browser.d.ts.map +0 -1
  263. package/dist/subscription.d.ts.map +0 -1
  264. package/dist/video-model-catalog.d.ts +0 -52
  265. package/dist/video-model-catalog.d.ts.map +0 -1
  266. package/dist/video-model-catalog.js +0 -306
  267. package/src/cli.ts +0 -1378
  268. package/src/model-catalog.ts +0 -1077
  269. package/src/models.ts +0 -309
  270. package/src/video-model-catalog.ts +0 -363
  271. /package/dist/{chatgpt-auth.d.ts → cli/chatgpt-auth.d.ts} +0 -0
  272. /package/dist/{chatgpt-auth.js → cli/chatgpt-auth.js} +0 -0
  273. /package/dist/{login.d.ts → cli/login.d.ts} +0 -0
  274. /package/dist/{open-browser.d.ts → cli/open-browser.d.ts} +0 -0
  275. /package/dist/{open-browser.js → cli/open-browser.js} +0 -0
  276. /package/dist/{subscription.d.ts → cli/subscription.d.ts} +0 -0
  277. /package/dist/{subscription.js → cli/subscription.js} +0 -0
  278. /package/src/{chatgpt-auth.ts → cli/chatgpt-auth.ts} +0 -0
  279. /package/src/{open-browser.ts → cli/open-browser.ts} +0 -0
  280. /package/src/{subscription.ts → cli/subscription.ts} +0 -0
@@ -0,0 +1,641 @@
1
+ 'use client';
2
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
3
+ /**
4
+ * CodeBlock — ray.so-faithful syntax-highlighted code block for video.
5
+ *
6
+ * Uses shiki (TextMate grammars + WASM) for real syntax highlighting,
7
+ * with a CSS variables theme that maps token scopes to --ray-* vars.
8
+ * Theme colors are injected as inline CSS custom properties. The HTML
9
+ * from shiki's codeToHtml() is rendered via dangerouslySetInnerHTML.
10
+ *
11
+ * Per-theme frame styles are ported from ray.so's frame components:
12
+ * - Vercel: gridlines + corner brackets, no window chrome
13
+ * - Stripe: dashed gridlines + skewed stripe shape
14
+ * - OpenAI: clean rounded window, navy background
15
+ * - Supabase/Cloudflare: filename header, custom borders
16
+ * - Default: macOS traffic lights + title bar (non-partner themes)
17
+ *
18
+ * Source: https://github.com/raycast/ray-so
19
+ *
20
+ * Key source files:
21
+ * - Themes & syntax colors: https://github.com/raycast/ray-so/blob/main/app/(navigation)/(code)/store/themes.ts
22
+ * - CSS variables theme: https://github.com/raycast/ray-so/blob/main/app/(navigation)/(code)/util/theme-css-variables.ts
23
+ * - Shiki highlighter init: https://github.com/raycast/ray-so/blob/main/app/(navigation)/(code)/code.tsx
24
+ * - Highlighted code render: https://github.com/raycast/ray-so/blob/main/app/(navigation)/(code)/components/HighlightedCode.tsx
25
+ * - Editor + token CSS: https://github.com/raycast/ray-so/blob/main/app/(navigation)/(code)/components/Editor.module.css
26
+ * - Frame switch: https://github.com/raycast/ray-so/blob/main/app/(navigation)/(code)/components/Frame.tsx
27
+ * - Default frame: https://github.com/raycast/ray-so/blob/main/app/(navigation)/(code)/components/frames/DefaultFrame.tsx
28
+ * - Vercel frame: https://github.com/raycast/ray-so/blob/main/app/(navigation)/(code)/components/frames/VercelFrame.tsx
29
+ * - Stripe frame: https://github.com/raycast/ray-so/blob/main/app/(navigation)/(code)/components/frames/StripeFrame.tsx
30
+ * - OpenAI frame: https://github.com/raycast/ray-so/blob/main/app/(navigation)/(code)/components/frames/OpenAIFrame.tsx
31
+ * - Supabase frame: https://github.com/raycast/ray-so/blob/main/app/(navigation)/(code)/components/frames/SupabaseFrame.tsx
32
+ * - Cloudflare frame: https://github.com/raycast/ray-so/blob/main/app/(navigation)/(code)/components/frames/CloudflareFrame.tsx
33
+ */
34
+ import { createContext, useContext, useEffect, useMemo, useState } from 'react';
35
+ import { interpolate, spring, useCurrentFrame, useVideoConfig } from 'remotion';
36
+ import { useTweakpane } from "./tweakpane-hook.js";
37
+ // ---------------------------------------------------------------------------
38
+ // Nesting guard
39
+ // ---------------------------------------------------------------------------
40
+ export const CodeBlockContext = createContext(false);
41
+ // ---------------------------------------------------------------------------
42
+ // CSS variables theme — exact port from ray.so's theme-css-variables.ts
43
+ // Maps TextMate scopes to --ray-* CSS variables.
44
+ // ---------------------------------------------------------------------------
45
+ function createCssVariablesTheme() {
46
+ const prefix = '--ray-';
47
+ const v = (name) => `var(${prefix}${name})`;
48
+ return {
49
+ name: 'css-variables',
50
+ type: 'dark',
51
+ colors: {
52
+ 'editor.foreground': v('foreground'),
53
+ 'editor.background': v('background'),
54
+ },
55
+ tokenColors: [
56
+ { scope: ['keyword.operator.accessor', 'meta.group.braces.round.function.arguments', 'meta.template.expression', 'markup.fenced_code meta.embedded.block'], settings: { foreground: v('foreground') } },
57
+ { scope: 'emphasis', settings: { fontStyle: 'italic' } },
58
+ { scope: ['strong', 'markup.heading.markdown', 'markup.bold.markdown'], settings: { fontStyle: 'bold' } },
59
+ { scope: ['markup.italic.markdown'], settings: { fontStyle: 'italic' } },
60
+ { scope: 'meta.link.inline.markdown', settings: { fontStyle: 'underline', foreground: v('token-link') } },
61
+ { scope: ['string', 'markup.fenced_code', 'markup.inline', 'string.quoted.docstring.multi.python'], settings: { foreground: v('token-string') } },
62
+ { scope: ['comment', 'string.quoted.docstring.multi', 'meta.diff.header.from-file', 'meta.diff.header.to-file'], settings: { foreground: v('token-comment') } },
63
+ { scope: ['constant.numeric', 'constant.language', 'constant.other.placeholder', 'constant.character.format.placeholder', 'variable.language.this', 'variable.other.object', 'variable.other.class', 'variable.other.constant', 'meta.property-name', 'meta.property-value', 'support'], settings: { foreground: v('token-constant') } },
64
+ { scope: ['keyword', 'storage.modifier', 'storage.type', 'storage.control.clojure', 'entity.name.function.clojure', 'entity.name.tag.yaml', 'support.function.node', 'support.type.property-name.json', 'punctuation.separator.key-value', 'punctuation.definition.template-expression'], settings: { foreground: v('token-keyword') } },
65
+ { scope: 'variable.parameter.function', settings: { foreground: v('token-parameter') } },
66
+ { scope: ['support.function', 'entity.name.type', 'entity.other.inherited-class', 'meta.function-call', 'meta.instance.constructor', 'entity.other.attribute-name', 'entity.name.function', 'constant.keyword.clojure'], settings: { foreground: v('token-function') } },
67
+ { scope: ['entity.name.tag', 'string.quoted', 'string.regexp', 'string.interpolated', 'string.template', 'string.unquoted.plain.out.yaml', 'keyword.other.template'], settings: { foreground: v('token-string-expression') } },
68
+ { scope: ['punctuation.definition.arguments', 'punctuation.definition.dict', 'punctuation.separator', 'meta.function-call.arguments'], settings: { foreground: v('token-punctuation') } },
69
+ { scope: ['markup.underline.link', 'punctuation.definition.metadata.markdown'], settings: { foreground: v('token-link') } },
70
+ { scope: ['beginning.punctuation.definition.list.markdown'], settings: { foreground: v('token-string') } },
71
+ { scope: ['punctuation.definition.string.begin.markdown', 'punctuation.definition.string.end.markdown', 'string.other.link.title.markdown', 'string.other.link.description.markdown'], settings: { foreground: v('token-keyword') } },
72
+ { scope: ['constant.numeric.decimal', 'constant.language.boolean', 'meta.var.exp.ts'], settings: { foreground: v('token-number') } },
73
+ { scope: ['meta.objectliteral'], settings: { foreground: v('token-object-literal') } },
74
+ { scope: ['support.variable.property'], settings: { foreground: v('token-property') } },
75
+ { scope: ['punctuation.definition.deleted.diff', 'markup.deleted.diff'], settings: { foreground: v('token-diff-deleted') } },
76
+ { scope: ['punctuation.definition.inserted.diff', 'markup.inserted.diff'], settings: { foreground: v('token-diff-inserted') } },
77
+ ],
78
+ };
79
+ }
80
+ const CSS_VARS_THEME = createCssVariablesTheme();
81
+ let highlighterPromise = null;
82
+ let highlighterInstance = null;
83
+ async function getHighlighter() {
84
+ if (highlighterInstance)
85
+ return highlighterInstance;
86
+ if (highlighterPromise)
87
+ return highlighterPromise;
88
+ highlighterPromise = (async () => {
89
+ const { createHighlighter } = await import('shiki');
90
+ const h = await createHighlighter({
91
+ themes: [CSS_VARS_THEME],
92
+ langs: ['typescript', 'tsx', 'javascript', 'jsx', 'python', 'bash', 'json', 'css', 'html', 'markdown', 'yaml', 'rust', 'go', 'sql', 'diff', 'swift', 'ruby', 'java', 'c', 'cpp'],
93
+ });
94
+ highlighterInstance = h;
95
+ return h;
96
+ })();
97
+ return highlighterPromise;
98
+ }
99
+ function syntaxToCssVars(s) {
100
+ return {
101
+ '--ray-foreground': s.foreground,
102
+ '--ray-token-constant': s.constant,
103
+ '--ray-token-string': s.string,
104
+ '--ray-token-comment': s.comment,
105
+ '--ray-token-keyword': s.keyword,
106
+ '--ray-token-parameter': s.parameter,
107
+ '--ray-token-function': s.function,
108
+ '--ray-token-string-expression': s.stringExpression,
109
+ '--ray-token-punctuation': s.punctuation,
110
+ '--ray-token-link': s.link,
111
+ '--ray-token-number': s.number,
112
+ '--ray-token-property': s.property,
113
+ '--ray-token-object-literal': s.objectLiteral,
114
+ '--ray-highlight': s.highlight,
115
+ '--ray-highlight-border': s.highlightBorder,
116
+ '--ray-highlight-hover': s.highlightHover,
117
+ '--ray-token-diff-deleted': s.diffDeleted,
118
+ '--ray-token-diff-inserted': s.diffInserted,
119
+ };
120
+ }
121
+ // ---------------------------------------------------------------------------
122
+ // Theme type
123
+ // ---------------------------------------------------------------------------
124
+ /** Font stack used for code rendering. Uses the preferred theme font with
125
+ * safe system monospace fallbacks. ray.so uses per-theme web fonts (Geist
126
+ * Mono, Source Code Pro, etc.) which are loaded via Next.js font optimization.
127
+ * In egaki video we can't rely on web fonts being installed, so the stack
128
+ * falls through to SF Mono / Menlo / Consolas which are available on every
129
+ * major OS and render digits correctly in all browsers. */
130
+ const CODE_FONT = '"SF Mono", "Menlo", "Consolas", "Liberation Mono", monospace';
131
+ const CODE_FONT_WEIGHT = 400;
132
+ // ---------------------------------------------------------------------------
133
+ // Themes — curated from ray.so
134
+ // ---------------------------------------------------------------------------
135
+ export const CODE_THEMES = {
136
+ vercel: {
137
+ id: 'vercel',
138
+ name: 'Vercel',
139
+ background: { from: '#232323', to: '#1F1F1F' },
140
+ frame: 'vercel',
141
+ font: 'geist-mono',
142
+ frameColors: {
143
+ frameBg: '#000000',
144
+ frameBgLight: '#ffffff',
145
+ gridline: '#1a1a1a',
146
+ gridlineLight: '#ebebeb',
147
+ },
148
+ syntax: {
149
+ light: { foreground: 'hsla(0, 0%, 9%, 1)', constant: 'oklch(53.18% 0.2399 256.99)', string: 'oklch(51.75% 0.1453 147.65)', comment: 'hsla(0, 0%, 40%, 1)', keyword: 'oklch(53.5% 0.2058 2.84)', parameter: 'oklch(52.79% 0.1496 54.65)', function: 'oklch(47.18% 0.2579 304)', stringExpression: 'oklch(51.75% 0.1453 147.65)', punctuation: 'hsla(0, 0%, 9%, 1)', number: '#111111', property: 'oklch(53.18% 0.2399 256.99)', highlight: 'oklch(94.58% 0.0293 249.85)', highlightHover: 'oklch(94.58% 0.0293 249.85 / 30%)', highlightBorder: 'oklch(53.18% 0.2399 256.99)' },
150
+ dark: { foreground: 'hsla(0, 0%, 93%, 1)', constant: 'oklch(71.7% 0.1648 250.79)', string: 'oklch(73.1% 0.2158 148.29)', comment: 'hsla(0, 0%, 63%, 1)', keyword: 'oklch(69.36% 0.2223 3.91)', parameter: 'oklch(77.21% 0.1991 64.28)', function: 'oklch(69.87% 0.2037 309.51)', stringExpression: 'oklch(73.1% 0.2158 148.29)', punctuation: 'hsla(0, 0%, 93%, 1)', number: '#ffffff', property: 'oklch(71.7% 0.1648 250.79)', highlight: 'oklch(30.86% 0.1022 255.21)', highlightHover: 'oklch(30.86% 0.1022 255.21 / 30%)', highlightBorder: 'oklch(71.7% 0.1648 250.79)' },
151
+ },
152
+ },
153
+ stripe: {
154
+ id: 'stripe',
155
+ name: 'Stripe',
156
+ background: { from: '#0a2540', to: '#0a2540' },
157
+ frame: 'stripe',
158
+ font: 'source-code-pro',
159
+ frameColors: {
160
+ frameBg: '#0a2540',
161
+ windowBg: '#0c2e4e',
162
+ border: '#0f395e',
163
+ gridline: 'rgba(255, 255, 255, 0.1)',
164
+ lineNumber: '#55718d',
165
+ },
166
+ syntax: {
167
+ dark: { foreground: '#FFFFFF', constant: '#FFFFFF', string: '#ffa956', comment: '#a9bcce', keyword: '#8095ff', parameter: '#FF6B35', function: '#00d4ff', stringExpression: '#ffa956', punctuation: '#FFFFFF', number: '#ffa956', property: '#1abdc0', objectLiteral: '#1abdc0', highlight: 'rgba(255, 107, 53, 0.15)', highlightBorder: '#FF6B35', highlightHover: 'rgba(255, 107, 53, 0.08)' },
168
+ },
169
+ },
170
+ openai: {
171
+ id: 'openai',
172
+ name: 'OpenAI',
173
+ background: { from: '#000', to: '#000' },
174
+ frame: 'openai',
175
+ font: 'soehne-mono',
176
+ frameColors: {
177
+ frameBg: '#121a29',
178
+ frameBgLight: 'linear-gradient(238deg, #f1f0f4 0%, #f8f8fd 100%)',
179
+ windowBg: '#232b41',
180
+ windowBgLight: '#fff',
181
+ border: 'rgba(255, 255, 255, 0.1)',
182
+ borderLight: 'rgba(0, 0, 0, 0.1)',
183
+ lineNumber: 'rgba(255, 255, 255, 0.2)',
184
+ lineNumberLight: 'hsla(240, 12%, 71%, 1)',
185
+ },
186
+ syntax: {
187
+ light: { foreground: '#171717', constant: '#DF3079', string: '#171717', comment: 'hsla(240, 12%, 71%, 1)', keyword: '#2E95D3', parameter: '#ededed', function: '#00A67D', stringExpression: '#00a67d', punctuation: '#171717', number: '#e9950c', property: '#F22C3D' },
188
+ dark: { foreground: '#fff', constant: '#df3079', string: '#fff', comment: 'rgba(255,255,255,0.4)', keyword: '#2E95D3', parameter: '#fff', function: '#00A67D', stringExpression: '#00A67D', punctuation: '#fff', number: '#e9950c', property: '#F22C3D', highlight: 'rgba(255, 255, 255, 0.05)', highlightHover: 'rgba(255, 255, 255, 0.03)' },
189
+ },
190
+ },
191
+ supabase: {
192
+ id: 'supabase',
193
+ name: 'Supabase',
194
+ background: { from: '#121212', to: '#121212' },
195
+ frame: 'supabase',
196
+ font: 'jetbrains-mono',
197
+ frameColors: {
198
+ frameBg: '#121212',
199
+ frameBgLight: '#fcfcfc',
200
+ windowBg: '#171717',
201
+ windowBgLight: '#f8f8f8',
202
+ border: '#292929',
203
+ borderLight: '#dfdfdf',
204
+ headerBg: '#1f1f1f',
205
+ headerBgLight: '#fcfcfc',
206
+ },
207
+ syntax: {
208
+ light: { foreground: '#525252', constant: '#15593b', string: '#f1a10d', comment: '#7e7e7e', keyword: '#6b35dc', parameter: '#525252', function: '#15593b', stringExpression: '#f1a10d', punctuation: '#a0a0a0', number: '#525252', property: '#15593b', highlight: 'oklch(0.88 0.22 153.28 / 0.12)', highlightHover: 'oklch(0.88 0.22 153.28 / 0.06)', highlightBorder: '#009a55' },
209
+ dark: { foreground: '#ffffff', constant: '#3ecf8e', string: '#ffcda1', comment: '#7e7e7e', keyword: '#bda4ff', parameter: '#ffffff', function: '#3ecf8e', stringExpression: '#ffcda1', punctuation: '#ffffff', number: '#ededed', property: '#3ecf8e', highlight: '#232323', highlightHover: '#1D1D1D', highlightBorder: '#383838' },
210
+ },
211
+ },
212
+ cloudflare: {
213
+ id: 'cloudflare',
214
+ name: 'Cloudflare',
215
+ background: { from: '#0C0C0C', to: '#0C0C0C' },
216
+ frame: 'cloudflare',
217
+ font: 'ibm-plex-mono',
218
+ frameColors: {
219
+ frameBg: '#0c0c0c',
220
+ frameBgLight: '#f5f5f5',
221
+ windowBg: '#0c0c0c',
222
+ windowBgLight: '#ffffff',
223
+ border: 'transparent',
224
+ borderLight: 'transparent',
225
+ headerBg: '#0f0f0f',
226
+ headerBgLight: '#fafafa',
227
+ gridline: '#262626',
228
+ gridlineLight: '#e5e5e5',
229
+ },
230
+ syntax: {
231
+ light: { foreground: '#521000', constant: '#5a11cc', string: '#0876d9', comment: '#52100080', keyword: '#d94008', parameter: '#c77700', function: '#7612cc', stringExpression: '#0876d9', punctuation: '#52100080', number: '#5a11cc', property: '#5a11cc', highlight: 'rgba(255, 80, 10, 0.1)', highlightHover: 'rgba(255, 80, 10, 0.05)', highlightBorder: '#FF500A' },
232
+ dark: { foreground: '#E8E8E8', constant: '#79b8ff', string: '#0A95FF', comment: '#888888', keyword: '#FF7F4D', parameter: '#FFB366', function: '#B084FF', stringExpression: '#0A95FF', punctuation: '#AAAAAA', number: '#79b8ff', property: '#79b8ff', highlight: 'rgba(255, 80, 10, 0.15)', highlightHover: 'rgba(255, 80, 10, 0.08)', highlightBorder: '#FF500A' },
233
+ },
234
+ },
235
+ gemini: {
236
+ id: 'gemini', name: 'Gemini', background: { from: '#16181d', to: '#16181d' }, frame: 'default',
237
+ syntax: { dark: { foreground: '#abb2bf', constant: '#56b6c2', string: '#98c379', comment: '#5c6370', keyword: '#5c9dc7', parameter: '#d19a66', function: '#98c379', stringExpression: '#98c379', punctuation: '#abb2bf', number: '#56b6c2', property: '#56b6c2', highlight: 'rgba(92, 157, 199, 0.15)', highlightHover: 'rgba(92, 157, 199, 0.1)', highlightBorder: '#5c9dc7' } },
238
+ },
239
+ clerk: {
240
+ id: 'clerk', name: 'Clerk', background: { from: '#000000', to: '#000000' }, frame: 'default', font: 'geist-mono',
241
+ syntax: { dark: { foreground: '#ffffff', constant: '#86ef9b', string: '#5de3ff', comment: '#9394a1', keyword: '#bab1ff', parameter: '#86ef9b', function: '#bab1ff', stringExpression: '#5de3ff', punctuation: '#b7b8c2', number: '#86ef9b', property: '#86ef9b', highlight: '#5de3ff1a', highlightHover: '#5de3ff0d', highlightBorder: '#00000000' } },
242
+ },
243
+ prisma: {
244
+ id: 'prisma', name: 'Prisma', background: { from: '#000', to: '#000' }, frame: 'default',
245
+ syntax: { dark: { foreground: '#ffffff', constant: '#7F9CF5', string: '#71E8DF', comment: '#718096', keyword: '#71E8DF', parameter: '#71E8DF', function: '#7F9CF5', stringExpression: '#71E8DF', punctuation: '#FFFFFF', number: '#71E8DF', property: '#71E8DF', highlight: '#71e8de2e', highlightHover: '#71e8de1b', highlightBorder: '#71E8DF' } },
246
+ },
247
+ elevenlabs: {
248
+ id: 'elevenlabs', name: 'ElevenLabs', background: { from: '#000', to: '#000' }, frame: 'default', font: 'roboto-mono',
249
+ syntax: { dark: { foreground: '#fff', constant: '#8F8FFF', string: '#a1ffe0', comment: 'hsla(0, 0%, 63%, 1)', keyword: '#fff9b2', parameter: '#8F8FFF', function: '#ff8080', stringExpression: '#A1FFE0', punctuation: '#fff', number: '#8F8FFF', property: '#8F8FFF', highlight: 'hsla(240, 100%, 78%, 0.09)', highlightHover: 'hsla(240, 100%, 78%, 0.05)', highlightBorder: 'hsla(240, 100%, 78%, 0.45)' } },
250
+ },
251
+ triggerdev: {
252
+ id: 'triggerdev', name: 'Trigger.dev', background: { from: '#121317', to: '#121317' }, frame: 'default', font: 'geist-mono',
253
+ syntax: { dark: { foreground: '#CCCBFF', constant: '#9C9AF2', string: '#AFEC73', comment: '#5F6570', keyword: '#E888F8', parameter: '#CCCBFF', function: '#9684FF', stringExpression: '#AFEC73', punctuation: '#878C99', number: '#b5cea8', property: '#CCCBFF' } },
254
+ },
255
+ nuxt: {
256
+ id: 'nuxt', name: 'Nuxt', background: { from: '#292D3E', to: '#292D3E' }, frame: 'default', font: 'geist-mono',
257
+ syntax: { dark: { foreground: '#babed8', constant: '#BABED8', string: '#C3E88D', comment: '#676E95', keyword: '#C793EA', parameter: '#babed8', function: '#82AAFF', stringExpression: '#f07178', punctuation: '#89DDFF', number: '#F78C6C', property: '#f07178', highlight: 'rgba(113, 124, 180, 0.31)', highlightHover: 'rgba(113, 124, 180, 0.2)', highlightBorder: '#80CBC4' } },
258
+ },
259
+ tailwind: {
260
+ id: 'tailwind', name: 'Tailwind', background: { from: '#36B6F0', to: '#36B6F0' }, frame: 'default', font: 'fira-code',
261
+ syntax: { dark: { foreground: '#fff', constant: '#C1B2F9', string: '#C1B2F9', comment: 'rgba(255,255,255,0.4)', keyword: '#C1B2F9', punctuation: 'rgba(255,255,255,0.6)', number: '#C1B2F9', property: '#C1B2F9', function: '#fff', highlight: 'rgba(193,178,249,0.12)', highlightBorder: '#C1B2F9' } },
262
+ },
263
+ resend: {
264
+ id: 'resend', name: 'Resend', background: { from: '#B1B1B1', to: '#181818' }, frame: 'default', font: 'commit-mono',
265
+ syntax: { dark: { foreground: '#ffffff', constant: '#a7a7a7', string: '#a7a7a7', comment: '#666666', keyword: '#a7a7a7', function: '#ffffff', punctuation: '#a7a7a7', number: '#ffffff', property: '#a7a7a7' } },
266
+ },
267
+ auth0: {
268
+ id: 'auth0', name: 'Auth0', background: { from: '#171717', to: '#24173A' }, frame: 'default',
269
+ syntax: { dark: { foreground: '#F1F1F1', constant: '#99A7F1', string: '#98D2B2', comment: '#808080', keyword: '#B59DF8', parameter: '#F1F1F1', function: '#8B66F4', stringExpression: '#98D2B2', punctuation: '#FDE66F', number: '#B59DF8', property: '#B9C3F5', highlight: 'rgba(82, 139, 255, 0.24)', highlightHover: 'rgba(82, 139, 255, 0.12)', highlightBorder: '#528BFF' } },
270
+ },
271
+ noir: {
272
+ id: 'noir', name: 'Noir', background: { from: '#B1B1B1', to: '#181818' }, frame: 'default',
273
+ syntax: {
274
+ light: { foreground: '#111111', constant: '#666666', keyword: '#666666', function: '#111111', punctuation: '#666666', string: '#666666', comment: '#999999', number: '#111111', property: '#666666' },
275
+ dark: { foreground: '#ffffff', constant: '#a7a7a7', keyword: '#a7a7a7', function: '#ffffff', punctuation: '#a7a7a7', string: '#a7a7a7', comment: '#666666', number: '#ffffff', property: '#a7a7a7' },
276
+ },
277
+ },
278
+ mono: {
279
+ id: 'mono', name: 'Mono', background: { from: '#333', to: '#181818' }, frame: 'default',
280
+ syntax: { dark: { foreground: '#ffffff', constant: '#a7a7a7', keyword: '#a7a7a7', function: '#ffffff', punctuation: '#a7a7a7', string: '#a7a7a7', comment: '#666666', number: '#ffffff', property: '#a7a7a7' } },
281
+ },
282
+ bitmap: {
283
+ id: 'bitmap', name: 'Bitmap', background: { from: '#881616', to: '#F1393F' }, frame: 'default',
284
+ syntax: { dark: { foreground: '#FEFDFD', constant: '#E42B37', string: '#E42B37', comment: '#996B6D', keyword: '#EB6F6F', parameter: '#C88E8E', function: '#E42B37', stringExpression: '#EBB99D', punctuation: '#EB6F6F', number: '#E42B37', property: '#E42B37', highlight: 'hsla(355, 76%, 63%, 0.25)', highlightBorder: '#E42B37', highlightHover: 'hsla(355, 76%, 63%, 0.16)' } },
285
+ },
286
+ ice: {
287
+ id: 'ice', name: 'Ice', background: { from: '#fff', to: '#80deea' }, frame: 'default',
288
+ syntax: {
289
+ light: { foreground: '#1C1B29', constant: '#00B0E9', string: '#6ABAD8', comment: '#BDC0C1', keyword: '#81909D', parameter: '#1E3C78', function: '#1E3C78', stringExpression: '#7BBCD8', punctuation: '#1E3C78', number: '#00B0E9', property: '#00B0E9', highlight: 'rgba(0,167,219,0.1)', highlightBorder: '#00B0E9', highlightHover: 'rgba(0,167,219,0.05)' },
290
+ dark: { foreground: '#FFFFFF', constant: '#92DEF6', string: '#92DEF6', comment: '#5C6A70', keyword: '#BFC4C9', parameter: '#778CB6', function: '#778CB6', stringExpression: '#89C3DC', punctuation: '#778CB6', number: '#00B0E9', property: '#00B0E9', highlight: 'rgba(146,222,246,0.14)', highlightBorder: '#92DEF6', highlightHover: 'rgba(146,222,246,0.09)' },
291
+ },
292
+ },
293
+ sand: { id: 'sand', name: 'Sand', background: { from: '#EED5B6', to: '#AF8856' }, frame: 'default', syntax: { dark: { foreground: '#FFFFFF', constant: '#C2B181', string: '#C2B181', comment: '#837E77', keyword: '#D3B48C', parameter: '#F4A361', function: '#F4A361', stringExpression: '#EED5B8', punctuation: '#F4A361', number: '#C2B181', property: '#C2B181', highlight: 'rgba(244,163,97,0.14)', highlightBorder: '#F4A361', highlightHover: 'rgba(244,163,97,0.09)' } } },
294
+ forest: { id: 'forest', name: 'Forest', background: { from: '#506853', to: '#213223' }, frame: 'default', syntax: { dark: { foreground: '#FFFFFF', constant: '#6B8F71', string: '#C9C8BC', comment: '#555E56', keyword: '#AAB4A3', parameter: '#6B8F71', function: '#87B882', stringExpression: '#CCBD6E', punctuation: '#AAB4A3', number: '#AAB4A3', property: '#C9C7BC', highlight: 'rgba(170,180,163,0.14)', highlightBorder: '#6B8F71', highlightHover: 'rgba(170,180,163,0.09)' } } },
295
+ breeze: { id: 'breeze', name: 'Breeze', background: { from: '#CF2F98', to: '#6A3DEC' }, frame: 'default', syntax: { dark: { foreground: '#FFFFFF', constant: '#49E8F2', string: '#E9AEFE', comment: '#8A757D', keyword: '#6599FF', parameter: '#F8518D', function: '#F8518D', stringExpression: '#E9AEFE', punctuation: '#F8518D', number: '#55E7B2', property: '#49E8F2' } } },
296
+ candy: { id: 'candy', name: 'Candy', background: { from: '#A58EFB', to: '#E9BFF8' }, frame: 'default', syntax: { dark: { foreground: '#FFFFFF', constant: '#1AC8FF', string: '#DFD473', comment: '#807796', keyword: '#FF659C', parameter: '#1AC8FF', function: '#73DFA5', stringExpression: '#DFD473', punctuation: '#FF659C', number: '#7A7FFD', property: '#1AC8FF' } } },
297
+ crimson: { id: 'crimson', name: 'Crimson', background: { from: '#FF6363', to: '#733434' }, frame: 'default', syntax: { dark: { foreground: '#FEFDFD', constant: '#D15510', string: '#EBB99D', comment: '#895E60', keyword: '#EB6F6F', parameter: '#C88E8E', function: '#C88E8E', stringExpression: '#EBB99D', punctuation: '#EB6F6F', number: '#FDA97A', property: '#D15510' } } },
298
+ falcon: { id: 'falcon', name: 'Falcon', background: { from: '#BDE3EC', to: '#363654' }, frame: 'default', syntax: { dark: { foreground: '#FFFFFF', constant: '#799DB1', string: '#6A8697', comment: '#6D7E88', keyword: '#9AB6B2', parameter: '#6D88BB', function: '#6D88BB', stringExpression: '#789083', punctuation: '#9AB6B2', number: '#BD9C9C', property: '#799DB1' } } },
299
+ meadow: { id: 'meadow', name: 'Meadow', background: { from: '#59D499', to: '#A0872D' }, frame: 'default', syntax: { dark: { foreground: '#FFFFFF', constant: '#E4B165', string: '#E9EB9D', comment: '#708B6C', keyword: '#6DD79F', parameter: '#B3D767', function: '#B3D767', stringExpression: '#E9EB9D', punctuation: '#6DD79F', number: '#46B114', property: '#E4B165' } } },
300
+ midnight: { id: 'midnight', name: 'Midnight', background: { from: '#4CC8C8', to: '#202033' }, frame: 'default', syntax: { dark: { foreground: '#FFFFFF', constant: '#9681C2', string: '#6D86A4', comment: '#4A4C56', keyword: '#7DA9AB', parameter: '#51D0F8', function: '#51D0F8', stringExpression: '#6D86A4', punctuation: '#7DA9AB', number: '#75D2B1', property: '#9681C2' } } },
301
+ raindrop: { id: 'raindrop', name: 'Raindrop', background: { from: '#8EC7FB', to: '#1C55AA' }, frame: 'default', syntax: { dark: { foreground: '#E4F2FF', constant: '#008BB7', string: '#9DD8EB', comment: '#6C808B', keyword: '#2ED9FF', parameter: '#1AD6B5', function: '#1AD6B5', stringExpression: '#9DD8EB', punctuation: '#2ED9FF', number: '#9984EE', property: '#008BB7' } } },
302
+ sunset: { id: 'sunset', name: 'Sunset', background: { from: '#FFCF73', to: '#FF7A2F' }, frame: 'default', syntax: { dark: { foreground: '#FFFFFF', constant: '#E978A1', string: '#F9D38C', comment: '#878572', keyword: '#FFAF65', parameter: '#E2D66B', function: '#E2D66B', stringExpression: '#F9D38C', punctuation: '#FFAF65', number: '#E7CF55', property: '#E978A1' } } },
303
+ };
304
+ // ---------------------------------------------------------------------------
305
+ // Language detection from filename or className
306
+ // ---------------------------------------------------------------------------
307
+ const EXT_TO_LANG = {
308
+ ts: 'tsx', tsx: 'tsx', js: 'javascript', jsx: 'jsx', py: 'python',
309
+ sh: 'bash', bash: 'bash', json: 'json', css: 'css', html: 'html',
310
+ md: 'markdown', yml: 'yaml', yaml: 'yaml', rs: 'rust', go: 'go',
311
+ sql: 'sql', diff: 'diff', swift: 'swift', rb: 'ruby', java: 'java',
312
+ c: 'c', cpp: 'cpp', h: 'c', hpp: 'cpp', typescript: 'tsx',
313
+ javascript: 'javascript', python: 'python', ruby: 'ruby',
314
+ };
315
+ function detectLanguage(title, language) {
316
+ if (language) {
317
+ return EXT_TO_LANG[language.toLowerCase()] || language.toLowerCase();
318
+ }
319
+ if (title) {
320
+ const ext = title.split('.').pop()?.toLowerCase();
321
+ if (ext && EXT_TO_LANG[ext])
322
+ return EXT_TO_LANG[ext];
323
+ }
324
+ return 'tsx';
325
+ }
326
+ // ---------------------------------------------------------------------------
327
+ // useHighlightedHtml — async shiki highlighting with state
328
+ // ---------------------------------------------------------------------------
329
+ function useHighlightedHtml(code, lang, highlightLines) {
330
+ const [html, setHtml] = useState('');
331
+ useEffect(() => {
332
+ let cancelled = false;
333
+ void getHighlighter().then((h) => {
334
+ if (cancelled)
335
+ return;
336
+ // Load language on demand if not already loaded
337
+ const loaded = h.getLoadedLanguages();
338
+ const langToUse = loaded.includes(lang) ? lang : 'tsx';
339
+ const result = h.codeToHtml(code, {
340
+ lang: langToUse,
341
+ theme: 'css-variables',
342
+ transformers: [{
343
+ line(node, line) {
344
+ node.properties = node.properties || {};
345
+ node.properties['data-line'] = line;
346
+ node.properties.style = (node.properties.style || '') + ';padding:0 16px;margin:0 -16px;display:inline-block;width:calc(100% + 32px);';
347
+ if (highlightLines.includes(line)) {
348
+ node.properties.style += 'background-color:var(--ray-highlight, rgba(255,255,255,0.06));';
349
+ node.properties['class'] = ((node.properties['class'] || '') + ' highlighted-line').trim();
350
+ }
351
+ },
352
+ }],
353
+ });
354
+ if (!cancelled)
355
+ setHtml(result);
356
+ });
357
+ return () => { cancelled = true; };
358
+ }, [code, lang, highlightLines.join(',')]);
359
+ return html;
360
+ }
361
+ // ---------------------------------------------------------------------------
362
+ // Frame sub-components
363
+ // ---------------------------------------------------------------------------
364
+ const FONT_SANS = '-apple-system, BlinkMacSystemFont, "SF Pro Display", "Segoe UI", sans-serif';
365
+ /** Vercel: gridlines extending beyond the code + corner brackets */
366
+ /** Gridlines that extend beyond the code window to the edges of the
367
+ * nearest overflow:hidden ancestor (the CodeBlock outer container).
368
+ * Uses large fixed offsets instead of vw/vh so they stay contained
369
+ * when multiple CodeBlocks sit in a grid. */
370
+ function VercelGridlines({ color }) {
371
+ const line = { position: 'absolute', background: color };
372
+ return (_jsxs(_Fragment, { children: [_jsx("span", { style: { ...line, top: 0, left: -9999, width: 99999, height: 1 } }), _jsx("span", { style: { ...line, bottom: 0, left: -9999, width: 99999, height: 1 } }), _jsx("span", { style: { ...line, top: -9999, left: 0, width: 1, height: 99999 } }), _jsx("span", { style: { ...line, top: -9999, right: 0, width: 1, height: 99999 } })] }));
373
+ }
374
+ function VercelBracket({ position, color }) {
375
+ const isTopLeft = position === 'top-left';
376
+ return (_jsxs("span", { style: {
377
+ position: 'absolute',
378
+ ...(isTopLeft ? { top: -12, left: -12 } : { bottom: -12, right: -12 }),
379
+ width: 25,
380
+ height: 25,
381
+ display: 'flex',
382
+ alignItems: 'center',
383
+ justifyContent: 'center',
384
+ }, children: [_jsx("span", { style: { position: 'absolute', top: 12, width: '100%', height: 1, background: color } }), _jsx("span", { style: { position: 'absolute', left: 12, width: 1, height: '100%', background: color } })] }));
385
+ }
386
+ /** Stripe: dashed vertical gridlines + skewed stripe shape */
387
+ function StripeBackground({ gridlineColor, windowWidth }) {
388
+ const positions = [0, 25, 50, 75, 100];
389
+ const lineStyle = (pct, solid) => ({
390
+ position: 'absolute',
391
+ top: 0,
392
+ left: `${pct}%`,
393
+ width: 1,
394
+ height: '100%',
395
+ borderLeft: `1px ${solid ? 'solid' : 'dashed'} ${gridlineColor}`,
396
+ pointerEvents: 'none',
397
+ });
398
+ return (_jsxs("div", { style: { position: 'absolute', inset: 0, overflow: 'hidden', pointerEvents: 'none' }, children: [_jsx("div", { style: { position: 'relative', width: windowWidth, height: '100%', margin: '0 auto' }, children: positions.map((pct) => (_jsx("div", { style: lineStyle(pct, pct === 0 || pct === 100) }, pct))) }), _jsx("div", { style: {
399
+ position: 'absolute',
400
+ bottom: 0,
401
+ left: 0,
402
+ width: '100%',
403
+ height: '40%',
404
+ background: 'hsla(213.69, 52%, 97.828%)',
405
+ transform: 'skewY(-6deg)',
406
+ transformOrigin: '100% 0',
407
+ }, children: _jsxs("div", { style: { position: 'relative', width: windowWidth, height: '100%', margin: '0 auto' }, children: [positions.map((pct) => (_jsx("div", { style: { ...lineStyle(pct, pct === 0 || pct === 100), borderColor: 'rgba(66, 71, 112, 0.15)' } }, pct))), _jsxs("div", { style: { position: 'absolute', bottom: 65, left: '75%', width: 500, height: 50 }, children: [_jsx("div", { style: { position: 'absolute', width: '100%', height: '100%', background: 'rgb(17, 239, 227)' } }), _jsx("div", { style: { position: 'absolute', width: '100%', height: 32, background: 'rgb(153, 102, 255)', transform: 'translate(50px, 50px)' } }), _jsx("div", { style: { position: 'absolute', width: '100%', height: 18, background: 'hsla(221.1, 99.822%, 44.876%)', transform: 'translate(50px, 32px)' } })] })] }) })] }));
408
+ }
409
+ /** Filename header for Supabase/Cloudflare style */
410
+ function FilenameHeader({ title, border, bg, titleColor, fontFamily }) {
411
+ return (_jsx("div", { style: {
412
+ display: 'flex',
413
+ height: 40,
414
+ alignItems: 'center',
415
+ justifyContent: 'space-between',
416
+ padding: '0 16px',
417
+ borderBottom: `1px solid ${border}`,
418
+ background: bg,
419
+ fontFamily,
420
+ fontSize: 14,
421
+ color: titleColor,
422
+ }, children: _jsx("span", { children: title }) }));
423
+ }
424
+ /** Default frame: macOS traffic lights + title. Matches ray.so DefaultFrame
425
+ * header: 24px grid with 60px | 1fr | 60px columns, 9px dot gap, no border. */
426
+ function DefaultChrome({ title, titleColor }) {
427
+ return (_jsxs("div", { style: {
428
+ display: 'grid',
429
+ height: 24,
430
+ alignItems: 'center',
431
+ padding: '0 16px',
432
+ gap: 12,
433
+ gridTemplateColumns: '60px 1fr 60px',
434
+ }, children: [_jsx("div", { style: { display: 'flex', gap: 9 }, children: ['a', 'b', 'c'].map((k) => (_jsx("div", { style: { width: 14, height: 14, borderRadius: 7, backgroundColor: 'rgba(128,128,128,0.2)' } }, k))) }), _jsx("div", { style: { textAlign: 'center', color: titleColor, fontSize: 12, fontWeight: 500, letterSpacing: '0.32px', lineHeight: '12px', fontFamily: FONT_SANS, overflow: 'hidden', whiteSpace: 'nowrap', textOverflow: 'ellipsis' }, children: title || '' }), _jsx("div", {})] }));
435
+ }
436
+ // ---------------------------------------------------------------------------
437
+ // Editor — code display via dangerouslySetInnerHTML
438
+ // ---------------------------------------------------------------------------
439
+ function CodeEditor({ html, showLineNumbers, lineCount, cssVars, lineNumColor, fontSize, fontFamily, fontWeight }) {
440
+ // Line number gutter widths computed from fontSize so they scale correctly.
441
+ // A monospace digit is ~0.6em wide; we size the gutter to fit the widest
442
+ // line number plus comfortable spacing.
443
+ // A monospace character is roughly 0.6em wide. Size the gutter to fit the
444
+ // widest line number plus comfortable spacing, all relative to fontSize.
445
+ const ch = Math.ceil(fontSize * 0.62);
446
+ const gutterDigits = lineCount > 99 ? 3 : 2;
447
+ const gutterWidth = ch * (gutterDigits + 1); // extra ch for breathing room
448
+ const gutterGap = Math.round(fontSize * 0.8); // space between number and code
449
+ const gutterTotal = gutterWidth + gutterGap;
450
+ const lineNumberCss = showLineNumbers ? `
451
+ .shiki code .line { padding-left: ${gutterTotal}px !important; }
452
+ .shiki code .line::before {
453
+ content: attr(data-line);
454
+ display: inline-block;
455
+ width: ${gutterWidth}px;
456
+ margin-right: ${gutterGap}px;
457
+ margin-left: -${gutterTotal}px;
458
+ color: ${lineNumColor};
459
+ text-align: right;
460
+ }
461
+ ` : '';
462
+ const editorCss = `
463
+ .shiki { margin: 0; background-color: transparent !important; font-family: inherit; white-space: pre-wrap; }
464
+ .shiki code { font-family: inherit; }
465
+ .shiki code .line { transition: width 0.2s, padding 0.2s, margin 0.2s; }
466
+ .highlighted-line { position: relative; }
467
+ .highlighted-line::after {
468
+ content: "";
469
+ position: absolute;
470
+ top: 0;
471
+ bottom: 0;
472
+ left: -1px;
473
+ width: 2px;
474
+ background-color: var(--ray-highlight-border, var(--ray-token-keyword));
475
+ }
476
+ ${lineNumberCss}
477
+ `;
478
+ return (_jsxs("div", { style: {
479
+ padding: 16,
480
+ fontFamily,
481
+ fontWeight,
482
+ fontSize,
483
+ lineHeight: 1.7,
484
+ letterSpacing: '0.1px',
485
+ tabSize: 2,
486
+ fontVariantLigatures: 'none',
487
+ ...cssVars,
488
+ }, children: [_jsx("style", { dangerouslySetInnerHTML: { __html: editorCss } }), _jsx("div", { dangerouslySetInnerHTML: { __html: html } })] }));
489
+ }
490
+ export function CodeBlock({ children, theme: themeId = 'vercel', darkMode = true, title, language, showLineNumbers = false, highlightLines = [], padding = 32, fontSize = 15, width = 780, height, showBackground = true, staggerFrames = 0, }) {
491
+ const frame = useCurrentFrame();
492
+ const { fps } = useVideoConfig();
493
+ const tp = useTweakpane('CodeBlock', {
494
+ theme: { value: themeId, options: Object.keys(CODE_THEMES).sort() },
495
+ darkMode,
496
+ showLineNumbers,
497
+ showBackground,
498
+ fontSize: { value: fontSize, min: 8, max: 32, step: 1 },
499
+ padding: { value: padding, min: 0, max: 80, step: 4 },
500
+ });
501
+ const resolvedThemeId = tp.theme;
502
+ const themeData = (CODE_THEMES[resolvedThemeId] ?? CODE_THEMES.vercel);
503
+ const fc = themeData.frameColors || {};
504
+ // Resolve dark/light mode
505
+ const hasDark = !!themeData.syntax.dark;
506
+ const hasLight = !!themeData.syntax.light;
507
+ const effectiveDark = hasDark && !hasLight ? true : hasLight && !hasDark ? false : tp.darkMode;
508
+ const colors = (effectiveDark ? themeData.syntax.dark : themeData.syntax.light);
509
+ const cssVars = syntaxToCssVars(colors);
510
+ // Extract code and highlight
511
+ const code = extractCodeString(children);
512
+ const lines = code.split('\n');
513
+ const lang = detectLanguage(title, language);
514
+ const html = useHighlightedHtml(code, lang, highlightLines);
515
+ // Entrance animation
516
+ const windowScale = staggerFrames > 0
517
+ ? spring({ frame, fps, config: { damping: 20, stiffness: 80 } })
518
+ : 1;
519
+ const windowOpacity = staggerFrames > 0
520
+ ? interpolate(frame, [0, 10], [0, 1], { extrapolateRight: 'clamp' })
521
+ : 1;
522
+ // Frame-specific colors
523
+ const frameBg = effectiveDark ? (fc.frameBg || 'transparent') : (fc.frameBgLight || 'transparent');
524
+ const windowBg = effectiveDark ? (fc.windowBg || 'rgba(0, 0, 0, 0.88)') : (fc.windowBgLight || 'rgba(255, 255, 255, 0.95)');
525
+ const borderColor = effectiveDark ? (fc.border || 'rgba(255,255,255,0.08)') : (fc.borderLight || 'rgba(0,0,0,0.08)');
526
+ const lineNumColor = effectiveDark ? (fc.lineNumber || 'rgba(255,255,255,0.2)') : (fc.lineNumberLight || 'rgba(0,0,0,0.2)');
527
+ const gridlineColor = effectiveDark ? (fc.gridline || '#1a1a1a') : (fc.gridlineLight || '#ebebeb');
528
+ const headerBg = effectiveDark ? (fc.headerBg || windowBg) : (fc.headerBgLight || windowBg);
529
+ const titleColor = effectiveDark ? 'rgba(255,255,255,0.5)' : 'rgba(0,0,0,0.5)';
530
+ // Background: either theme gradient or frame-specific solid
531
+ const outerBg = tp.showBackground
532
+ ? (themeData.frame === 'default'
533
+ ? `linear-gradient(140deg, ${themeData.background.from}, ${themeData.background.to})`
534
+ : frameBg)
535
+ : 'transparent';
536
+ const outerWidth = typeof width === 'number' ? width + tp.padding * 2 : width;
537
+ const outerHeight = height ? (typeof height === 'number' ? height + tp.padding * 2 : height) : undefined;
538
+ const editorEl = (_jsx(CodeEditor, { html: html, showLineNumbers: tp.showLineNumbers, lineCount: lines.length, cssVars: cssVars, lineNumColor: lineNumColor, fontSize: tp.fontSize, fontFamily: CODE_FONT, fontWeight: CODE_FONT_WEIGHT }));
539
+ function renderFrame() {
540
+ switch (themeData.frame) {
541
+ case 'vercel':
542
+ return (_jsxs("div", { style: { position: 'relative' }, children: [_jsx(VercelGridlines, { color: gridlineColor }), _jsx(VercelBracket, { position: "top-left", color: "#515356" }), _jsx(VercelBracket, { position: "bottom-right", color: "#515356" }), editorEl] }));
543
+ case 'stripe':
544
+ return (_jsxs(_Fragment, { children: [tp.showBackground && (_jsx(StripeBackground, { gridlineColor: fc.gridline || 'rgba(255,255,255,0.1)', windowWidth: typeof width === 'number' ? width : 780 })), _jsx("div", { style: {
545
+ position: 'relative',
546
+ zIndex: 1,
547
+ border: `1px solid ${borderColor}`,
548
+ borderRadius: 8,
549
+ background: windowBg,
550
+ boxShadow: 'rgba(50, 50, 93, 0.25) 0px 50px 100px -20px, rgba(0, 0, 0, 0.3) 0px 30px 60px -30px',
551
+ }, children: editorEl })] }));
552
+ case 'openai':
553
+ return (_jsx("div", { style: {
554
+ position: 'relative',
555
+ zIndex: 1,
556
+ border: `0.5px solid ${borderColor}`,
557
+ borderRadius: 8,
558
+ background: windowBg,
559
+ boxShadow: '0px 100px 89px 0px rgba(0,0,0,0.07), 0px 41.778px 37.182px 0px rgba(0,0,0,0.05), 0px 22.336px 19.879px 0px rgba(0,0,0,0.04), 0px 12.522px 11.144px 0px rgba(0,0,0,0.04), 0px 6.65px 5.919px 0px rgba(0,0,0,0.03), 0px 2.767px 2.463px 0px rgba(0,0,0,0.02)',
560
+ }, children: editorEl }));
561
+ case 'supabase':
562
+ return (_jsxs("div", { style: {
563
+ border: `1px solid ${borderColor}`,
564
+ borderRadius: 6,
565
+ background: windowBg,
566
+ overflow: 'hidden',
567
+ }, children: [title && _jsx(FilenameHeader, { title: title, border: borderColor, bg: headerBg, titleColor: effectiveDark ? '#fafafa' : '#171717', fontFamily: CODE_FONT }), editorEl] }));
568
+ case 'cloudflare':
569
+ return (_jsxs("div", { style: { position: 'relative' }, children: [_jsx(VercelGridlines, { color: gridlineColor }), _jsxs("div", { style: { position: 'relative', background: windowBg }, children: [title && _jsx(FilenameHeader, { title: title, border: gridlineColor, bg: headerBg, titleColor: effectiveDark ? '#ededed' : 'oklch(14.5% 0 0)', fontFamily: CODE_FONT }), editorEl] })] }));
570
+ case 'default':
571
+ default: {
572
+ // ray.so uses box-shadow borders (not CSS border) + multi-layer shadow
573
+ const shadowBorder = effectiveDark
574
+ ? '0 0 0 1px rgba(255,255,255,0.06), 0 0 0 1.5px rgba(0,0,0,0.4)'
575
+ : '0 0 0 1px rgba(0,0,0,0.04), 0 0 0 1.5px rgba(0,0,0,0.08)';
576
+ const dropShadow = tp.showBackground
577
+ ? ', 0 2.8px 2.2px rgba(0,0,0,0.034), 0 6.7px 5.3px rgba(0,0,0,0.048), 0 12.5px 10px rgba(0,0,0,0.06), 0 22.3px 17.9px rgba(0,0,0,0.072), 0 41.8px 33.4px rgba(0,0,0,0.086), 0 100px 80px rgba(0,0,0,0.12)'
578
+ : '';
579
+ return (_jsxs("div", { style: {
580
+ display: 'flex',
581
+ flexDirection: 'column',
582
+ minHeight: 100,
583
+ paddingTop: 10,
584
+ borderRadius: 18,
585
+ background: windowBg,
586
+ overflow: 'hidden',
587
+ boxShadow: shadowBorder + dropShadow,
588
+ }, children: [_jsx(DefaultChrome, { title: title, titleColor: titleColor }), editorEl] }));
589
+ }
590
+ }
591
+ }
592
+ return (_jsx(CodeBlockContext.Provider, { value: true, children: _jsx("div", { style: {
593
+ display: 'flex',
594
+ alignItems: 'center',
595
+ justifyContent: 'center',
596
+ padding: tp.padding,
597
+ background: outerBg,
598
+ borderRadius: tp.showBackground ? (themeData.frame === 'default' ? 18 : 0) : 0,
599
+ width: outerWidth,
600
+ height: outerHeight,
601
+ transform: `scale(${windowScale})`,
602
+ opacity: windowOpacity,
603
+ overflow: 'hidden',
604
+ position: 'relative',
605
+ }, children: renderFrame() }) }));
606
+ }
607
+ // ---------------------------------------------------------------------------
608
+ // Helper: extract code string from React children
609
+ // ---------------------------------------------------------------------------
610
+ function extractCodeString(children) {
611
+ if (typeof children === 'string')
612
+ return children.trimEnd();
613
+ if (children == null)
614
+ return '';
615
+ if (typeof children === 'object' && 'props' in children) {
616
+ const el = children;
617
+ if (el.props?.children != null)
618
+ return extractCodeString(el.props.children);
619
+ }
620
+ if (Array.isArray(children))
621
+ return children.map(extractCodeString).join('');
622
+ return String(children).trimEnd();
623
+ }
624
+ // ---------------------------------------------------------------------------
625
+ // MDX code block wrapper
626
+ // ---------------------------------------------------------------------------
627
+ export function MdxCodeBlockWrapper({ children, ...rest }) {
628
+ const isNested = useContext(CodeBlockContext);
629
+ if (isNested)
630
+ return _jsx("pre", { ...rest, children: children });
631
+ let language;
632
+ if (children && typeof children === 'object' && 'props' in children) {
633
+ const className = children.props?.className;
634
+ if (typeof className === 'string') {
635
+ const match = className.match(/language-(\w+)/);
636
+ if (match)
637
+ language = match[1];
638
+ }
639
+ }
640
+ return (_jsx("div", { style: { width: '100%', maxWidth: '80%', display: 'flex', justifyContent: 'center' }, children: _jsx(CodeBlock, { title: language || undefined, language: language, width: "100%", showBackground: false, showLineNumbers: true, children: children }) }));
641
+ }