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,202 @@
1
+ /**
2
+ * Tests for the centralized egaki client store.
3
+ * Pure state transitions — no React, no mocks, just data in and data out.
4
+ */
5
+
6
+ import { describe, test, expect, beforeEach } from 'vitest'
7
+ import {
8
+ egakiStore,
9
+ selectMediaDurations,
10
+ selectGenerationStatus,
11
+ } from './store.ts'
12
+ import {
13
+ reportSectionDuration,
14
+ clearSectionDuration,
15
+ resetSectionDurations,
16
+ } from './media-duration-store.ts'
17
+
18
+ beforeEach(() => {
19
+ egakiStore.setState({
20
+ modules: {},
21
+ sectionReports: new Map(),
22
+ serverGenerationStatus: null,
23
+ serverGenerationEntries: [],
24
+ serverGenerationErrors: [],
25
+ })
26
+ })
27
+
28
+ // ---------------------------------------------------------------------------
29
+ // Modules
30
+ // ---------------------------------------------------------------------------
31
+
32
+ describe('modules', () => {
33
+ test('setState updates modules', () => {
34
+ const mods = { './foo.tsx': { default: () => null } }
35
+ egakiStore.setState({ modules: mods })
36
+ expect(egakiStore.getState().modules).toBe(mods)
37
+ })
38
+
39
+ test('setState replaces previous modules', () => {
40
+ egakiStore.setState({ modules: { './a.tsx': { default: 1 } } })
41
+ const next = { './b.tsx': { default: 2 } }
42
+ egakiStore.setState({ modules: next })
43
+ expect(egakiStore.getState().modules).toBe(next)
44
+ expect(egakiStore.getState().modules['./a.tsx']).toBeUndefined()
45
+ })
46
+ })
47
+
48
+ // ---------------------------------------------------------------------------
49
+ // Section duration reports
50
+ // ---------------------------------------------------------------------------
51
+
52
+ describe('sectionReports', () => {
53
+ test('reportSectionDuration adds a report', () => {
54
+ reportSectionDuration(0, 'inst-1', 5.5)
55
+ const durations = selectMediaDurations(egakiStore.getState())
56
+ expect(durations).toMatchInlineSnapshot(`
57
+ {
58
+ "0": 5.5,
59
+ }
60
+ `)
61
+ })
62
+
63
+ test('per-section max is derived from multiple instances', () => {
64
+ reportSectionDuration(0, 'inst-1', 3)
65
+ reportSectionDuration(0, 'inst-2', 7)
66
+ reportSectionDuration(0, 'inst-3', 5)
67
+ const durations = selectMediaDurations(egakiStore.getState())
68
+ expect(durations['0']).toBe(7)
69
+ })
70
+
71
+ test('multiple sections tracked independently', () => {
72
+ reportSectionDuration(0, 'a', 2)
73
+ reportSectionDuration(1, 'b', 4)
74
+ reportSectionDuration(2, 'c', 6)
75
+ const durations = selectMediaDurations(egakiStore.getState())
76
+ expect(durations).toMatchInlineSnapshot(`
77
+ {
78
+ "0": 2,
79
+ "1": 4,
80
+ "2": 6,
81
+ }
82
+ `)
83
+ })
84
+
85
+ test('duplicate report with same value is a no-op', () => {
86
+ reportSectionDuration(0, 'inst-1', 5)
87
+ const before = egakiStore.getState().sectionReports
88
+ reportSectionDuration(0, 'inst-1', 5)
89
+ expect(egakiStore.getState().sectionReports).toBe(before)
90
+ })
91
+
92
+ test('clearSectionDuration removes an instance report', () => {
93
+ reportSectionDuration(0, 'a', 3)
94
+ reportSectionDuration(0, 'b', 7)
95
+ clearSectionDuration(0, 'b')
96
+ const durations = selectMediaDurations(egakiStore.getState())
97
+ expect(durations['0']).toBe(3)
98
+ })
99
+
100
+ test('clearSectionDuration removes section entry when last instance clears', () => {
101
+ reportSectionDuration(0, 'a', 3)
102
+ clearSectionDuration(0, 'a')
103
+ const durations = selectMediaDurations(egakiStore.getState())
104
+ expect(durations).toMatchInlineSnapshot(`{}`)
105
+ })
106
+
107
+ test('clearSectionDuration is a no-op for missing instance', () => {
108
+ reportSectionDuration(0, 'a', 3)
109
+ const before = egakiStore.getState().sectionReports
110
+ clearSectionDuration(0, 'nonexistent')
111
+ expect(egakiStore.getState().sectionReports).toBe(before)
112
+ })
113
+
114
+ test('resetSectionDurations clears all reports', () => {
115
+ reportSectionDuration(0, 'a', 3)
116
+ reportSectionDuration(1, 'b', 5)
117
+ resetSectionDurations()
118
+ expect(egakiStore.getState().sectionReports.size).toBe(0)
119
+ expect(selectMediaDurations(egakiStore.getState())).toMatchInlineSnapshot(`{}`)
120
+ })
121
+
122
+ test('resetSectionDurations is a no-op when already empty', () => {
123
+ const before = egakiStore.getState().sectionReports
124
+ resetSectionDurations()
125
+ expect(egakiStore.getState().sectionReports).toBe(before)
126
+ })
127
+
128
+ test('ignores invalid inputs', () => {
129
+ reportSectionDuration(-1, 'a', 5)
130
+ reportSectionDuration(0, 'a', NaN)
131
+ reportSectionDuration(0, 'a', 0)
132
+ reportSectionDuration(0, 'a', -1)
133
+ expect(egakiStore.getState().sectionReports.size).toBe(0)
134
+ })
135
+ })
136
+
137
+ // ---------------------------------------------------------------------------
138
+ // Generation status (server-driven)
139
+ // ---------------------------------------------------------------------------
140
+
141
+ describe('serverGenerationStatus', () => {
142
+ test('setState tracks server generation status', () => {
143
+ egakiStore.setState({
144
+ serverGenerationStatus: { counts: { image: 1 }, total: 1 },
145
+ })
146
+ const status = selectGenerationStatus(egakiStore.getState())
147
+ expect(status).toMatchInlineSnapshot(`
148
+ {
149
+ "counts": {
150
+ "image": 1,
151
+ },
152
+ "total": 1,
153
+ }
154
+ `)
155
+ })
156
+
157
+ test('multiple generation types counted separately', () => {
158
+ egakiStore.setState({
159
+ serverGenerationStatus: { counts: { image: 2, video: 1, audio: 1 }, total: 4 },
160
+ })
161
+ const status = selectGenerationStatus(egakiStore.getState())
162
+ expect(status).toMatchInlineSnapshot(`
163
+ {
164
+ "counts": {
165
+ "audio": 1,
166
+ "image": 2,
167
+ "video": 1,
168
+ },
169
+ "total": 4,
170
+ }
171
+ `)
172
+ })
173
+
174
+ test('setting status to null clears generation tracking', () => {
175
+ egakiStore.setState({
176
+ serverGenerationStatus: { counts: { image: 1 }, total: 1 },
177
+ })
178
+ egakiStore.setState({ serverGenerationStatus: null })
179
+ const status = selectGenerationStatus(egakiStore.getState())
180
+ expect(status).toBeNull()
181
+ })
182
+
183
+ test('returns null when no generations active', () => {
184
+ expect(selectGenerationStatus(egakiStore.getState())).toBeNull()
185
+ })
186
+
187
+ test('tracks any namespace', () => {
188
+ egakiStore.setState({
189
+ serverGenerationStatus: { counts: { audio: 1, transcription: 2 }, total: 3 },
190
+ })
191
+ const status = selectGenerationStatus(egakiStore.getState())
192
+ expect(status).toMatchInlineSnapshot(`
193
+ {
194
+ "counts": {
195
+ "audio": 1,
196
+ "transcription": 2,
197
+ },
198
+ "total": 3,
199
+ }
200
+ `)
201
+ })
202
+ })
@@ -0,0 +1,115 @@
1
+ /**
2
+ * Centralized client-side state store (zustand/vanilla).
3
+ *
4
+ * Pure vanilla store with no React imports. State slices:
5
+ *
6
+ * 1. **modules** — eagerly-imported user .tsx/.ts modules, updated via HMR
7
+ * 2. **sectionReports** — per-section effective media durations reported by
8
+ * Audio/Video components (ephemeral, not persisted)
9
+ * 3. **serverGenerationStatus** — server-reported generation progress from
10
+ * the /api/generation-progress SSE stream, for the toolbar status indicator
11
+ *
12
+ * React hooks live in store-hooks.ts ('use client') to keep this file
13
+ * importable from any environment (RSC, SSR, client) without pulling in React.
14
+ */
15
+
16
+ import { createStore } from 'zustand/vanilla'
17
+ import { subscribeWithSelector } from 'zustand/middleware'
18
+
19
+ // ---------------------------------------------------------------------------
20
+ // Types
21
+ // ---------------------------------------------------------------------------
22
+
23
+ /** Same as safe-mdx's EagerModules: Record<string, Record<string, any>> */
24
+ type EagerModules = Record<string, Record<string, any>>
25
+
26
+ export interface GenerationStatus {
27
+ /** Count of active generations per namespace (e.g. { image: 2, video: 1 }) */
28
+ counts: Record<string, number>
29
+ total: number
30
+ }
31
+
32
+ /** Individual generation entry from the server progress stream. */
33
+ export interface GenerationProgressEntry {
34
+ key: string
35
+ namespace: string
36
+ label: string
37
+ model?: string
38
+ startedAt: number
39
+ elapsedMs: number
40
+ params: Record<string, unknown>
41
+ }
42
+
43
+ /** A generation that failed, reported via the progress stream. */
44
+ export interface GenerationError {
45
+ key: string
46
+ namespace: string
47
+ label: string
48
+ model?: string
49
+ error: string
50
+ durationMs: number
51
+ }
52
+
53
+ export interface EgakiClientState {
54
+ /** User-imported modules from virtual:egaki-modules. Updated by HMR. */
55
+ modules: EagerModules
56
+
57
+ /**
58
+ * Per-instance section duration reports (ephemeral).
59
+ * sectionIndex → Map<instanceId, effectiveSeconds>
60
+ * Each Audio/Video component reports its effective playback duration.
61
+ */
62
+ sectionReports: Map<number, Map<string, number>>
63
+
64
+ /** Server-reported generation progress from /api/generation-progress SSE stream. */
65
+ serverGenerationStatus: GenerationStatus | null
66
+
67
+ /** Detailed generation entries from the server progress stream. */
68
+ serverGenerationEntries: GenerationProgressEntry[]
69
+
70
+ /** Recent generation errors from the server progress stream.
71
+ * Errors auto-clear after ERROR_DISPLAY_DURATION_MS. */
72
+ serverGenerationErrors: GenerationError[]
73
+ }
74
+
75
+ // ---------------------------------------------------------------------------
76
+ // Store
77
+ // ---------------------------------------------------------------------------
78
+
79
+ export const egakiStore = createStore<EgakiClientState>()(
80
+ subscribeWithSelector(() => ({
81
+ modules: {} as EagerModules,
82
+ sectionReports: new Map(),
83
+ serverGenerationStatus: null as GenerationStatus | null,
84
+ serverGenerationEntries: [] as GenerationProgressEntry[],
85
+ serverGenerationErrors: [] as GenerationError[],
86
+ })),
87
+ )
88
+
89
+ // ---------------------------------------------------------------------------
90
+ // Selectors — derive values from state
91
+ // ---------------------------------------------------------------------------
92
+
93
+ /**
94
+ * Derive per-section max media durations.
95
+ * Returns Record<string, number> (section-index-as-string → seconds).
96
+ */
97
+ export function selectMediaDurations(state: EgakiClientState): Record<string, number> {
98
+ const obj: Record<string, number> = {}
99
+ for (const [idx, reports] of state.sectionReports) {
100
+ if (reports.size === 0) continue
101
+ let max = 0
102
+ for (const dur of reports.values()) {
103
+ if (dur > max) max = dur
104
+ }
105
+ if (max > 0) obj[String(idx)] = max
106
+ }
107
+ return obj
108
+ }
109
+
110
+ /** Generation status from the server progress stream, or null when idle. */
111
+ export function selectGenerationStatus(state: EgakiClientState): GenerationStatus | null {
112
+ return state.serverGenerationStatus
113
+ }
114
+
115
+
@@ -0,0 +1,21 @@
1
+ @import 'tailwindcss';
2
+ @source ".";
3
+
4
+ html,
5
+ body,
6
+ #root {
7
+ height: 100%;
8
+ margin: 0;
9
+ background: #000;
10
+ color: #fafafa;
11
+ overflow: clip;
12
+ scrollbar-width: thin;
13
+ scrollbar-color: rgba(255, 255, 255, 0.15) transparent;
14
+ }
15
+
16
+ /* Tweakpane sidebar: remove rounded corners and shadow so it sits flush */
17
+ .egaki-sidebar .tp-rotv {
18
+ border-radius: 0;
19
+ box-shadow: none;
20
+ background-color: transparent;
21
+ }