egaki 0.6.0 → 0.7.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +145 -9
- package/dist/cli/cache-utils.d.ts +33 -0
- package/dist/cli/cache-utils.d.ts.map +1 -0
- package/dist/cli/cache-utils.js +157 -0
- package/dist/cli/cached-generate.d.ts +105 -0
- package/dist/cli/cached-generate.d.ts.map +1 -0
- package/dist/cli/cached-generate.js +208 -0
- package/dist/cli/cached-generate.test.d.ts +11 -0
- package/dist/cli/cached-generate.test.d.ts.map +1 -0
- package/dist/cli/cached-generate.test.js +431 -0
- package/dist/cli/cartesia-provider.d.ts +16 -0
- package/dist/cli/cartesia-provider.d.ts.map +1 -0
- package/dist/cli/cartesia-provider.js +184 -0
- package/dist/cli/chatgpt-auth.d.ts.map +1 -0
- package/dist/cli/cli.d.ts +4 -0
- package/dist/cli/cli.d.ts.map +1 -0
- package/dist/{cli.js → cli/cli.js} +626 -600
- package/dist/cli/clone-generate.d.ts +26 -0
- package/dist/cli/clone-generate.d.ts.map +1 -0
- package/dist/cli/clone-generate.js +148 -0
- package/dist/cli/credentials.d.ts.map +1 -0
- package/dist/{credentials.js → cli/credentials.js} +20 -0
- package/dist/cli/elevenlabs-speech-provider.d.ts +26 -0
- package/dist/cli/elevenlabs-speech-provider.d.ts.map +1 -0
- package/dist/cli/elevenlabs-speech-provider.js +199 -0
- package/dist/cli/generate.d.ts +140 -0
- package/dist/cli/generate.d.ts.map +1 -0
- package/dist/cli/generate.js +604 -0
- package/dist/cli/generate.test.d.ts +2 -0
- package/dist/cli/generate.test.d.ts.map +1 -0
- package/dist/cli/generate.test.js +131 -0
- package/dist/cli/login.d.ts.map +1 -0
- package/dist/cli/main.d.ts +3 -0
- package/dist/cli/main.d.ts.map +1 -0
- package/dist/cli/main.js +7 -0
- package/dist/{model-catalog.d.ts → cli/model-catalog.d.ts} +6 -0
- package/dist/cli/model-catalog.d.ts.map +1 -0
- package/dist/{model-catalog.js → cli/model-catalog.js} +714 -4
- package/dist/{models.d.ts → cli/models.d.ts} +12 -5
- package/dist/cli/models.d.ts.map +1 -0
- package/dist/{models.js → cli/models.js} +58 -57
- package/dist/cli/open-browser.d.ts.map +1 -0
- package/dist/cli/openai-speech-provider.d.ts +3 -0
- package/dist/cli/openai-speech-provider.d.ts.map +1 -0
- package/dist/cli/openai-speech-provider.js +28 -0
- package/dist/cli/separation-generate.d.ts +34 -0
- package/dist/cli/separation-generate.d.ts.map +1 -0
- package/dist/cli/separation-generate.js +117 -0
- package/dist/cli/speech-catalog.d.ts +35 -0
- package/dist/cli/speech-catalog.d.ts.map +1 -0
- package/dist/cli/speech-catalog.js +195 -0
- package/dist/cli/speech-generate.d.ts +86 -0
- package/dist/cli/speech-generate.d.ts.map +1 -0
- package/dist/cli/speech-generate.js +156 -0
- package/dist/cli/speech-models.d.ts +8 -0
- package/dist/cli/speech-models.d.ts.map +1 -0
- package/dist/cli/speech-models.js +109 -0
- package/dist/cli/speech-provider.test.d.ts +2 -0
- package/dist/cli/speech-provider.test.d.ts.map +1 -0
- package/dist/cli/speech-provider.test.js +197 -0
- package/dist/cli/subscription.d.ts.map +1 -0
- package/dist/cli/transcription-catalog.d.ts +35 -0
- package/dist/cli/transcription-catalog.d.ts.map +1 -0
- package/dist/cli/transcription-catalog.js +192 -0
- package/dist/cli/transcription-generate.d.ts +62 -0
- package/dist/cli/transcription-generate.d.ts.map +1 -0
- package/dist/cli/transcription-generate.js +144 -0
- package/dist/cli/transcription-models.d.ts +9 -0
- package/dist/cli/transcription-models.d.ts.map +1 -0
- package/dist/cli/transcription-models.js +100 -0
- package/dist/cli/transcription-providers.d.ts +22 -0
- package/dist/cli/transcription-providers.d.ts.map +1 -0
- package/dist/cli/transcription-providers.js +290 -0
- package/dist/cli/video-model-catalog.d.ts.map +1 -0
- package/dist/cli/xai-auth.d.ts.map +1 -0
- package/dist/{xai-auth.js → cli/xai-auth.js} +1 -1
- package/dist/vite/angled-screen.d.ts +57 -0
- package/dist/vite/angled-screen.d.ts.map +1 -0
- package/dist/vite/angled-screen.js +115 -0
- package/dist/vite/app.d.ts +51 -0
- package/dist/vite/app.d.ts.map +1 -0
- package/dist/vite/app.js +150 -0
- package/dist/vite/bands-shader.d.ts +47 -0
- package/dist/vite/bands-shader.d.ts.map +1 -0
- package/dist/vite/bands-shader.js +267 -0
- package/dist/vite/code-block.d.ts +107 -0
- package/dist/vite/code-block.d.ts.map +1 -0
- package/dist/vite/code-block.js +641 -0
- package/dist/vite/components.d.ts +36 -0
- package/dist/vite/components.d.ts.map +1 -0
- package/dist/vite/components.js +113 -0
- package/dist/vite/dispersion-rings-shader.d.ts +44 -0
- package/dist/vite/dispersion-rings-shader.d.ts.map +1 -0
- package/dist/vite/dispersion-rings-shader.js +282 -0
- package/dist/vite/easing-curves.d.ts +129 -0
- package/dist/vite/easing-curves.d.ts.map +1 -0
- package/dist/vite/easing-curves.js +584 -0
- package/dist/vite/easing-curves.test.d.ts +2 -0
- package/dist/vite/easing-curves.test.d.ts.map +1 -0
- package/dist/vite/easing-curves.test.js +200 -0
- package/dist/vite/generated-media-client.d.ts +35 -0
- package/dist/vite/generated-media-client.d.ts.map +1 -0
- package/dist/vite/generated-media-client.js +76 -0
- package/dist/vite/keyframes.d.ts +129 -0
- package/dist/vite/keyframes.d.ts.map +1 -0
- package/dist/vite/keyframes.js +194 -0
- package/dist/vite/layout-editor.d.ts +45 -0
- package/dist/vite/layout-editor.d.ts.map +1 -0
- package/dist/vite/layout-editor.js +587 -0
- package/dist/vite/layout-transition.d.ts +136 -0
- package/dist/vite/layout-transition.d.ts.map +1 -0
- package/dist/vite/layout-transition.js +581 -0
- package/dist/vite/liquid-gradient-shader.d.ts +62 -0
- package/dist/vite/liquid-gradient-shader.d.ts.map +1 -0
- package/dist/vite/liquid-gradient-shader.js +413 -0
- package/dist/vite/mdx-client.d.ts +15 -0
- package/dist/vite/mdx-client.d.ts.map +1 -0
- package/dist/vite/mdx-client.js +265 -0
- package/dist/vite/mdx-parse.d.ts +102 -0
- package/dist/vite/mdx-parse.d.ts.map +1 -0
- package/dist/vite/mdx-parse.js +251 -0
- package/dist/vite/mdx-provided-components.d.ts +18 -0
- package/dist/vite/mdx-provided-components.d.ts.map +1 -0
- package/dist/vite/mdx-provided-components.js +14 -0
- package/dist/vite/mdx-video.d.ts +327 -0
- package/dist/vite/mdx-video.d.ts.map +1 -0
- package/dist/vite/mdx-video.js +596 -0
- package/dist/vite/mdx-video.test.d.ts +9 -0
- package/dist/vite/mdx-video.test.d.ts.map +1 -0
- package/dist/vite/mdx-video.test.js +1945 -0
- package/dist/vite/media-components.d.ts +45 -0
- package/dist/vite/media-components.d.ts.map +1 -0
- package/dist/vite/media-components.js +355 -0
- package/dist/vite/media-duration-store.d.ts +66 -0
- package/dist/vite/media-duration-store.d.ts.map +1 -0
- package/dist/vite/media-duration-store.js +204 -0
- package/dist/vite/player-page.d.ts +35 -0
- package/dist/vite/player-page.d.ts.map +1 -0
- package/dist/vite/player-page.js +720 -0
- package/dist/vite/render-client.d.ts +18 -0
- package/dist/vite/render-client.d.ts.map +1 -0
- package/dist/vite/render-client.js +67 -0
- package/dist/vite/sdk.d.ts +221 -0
- package/dist/vite/sdk.d.ts.map +1 -0
- package/dist/vite/sdk.js +355 -0
- package/dist/vite/server-components.d.ts +55 -0
- package/dist/vite/server-components.d.ts.map +1 -0
- package/dist/vite/server-components.js +156 -0
- package/dist/vite/server-mdx.d.ts +74 -0
- package/dist/vite/server-mdx.d.ts.map +1 -0
- package/dist/vite/server-mdx.js +259 -0
- package/dist/vite/shader-renderer.d.ts +48 -0
- package/dist/vite/shader-renderer.d.ts.map +1 -0
- package/dist/vite/shader-renderer.js +411 -0
- package/dist/vite/store-hooks.d.ts +12 -0
- package/dist/vite/store-hooks.d.ts.map +1 -0
- package/dist/vite/store-hooks.js +93 -0
- package/dist/vite/store.d.ts +75 -0
- package/dist/vite/store.d.ts.map +1 -0
- package/dist/vite/store.js +52 -0
- package/dist/vite/store.test.d.ts +6 -0
- package/dist/vite/store.test.d.ts.map +1 -0
- package/dist/vite/store.test.js +172 -0
- package/dist/vite/tweakpane-hook.d.ts +94 -0
- package/dist/vite/tweakpane-hook.d.ts.map +1 -0
- package/dist/vite/tweakpane-hook.js +450 -0
- package/dist/vite/vite-plugin.d.ts +28 -0
- package/dist/vite/vite-plugin.d.ts.map +1 -0
- package/dist/vite/vite-plugin.js +539 -0
- package/dist/vite/wave-gradient-shader.d.ts +41 -0
- package/dist/vite/wave-gradient-shader.d.ts.map +1 -0
- package/dist/vite/wave-gradient-shader.js +207 -0
- package/package.json +101 -24
- package/skills/egaki/SKILL.md +162 -1
- package/src/cli/cache-utils.ts +169 -0
- package/src/cli/cached-generate.test.ts +531 -0
- package/src/cli/cached-generate.ts +348 -0
- package/src/cli/cartesia-provider.ts +249 -0
- package/src/{cli.ts → cli/cli.ts} +836 -884
- package/src/cli/clone-generate.ts +202 -0
- package/src/{credentials.ts → cli/credentials.ts} +20 -0
- package/src/cli/elevenlabs-speech-provider.ts +243 -0
- package/src/cli/generate.test.ts +166 -0
- package/src/cli/generate.ts +912 -0
- package/src/cli/main.ts +8 -0
- package/src/{model-catalog.ts → cli/model-catalog.ts} +787 -58
- package/src/{models.ts → cli/models.ts} +65 -86
- package/src/cli/openai-speech-provider.ts +34 -0
- package/src/cli/separation-generate.ts +187 -0
- package/src/cli/speech-catalog.ts +253 -0
- package/src/cli/speech-generate.ts +275 -0
- package/src/cli/speech-models.ts +146 -0
- package/src/cli/speech-provider.test.ts +226 -0
- package/src/cli/transcription-catalog.ts +250 -0
- package/src/cli/transcription-generate.ts +214 -0
- package/src/cli/transcription-models.ts +136 -0
- package/src/cli/transcription-providers.ts +427 -0
- package/src/{xai-auth.ts → cli/xai-auth.ts} +1 -1
- package/src/vite/angled-screen.tsx +214 -0
- package/src/vite/app.tsx +197 -0
- package/src/vite/bands-shader.tsx +319 -0
- package/src/vite/code-block.tsx +939 -0
- package/src/vite/components.tsx +278 -0
- package/src/vite/dispersion-rings-shader.tsx +331 -0
- package/src/vite/easing-curves.test.ts +232 -0
- package/src/vite/easing-curves.ts +743 -0
- package/src/vite/generated-media-client.tsx +121 -0
- package/src/vite/keyframes.tsx +347 -0
- package/src/vite/layout-editor.tsx +709 -0
- package/src/vite/layout-transition.tsx +706 -0
- package/src/vite/liquid-gradient-shader.tsx +474 -0
- package/src/vite/mdx-client.tsx +420 -0
- package/src/vite/mdx-parse.ts +343 -0
- package/src/vite/mdx-provided-components.ts +19 -0
- package/src/vite/mdx-video.test.tsx +2219 -0
- package/src/vite/mdx-video.tsx +976 -0
- package/src/vite/media-components.tsx +473 -0
- package/src/vite/media-duration-store.ts +225 -0
- package/src/vite/motion-timing.ts +194 -0
- package/src/vite/player-page.tsx +1107 -0
- package/src/vite/render-client.ts +84 -0
- package/src/vite/sdk.ts +598 -0
- package/src/vite/server-components.tsx +241 -0
- package/src/vite/server-mdx.ts +285 -0
- package/src/vite/shader-renderer.tsx +535 -0
- package/src/vite/store-hooks.ts +117 -0
- package/src/vite/store.test.ts +202 -0
- package/src/vite/store.ts +115 -0
- package/src/vite/styles.css +21 -0
- package/src/vite/tweakpane-hook.tsx +593 -0
- package/src/vite/virtual-modules.d.ts +30 -0
- package/src/vite/vite-plugin.ts +585 -0
- package/src/vite/wave-gradient-shader.tsx +253 -0
- package/README.md +0 -370
- package/dist/antigravity-auth.d.ts +0 -15
- package/dist/antigravity-auth.d.ts.map +0 -1
- package/dist/antigravity-auth.js +0 -544
- package/dist/antigravity-image.d.ts +0 -11
- package/dist/antigravity-image.d.ts.map +0 -1
- package/dist/antigravity-image.js +0 -285
- package/dist/chatgpt-auth.d.ts.map +0 -1
- package/dist/cli.d.ts +0 -3
- package/dist/cli.d.ts.map +0 -1
- package/dist/credentials.d.ts.map +0 -1
- package/dist/gateway-model-candidates.generated.d.ts +0 -11
- package/dist/gateway-model-candidates.generated.d.ts.map +0 -1
- package/dist/gateway-model-candidates.generated.js +0 -64
- package/dist/login.d.ts.map +0 -1
- package/dist/model-catalog.d.ts.map +0 -1
- package/dist/models.d.ts.map +0 -1
- package/dist/open-browser.d.ts.map +0 -1
- package/dist/subscription.d.ts.map +0 -1
- package/dist/video-model-catalog.d.ts.map +0 -1
- package/dist/xai-auth.d.ts.map +0 -1
- /package/dist/{chatgpt-auth.d.ts → cli/chatgpt-auth.d.ts} +0 -0
- /package/dist/{chatgpt-auth.js → cli/chatgpt-auth.js} +0 -0
- /package/dist/{credentials.d.ts → cli/credentials.d.ts} +0 -0
- /package/dist/{login.d.ts → cli/login.d.ts} +0 -0
- /package/dist/{login.js → cli/login.js} +0 -0
- /package/dist/{open-browser.d.ts → cli/open-browser.d.ts} +0 -0
- /package/dist/{open-browser.js → cli/open-browser.js} +0 -0
- /package/dist/{subscription.d.ts → cli/subscription.d.ts} +0 -0
- /package/dist/{subscription.js → cli/subscription.js} +0 -0
- /package/dist/{video-model-catalog.d.ts → cli/video-model-catalog.d.ts} +0 -0
- /package/dist/{video-model-catalog.js → cli/video-model-catalog.js} +0 -0
- /package/dist/{xai-auth.d.ts → cli/xai-auth.d.ts} +0 -0
- /package/src/{chatgpt-auth.ts → cli/chatgpt-auth.ts} +0 -0
- /package/src/{login.ts → cli/login.ts} +0 -0
- /package/src/{open-browser.ts → cli/open-browser.ts} +0 -0
- /package/src/{subscription.ts → cli/subscription.ts} +0 -0
- /package/src/{video-model-catalog.ts → cli/video-model-catalog.ts} +0 -0
|
@@ -0,0 +1,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
|
+
}
|