@crievo/omni 0.0.0-stage → 0.1.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 (709) hide show
  1. package/.env.example +41 -0
  2. package/README.md +2 -2
  3. package/bin/omni.mjs +14 -0
  4. package/docs/onboarding/clone-voice.png +0 -0
  5. package/docs/onboarding/getting-started.png +0 -0
  6. package/docs/onboarding/new-video.png +0 -0
  7. package/docs/onboarding/providers.png +0 -0
  8. package/docs/onboarding/publish.png +0 -0
  9. package/docs/onboarding/references.png +0 -0
  10. package/docs/onboarding.md +212 -0
  11. package/omnivideo.config.example.json +437 -0
  12. package/package.json +40 -4
  13. package/reference/captions.md +156 -0
  14. package/reference/carousel-contract.md +91 -0
  15. package/reference/cloud.md +96 -0
  16. package/reference/learnings.md +792 -0
  17. package/reference/omnichat.md +305 -0
  18. package/reference/premiere-mcp.md +91 -0
  19. package/reference/publish.md +305 -0
  20. package/reference/scenes-contract.md +109 -0
  21. package/reference/studio.md +666 -0
  22. package/reference/voicestudio.md +81 -0
  23. package/reference/writing-for-voice.md +67 -0
  24. package/requirements.txt +3 -0
  25. package/studio/api/app.ts +87 -0
  26. package/studio/api/lib/errors.ts +33 -0
  27. package/studio/api/lib/files.ts +41 -0
  28. package/studio/api/lib/hono.ts +9 -0
  29. package/studio/api/lib/result.ts +14 -0
  30. package/studio/api/lib/validate.ts +31 -0
  31. package/studio/api/lib/video.ts +10 -0
  32. package/studio/api/middleware/json-charset.ts +11 -0
  33. package/studio/api/middleware/local-only.ts +32 -0
  34. package/studio/api/middleware/no-timeout.ts +10 -0
  35. package/studio/api/routes/admin.ts +18 -0
  36. package/studio/api/routes/agent-cascade.ts +100 -0
  37. package/studio/api/routes/agent-history.ts +23 -0
  38. package/studio/api/routes/agent.ts +162 -0
  39. package/studio/api/routes/analytics.ts +31 -0
  40. package/studio/api/routes/approval.ts +60 -0
  41. package/studio/api/routes/archive.ts +12 -0
  42. package/studio/api/routes/captions.ts +28 -0
  43. package/studio/api/routes/carousel.ts +38 -0
  44. package/studio/api/routes/clone.ts +27 -0
  45. package/studio/api/routes/create.ts +19 -0
  46. package/studio/api/routes/events.ts +11 -0
  47. package/studio/api/routes/export.ts +14 -0
  48. package/studio/api/routes/media.ts +30 -0
  49. package/studio/api/routes/omnichat.ts +113 -0
  50. package/studio/api/routes/onboarding.ts +14 -0
  51. package/studio/api/routes/publish.ts +40 -0
  52. package/studio/api/routes/queue.ts +14 -0
  53. package/studio/api/routes/references.ts +40 -0
  54. package/studio/api/routes/runner.ts +17 -0
  55. package/studio/api/routes/schedule.ts +47 -0
  56. package/studio/api/routes/services.ts +11 -0
  57. package/studio/api/routes/sessions.ts +27 -0
  58. package/studio/api/routes/settings.ts +102 -0
  59. package/studio/api/routes/sound.ts +45 -0
  60. package/studio/api/routes/thumbnail.ts +18 -0
  61. package/studio/api/routes/usage.ts +13 -0
  62. package/studio/api/routes/videos.ts +41 -0
  63. package/studio/api/routes/voices.ts +71 -0
  64. package/studio/api/services/admin.ts +113 -0
  65. package/studio/api/services/agent-cascade.ts +975 -0
  66. package/studio/api/services/agent-claude.ts +47 -0
  67. package/studio/api/services/agent-history.ts +103 -0
  68. package/studio/api/services/agent-persona.ts +77 -0
  69. package/studio/api/services/agent-tools.ts +2237 -0
  70. package/studio/api/services/agent-ui-tools.ts +259 -0
  71. package/studio/api/services/agent-wait.ts +32 -0
  72. package/studio/api/services/agent.ts +553 -0
  73. package/studio/api/services/analytics.ts +92 -0
  74. package/studio/api/services/approval.ts +134 -0
  75. package/studio/api/services/archive.ts +38 -0
  76. package/studio/api/services/captions.ts +268 -0
  77. package/studio/api/services/carousel.ts +206 -0
  78. package/studio/api/services/classify.ts +196 -0
  79. package/studio/api/services/clone-phrases.ts +75 -0
  80. package/studio/api/services/clone.ts +722 -0
  81. package/studio/api/services/create.ts +421 -0
  82. package/studio/api/services/events.ts +215 -0
  83. package/studio/api/services/media.ts +32 -0
  84. package/studio/api/services/omnichat.ts +624 -0
  85. package/studio/api/services/onboarding.ts +77 -0
  86. package/studio/api/services/publish.ts +287 -0
  87. package/studio/api/services/queue.ts +150 -0
  88. package/studio/api/services/references.ts +269 -0
  89. package/studio/api/services/root.ts +60 -0
  90. package/studio/api/services/services.ts +114 -0
  91. package/studio/api/services/sessions.ts +1007 -0
  92. package/studio/api/services/settings.ts +983 -0
  93. package/studio/api/services/sound.ts +311 -0
  94. package/studio/api/services/thumbnail.ts +133 -0
  95. package/studio/api/services/usage.ts +27 -0
  96. package/studio/api/services/videos.ts +418 -0
  97. package/studio/api/services/voice-pause.ts +98 -0
  98. package/studio/api/services/voices.ts +324 -0
  99. package/studio/runner/runner.ts +244 -0
  100. package/studio/shared/omni-actions.ts +68 -0
  101. package/studio/shared/omni-admin.ts +52 -0
  102. package/studio/shared/omni-farewell.ts +48 -0
  103. package/studio/shared/omni-hotword.ts +20 -0
  104. package/studio/shared/omni-screens.ts +228 -0
  105. package/studio/shared/schemas/admin.ts +15 -0
  106. package/studio/shared/schemas/agent-cascade.ts +58 -0
  107. package/studio/shared/schemas/agent-history.ts +73 -0
  108. package/studio/shared/schemas/agent.ts +50 -0
  109. package/studio/shared/schemas/analytics.ts +6 -0
  110. package/studio/shared/schemas/approval.ts +22 -0
  111. package/studio/shared/schemas/captions.ts +19 -0
  112. package/studio/shared/schemas/carousel.ts +26 -0
  113. package/studio/shared/schemas/clone.ts +40 -0
  114. package/studio/shared/schemas/common.ts +16 -0
  115. package/studio/shared/schemas/omnichat.ts +54 -0
  116. package/studio/shared/schemas/onboarding.ts +5 -0
  117. package/studio/shared/schemas/publish.ts +45 -0
  118. package/studio/shared/schemas/references.ts +20 -0
  119. package/studio/shared/schemas/settings.ts +82 -0
  120. package/studio/shared/schemas/sound.ts +22 -0
  121. package/studio/shared/schemas/videos.ts +38 -0
  122. package/studio/shared/schemas/voices.ts +23 -0
  123. package/studio/shared/types/api.ts +1290 -0
  124. package/studio/studio.ts +148 -0
  125. package/studio/tsconfig.json +15 -0
  126. package/studio/web/assets/logos/cartesia.svg +4 -0
  127. package/studio/web/assets/logos/elevenlabs.svg +12 -0
  128. package/studio/web/assets/logos/fish.svg +24 -0
  129. package/studio/web/assets/logos/omni-black.png +0 -0
  130. package/studio/web/assets/logos/omni-white.png +0 -0
  131. package/studio/web/assets/logos/voicestudio.svg +21 -0
  132. package/studio/web/components/AgentMark.tsx +7 -0
  133. package/studio/web/components/AgentWalking.tsx +11 -0
  134. package/studio/web/components/ApiKeyField.tsx +104 -0
  135. package/studio/web/components/AppShell.tsx +64 -0
  136. package/studio/web/components/ApprovalNotice.tsx +65 -0
  137. package/studio/web/components/AvatarGroupIcon.tsx +22 -0
  138. package/studio/web/components/AvatarNameField.tsx +86 -0
  139. package/studio/web/components/AvatarPicker.tsx +84 -0
  140. package/studio/web/components/BrandLogo.tsx +16 -0
  141. package/studio/web/components/CartesiaPlanSetting.tsx +90 -0
  142. package/studio/web/components/ClaudeWalking.tsx +32 -0
  143. package/studio/web/components/CloneVoiceDialog/GuidedScript.tsx +39 -0
  144. package/studio/web/components/CloneVoiceDialog/LanguageBar.tsx +37 -0
  145. package/studio/web/components/CloneVoiceDialog/LevelMeter.tsx +31 -0
  146. package/studio/web/components/CloneVoiceDialog/ProviderChoice.tsx +81 -0
  147. package/studio/web/components/CloneVoiceDialog/QualityPanel.tsx +62 -0
  148. package/studio/web/components/CloneVoiceDialog/RecordStep.tsx +50 -0
  149. package/studio/web/components/CloneVoiceDialog/RecorderBar.tsx +79 -0
  150. package/studio/web/components/CloneVoiceDialog/RedoSummary.tsx +54 -0
  151. package/studio/web/components/CloneVoiceDialog/SaveStep.tsx +91 -0
  152. package/studio/web/components/CloneVoiceDialog/SourceBar.tsx +30 -0
  153. package/studio/web/components/CloneVoiceDialog/index.tsx +141 -0
  154. package/studio/web/components/CloneVoiceDialog/types.ts +28 -0
  155. package/studio/web/components/CloneVoiceDialog/useCloneVoice.ts +528 -0
  156. package/studio/web/components/ConnectionSignals.tsx +51 -0
  157. package/studio/web/components/EditVoiceDialog/EditVoiceFooter.tsx +72 -0
  158. package/studio/web/components/EditVoiceDialog/ProviderCards.tsx +81 -0
  159. package/studio/web/components/EditVoiceDialog/index.tsx +256 -0
  160. package/studio/web/components/ErrorBanner.tsx +115 -0
  161. package/studio/web/components/FinishedTask.tsx +41 -0
  162. package/studio/web/components/FishModelSetting.tsx +69 -0
  163. package/studio/web/components/FishPriceSetting.tsx +44 -0
  164. package/studio/web/components/HumanBanner.tsx +64 -0
  165. package/studio/web/components/Lightbox.tsx +41 -0
  166. package/studio/web/components/LogStream.tsx +42 -0
  167. package/studio/web/components/Markdown.tsx +21 -0
  168. package/studio/web/components/MissingDmsPanel/BatchSend.tsx +137 -0
  169. package/studio/web/components/MissingDmsPanel/CommentCard.tsx +168 -0
  170. package/studio/web/components/MissingDmsPanel/CommentFeed.tsx +45 -0
  171. package/studio/web/components/MissingDmsPanel/DmBubble.tsx +38 -0
  172. package/studio/web/components/MissingDmsPanel/ReplyComposer.tsx +63 -0
  173. package/studio/web/components/MissingDmsPanel/index.tsx +98 -0
  174. package/studio/web/components/NotFound.tsx +48 -0
  175. package/studio/web/components/Omni/OmniAvatarPicker.tsx +92 -0
  176. package/studio/web/components/Omni/OmniCards.tsx +202 -0
  177. package/studio/web/components/Omni/OmniGlow.tsx +183 -0
  178. package/studio/web/components/Omni/OmniHistory.tsx +119 -0
  179. package/studio/web/components/Omni/OmniHotword.tsx +40 -0
  180. package/studio/web/components/Omni/OmniMicSelect.tsx +92 -0
  181. package/studio/web/components/Omni/OmniVoiceSample.tsx +81 -0
  182. package/studio/web/components/Omni/OmniWidget.tsx +743 -0
  183. package/studio/web/components/Omni/PendingActions.tsx +200 -0
  184. package/studio/web/components/Omni/Transcript.tsx +127 -0
  185. package/studio/web/components/OmnichatAccountStatus.tsx +87 -0
  186. package/studio/web/components/OmnichatRulePanel/RuleConfig.tsx +150 -0
  187. package/studio/web/components/OmnichatRulePanel/RuleDryRun.tsx +15 -0
  188. package/studio/web/components/OmnichatRulePanel/RuleEnableConfirm.tsx +42 -0
  189. package/studio/web/components/OmnichatRulePanel/RuleSkeleton.tsx +18 -0
  190. package/studio/web/components/OmnichatRulePanel/RuleStatus.tsx +55 -0
  191. package/studio/web/components/OmnichatRulePanel/index.tsx +115 -0
  192. package/studio/web/components/OmnichatRulePanel/rule-steps.ts +67 -0
  193. package/studio/web/components/OmnichatRulePanel/steps/MessageStep.tsx +91 -0
  194. package/studio/web/components/OmnichatRulePanel/steps/RepliesStep.tsx +201 -0
  195. package/studio/web/components/OmnichatRulePanel/steps/TriggerStep.tsx +76 -0
  196. package/studio/web/components/OmnichatRulePanel/steps/types.ts +11 -0
  197. package/studio/web/components/Palette.tsx +17 -0
  198. package/studio/web/components/PlatformCard.tsx +109 -0
  199. package/studio/web/components/PlatformIcon.tsx +19 -0
  200. package/studio/web/components/ProviderCard.tsx +80 -0
  201. package/studio/web/components/ProviderDetail.tsx +367 -0
  202. package/studio/web/components/ProviderLogo.tsx +20 -0
  203. package/studio/web/components/QueuePanel.tsx +77 -0
  204. package/studio/web/components/ReferenceActions.tsx +89 -0
  205. package/studio/web/components/ReferencePicker.tsx +136 -0
  206. package/studio/web/components/ResultMessage.tsx +7 -0
  207. package/studio/web/components/RuleForm.tsx +138 -0
  208. package/studio/web/components/RuleLog.tsx +151 -0
  209. package/studio/web/components/RunningTask.tsx +23 -0
  210. package/studio/web/components/SampleList.tsx +55 -0
  211. package/studio/web/components/SessionPanel.tsx +352 -0
  212. package/studio/web/components/SettingRow.tsx +14 -0
  213. package/studio/web/components/SettingsCategorySkeleton.tsx +25 -0
  214. package/studio/web/components/SidePanel.tsx +77 -0
  215. package/studio/web/components/Sidebar.tsx +289 -0
  216. package/studio/web/components/StageStepper.tsx +87 -0
  217. package/studio/web/components/StudioOffline.tsx +80 -0
  218. package/studio/web/components/TaskName.tsx +17 -0
  219. package/studio/web/components/TopBanners.tsx +30 -0
  220. package/studio/web/components/VideoOmnichatPanel.tsx +287 -0
  221. package/studio/web/components/VoiceAvatar.tsx +33 -0
  222. package/studio/web/components/VoiceCarousel.tsx +196 -0
  223. package/studio/web/components/VoiceCategory.tsx +9 -0
  224. package/studio/web/components/VoiceModal.tsx +39 -0
  225. package/studio/web/components/VoiceProfileCard.tsx +75 -0
  226. package/studio/web/components/VoiceProfilePicker.tsx +107 -0
  227. package/studio/web/components/VoiceProfilesSettings.tsx +150 -0
  228. package/studio/web/components/VoiceProviderCard.tsx +118 -0
  229. package/studio/web/components/VoiceProvidersCard.tsx +26 -0
  230. package/studio/web/components/WaitList.tsx +21 -0
  231. package/studio/web/components/icons.tsx +247 -0
  232. package/studio/web/components/ui/accordion.tsx +47 -0
  233. package/studio/web/components/ui/badge.tsx +18 -0
  234. package/studio/web/components/ui/calendar.tsx +43 -0
  235. package/studio/web/components/ui/chart.tsx +229 -0
  236. package/studio/web/components/ui/checkbox.tsx +26 -0
  237. package/studio/web/components/ui/cx.ts +2 -0
  238. package/studio/web/components/ui/date-time-picker.tsx +84 -0
  239. package/studio/web/components/ui/panel.tsx +23 -0
  240. package/studio/web/components/ui/picker.tsx +58 -0
  241. package/studio/web/components/ui/popover.tsx +24 -0
  242. package/studio/web/components/ui/select.tsx +89 -0
  243. package/studio/web/components/ui/skeleton.tsx +10 -0
  244. package/studio/web/components/ui/slider.tsx +68 -0
  245. package/studio/web/components/ui/switch.tsx +13 -0
  246. package/studio/web/components/ui/tooltip.test.tsx +30 -0
  247. package/studio/web/components/ui/tooltip.tsx +59 -0
  248. package/studio/web/hooks/useAccounts.ts +27 -0
  249. package/studio/web/hooks/useAdmin.ts +12 -0
  250. package/studio/web/hooks/useAnalytics.ts +21 -0
  251. package/studio/web/hooks/useAvatarAudioReactive.ts +87 -0
  252. package/studio/web/hooks/useBrowserConnect.ts +48 -0
  253. package/studio/web/hooks/useClonePlan.ts +14 -0
  254. package/studio/web/hooks/useConnected.ts +7 -0
  255. package/studio/web/hooks/useLiveQuery.ts +29 -0
  256. package/studio/web/hooks/useMenuCollapsed.ts +32 -0
  257. package/studio/web/hooks/useMicDevices.ts +53 -0
  258. package/studio/web/hooks/useMissingDms.ts +211 -0
  259. package/studio/web/hooks/useMissingSetupSteps.ts +26 -0
  260. package/studio/web/hooks/useNow.ts +11 -0
  261. package/studio/web/hooks/useOmniAgent.ts +278 -0
  262. package/studio/web/hooks/useOmniClaude.ts +124 -0
  263. package/studio/web/hooks/useOmniDock.ts +142 -0
  264. package/studio/web/hooks/useOmniGreeting.ts +60 -0
  265. package/studio/web/hooks/useOmniHotword.ts +128 -0
  266. package/studio/web/hooks/useOmnichat.ts +26 -0
  267. package/studio/web/hooks/useOnboarding.ts +27 -0
  268. package/studio/web/hooks/usePost.ts +23 -0
  269. package/studio/web/hooks/useQueue.ts +10 -0
  270. package/studio/web/hooks/useReferences.ts +20 -0
  271. package/studio/web/hooks/useRuleEditor.ts +101 -0
  272. package/studio/web/hooks/useRunner.ts +24 -0
  273. package/studio/web/hooks/useSaveAction.ts +23 -0
  274. package/studio/web/hooks/useSchedule.ts +11 -0
  275. package/studio/web/hooks/useServices.ts +10 -0
  276. package/studio/web/hooks/useSessions.ts +15 -0
  277. package/studio/web/hooks/useSettings.ts +11 -0
  278. package/studio/web/hooks/useSettingsSection.ts +30 -0
  279. package/studio/web/hooks/useSidePanel.ts +23 -0
  280. package/studio/web/hooks/useSound.ts +30 -0
  281. package/studio/web/hooks/useUsage.ts +11 -0
  282. package/studio/web/hooks/useVideoVoice.ts +36 -0
  283. package/studio/web/hooks/useVideos.ts +21 -0
  284. package/studio/web/hooks/useVoiceProfilePreview.ts +52 -0
  285. package/studio/web/hooks/useVoiceProfiles.ts +7 -0
  286. package/studio/web/hooks/useVoiceRunning.ts +10 -0
  287. package/studio/web/hooks/useVoiceStudioClosed.ts +8 -0
  288. package/studio/web/index.html +19 -0
  289. package/studio/web/lib/agent.ts +7 -0
  290. package/studio/web/lib/alerts.ts +210 -0
  291. package/studio/web/lib/analytics-metrics.ts +351 -0
  292. package/studio/web/lib/api.ts +70 -0
  293. package/studio/web/lib/audio.ts +39 -0
  294. package/studio/web/lib/avatars.ts +41 -0
  295. package/studio/web/lib/calendar.ts +218 -0
  296. package/studio/web/lib/caption-position.ts +249 -0
  297. package/studio/web/lib/carousel.ts +24 -0
  298. package/studio/web/lib/chime.ts +42 -0
  299. package/studio/web/lib/clone-voice.ts +55 -0
  300. package/studio/web/lib/export.ts +11 -0
  301. package/studio/web/lib/files.ts +8 -0
  302. package/studio/web/lib/format.ts +93 -0
  303. package/studio/web/lib/media.ts +29 -0
  304. package/studio/web/lib/mic-device.ts +72 -0
  305. package/studio/web/lib/missing-dms.ts +70 -0
  306. package/studio/web/lib/omni-cards.ts +42 -0
  307. package/studio/web/lib/omni-cascade.ts +1067 -0
  308. package/studio/web/lib/omni-greeting-audio.ts +24 -0
  309. package/studio/web/lib/omni-hotword.ts +231 -0
  310. package/studio/web/lib/omni-panel.ts +67 -0
  311. package/studio/web/lib/omni-realtime.ts +811 -0
  312. package/studio/web/lib/omni-ui.ts +402 -0
  313. package/studio/web/lib/omni-wait.ts +174 -0
  314. package/studio/web/lib/omnichat-api.ts +44 -0
  315. package/studio/web/lib/orb.ts +270 -0
  316. package/studio/web/lib/pcm-recorder.ts +115 -0
  317. package/studio/web/lib/platforms.ts +17 -0
  318. package/studio/web/lib/providers.ts +27 -0
  319. package/studio/web/lib/publish-draft.ts +52 -0
  320. package/studio/web/lib/publish-settings.ts +81 -0
  321. package/studio/web/lib/query-client.ts +122 -0
  322. package/studio/web/lib/references.ts +13 -0
  323. package/studio/web/lib/services.ts +40 -0
  324. package/studio/web/lib/sessions.ts +103 -0
  325. package/studio/web/lib/settings-categories.ts +58 -0
  326. package/studio/web/lib/sound-audio.ts +33 -0
  327. package/studio/web/lib/sound-timeline.ts +291 -0
  328. package/studio/web/lib/sse.ts +86 -0
  329. package/studio/web/lib/stage-history.ts +141 -0
  330. package/studio/web/lib/stages.ts +344 -0
  331. package/studio/web/lib/studio-offline.ts +60 -0
  332. package/studio/web/lib/upload.ts +24 -0
  333. package/studio/web/lib/video-tabs.ts +118 -0
  334. package/studio/web/lib/voice-profile-draft.ts +34 -0
  335. package/studio/web/lib/voice-profiles.ts +3 -0
  336. package/studio/web/main.tsx +14 -0
  337. package/studio/web/pages/AdminPage/index.tsx +123 -0
  338. package/studio/web/pages/AgentPage/components/Measurements.tsx +102 -0
  339. package/studio/web/pages/AgentPage/index.tsx +28 -0
  340. package/studio/web/pages/AnalyticsPage/components/About.tsx +52 -0
  341. package/studio/web/pages/AnalyticsPage/components/ChannelCards.tsx +106 -0
  342. package/studio/web/pages/AnalyticsPage/components/ChannelView.tsx +137 -0
  343. package/studio/web/pages/AnalyticsPage/components/CollectionStatus.tsx +46 -0
  344. package/studio/web/pages/AnalyticsPage/components/Comparison.tsx +68 -0
  345. package/studio/web/pages/AnalyticsPage/components/Diagnosis.tsx +50 -0
  346. package/studio/web/pages/AnalyticsPage/components/Highlights.tsx +49 -0
  347. package/studio/web/pages/AnalyticsPage/components/Hint.tsx +11 -0
  348. package/studio/web/pages/AnalyticsPage/components/HookVsBody.tsx +100 -0
  349. package/studio/web/pages/AnalyticsPage/components/HourlyWave.tsx +133 -0
  350. package/studio/web/pages/AnalyticsPage/components/Insights.tsx +70 -0
  351. package/studio/web/pages/AnalyticsPage/components/MetricCard.tsx +46 -0
  352. package/studio/web/pages/AnalyticsPage/components/PlanSection.tsx +40 -0
  353. package/studio/web/pages/AnalyticsPage/components/ProgressionChart.tsx +155 -0
  354. package/studio/web/pages/AnalyticsPage/components/ReelBarsChart.tsx +154 -0
  355. package/studio/web/pages/AnalyticsPage/components/ReelThumb.tsx +19 -0
  356. package/studio/web/pages/AnalyticsPage/components/ReelView.tsx +290 -0
  357. package/studio/web/pages/AnalyticsPage/components/ReelsTable.tsx +269 -0
  358. package/studio/web/pages/AnalyticsPage/components/RequestWithFocus.tsx +38 -0
  359. package/studio/web/pages/AnalyticsPage/components/SegmentedControl.tsx +35 -0
  360. package/studio/web/pages/AnalyticsPage/components/VsMedian.tsx +31 -0
  361. package/studio/web/pages/AnalyticsPage/index.tsx +17 -0
  362. package/studio/web/pages/GettingStartedPage/index.tsx +174 -0
  363. package/studio/web/pages/NewVideoPage/components/DeliverablesPicker.tsx +62 -0
  364. package/studio/web/pages/NewVideoPage/components/FormatPicker.tsx +33 -0
  365. package/studio/web/pages/NewVideoPage/index.tsx +133 -0
  366. package/studio/web/pages/NotFoundPage/index.tsx +9 -0
  367. package/studio/web/pages/OmnichatPage/components/NewFromLink.tsx +59 -0
  368. package/studio/web/pages/OmnichatPage/components/RuleListItem.tsx +64 -0
  369. package/studio/web/pages/OmnichatPage/components/RulePreview.tsx +14 -0
  370. package/studio/web/pages/OmnichatPage/components/RuleView.tsx +88 -0
  371. package/studio/web/pages/OmnichatPage/index.tsx +153 -0
  372. package/studio/web/pages/QueuePage/index.tsx +22 -0
  373. package/studio/web/pages/ReferencePage/components/ReferenceTexts.tsx +131 -0
  374. package/studio/web/pages/ReferencePage/index.tsx +216 -0
  375. package/studio/web/pages/ReferencesPage/components/NewReferenceForm.tsx +150 -0
  376. package/studio/web/pages/ReferencesPage/components/ReferenceCard.tsx +42 -0
  377. package/studio/web/pages/ReferencesPage/components/ReferenceGridSkeleton.tsx +31 -0
  378. package/studio/web/pages/ReferencesPage/index.tsx +67 -0
  379. package/studio/web/pages/SchedulePage/components/CalendarSkeleton.tsx +18 -0
  380. package/studio/web/pages/SchedulePage/components/DayView.tsx +177 -0
  381. package/studio/web/pages/SchedulePage/components/HourPicker.tsx +103 -0
  382. package/studio/web/pages/SchedulePage/components/MonthDayCell.tsx +111 -0
  383. package/studio/web/pages/SchedulePage/components/MonthView.tsx +47 -0
  384. package/studio/web/pages/SchedulePage/components/PlatformIcons.tsx +12 -0
  385. package/studio/web/pages/SchedulePage/components/PostThumb.tsx +15 -0
  386. package/studio/web/pages/SchedulePage/components/WeekColumn.tsx +75 -0
  387. package/studio/web/pages/SchedulePage/components/WeekView.tsx +35 -0
  388. package/studio/web/pages/SchedulePage/index.tsx +285 -0
  389. package/studio/web/pages/SettingsPage/components/AgentAccounts.tsx +263 -0
  390. package/studio/web/pages/SettingsPage/components/AgentCategory.tsx +20 -0
  391. package/studio/web/pages/SettingsPage/components/ModelPicker.tsx +49 -0
  392. package/studio/web/pages/SettingsPage/components/ModelsSettings.tsx +138 -0
  393. package/studio/web/pages/SettingsPage/components/PlatformsCategory.tsx +23 -0
  394. package/studio/web/pages/SettingsPage/components/PublishingCategory/AccountStatus.tsx +29 -0
  395. package/studio/web/pages/SettingsPage/components/PublishingCategory/AutoPublishPanel.tsx +90 -0
  396. package/studio/web/pages/SettingsPage/components/PublishingCategory/BrowserConnect.tsx +62 -0
  397. package/studio/web/pages/SettingsPage/components/PublishingCategory/CheckField.tsx +19 -0
  398. package/studio/web/pages/SettingsPage/components/PublishingCategory/InstagramPanel.tsx +113 -0
  399. package/studio/web/pages/SettingsPage/components/PublishingCategory/OmnichatSettingsPanel.tsx +146 -0
  400. package/studio/web/pages/SettingsPage/components/PublishingCategory/SaveBar.tsx +33 -0
  401. package/studio/web/pages/SettingsPage/components/PublishingCategory/SchedulePanel.tsx +132 -0
  402. package/studio/web/pages/SettingsPage/components/PublishingCategory/SettingsCard.tsx +47 -0
  403. package/studio/web/pages/SettingsPage/components/PublishingCategory/SettingsCardGroup.tsx +19 -0
  404. package/studio/web/pages/SettingsPage/components/PublishingCategory/TextPanel.tsx +55 -0
  405. package/studio/web/pages/SettingsPage/components/PublishingCategory/TiktokPanel.tsx +123 -0
  406. package/studio/web/pages/SettingsPage/components/PublishingCategory/YoutubePanel.tsx +100 -0
  407. package/studio/web/pages/SettingsPage/components/PublishingCategory/badges.tsx +15 -0
  408. package/studio/web/pages/SettingsPage/components/PublishingCategory/index.tsx +40 -0
  409. package/studio/web/pages/SettingsPage/components/PublishingCategory/types.ts +10 -0
  410. package/studio/web/pages/SettingsPage/components/UsageCategory.tsx +389 -0
  411. package/studio/web/pages/SettingsPage/components/VoiceProvidersCategory.tsx +97 -0
  412. package/studio/web/pages/SettingsPage/index.tsx +85 -0
  413. package/studio/web/pages/VideoPage/components/AudioPlayer.tsx +113 -0
  414. package/studio/web/pages/VideoPage/components/CaptionPositionEditor/AxisField.tsx +50 -0
  415. package/studio/web/pages/VideoPage/components/CaptionPositionEditor/CaptionList.tsx +56 -0
  416. package/studio/web/pages/VideoPage/components/CaptionPositionEditor/CaptionListItem.tsx +92 -0
  417. package/studio/web/pages/VideoPage/components/CaptionPositionEditor/CaptionMargins.tsx +57 -0
  418. package/studio/web/pages/VideoPage/components/CaptionPositionEditor/CaptionStyleFields.tsx +70 -0
  419. package/studio/web/pages/VideoPage/components/CaptionPositionEditor/CaptionTextForm.tsx +87 -0
  420. package/studio/web/pages/VideoPage/components/CaptionPositionEditor/ColorField.tsx +37 -0
  421. package/studio/web/pages/VideoPage/components/CaptionPositionEditor/MarginField.tsx +59 -0
  422. package/studio/web/pages/VideoPage/components/CaptionPositionEditor/NumberField.tsx +57 -0
  423. package/studio/web/pages/VideoPage/components/CaptionPositionEditor/PositionControls.tsx +140 -0
  424. package/studio/web/pages/VideoPage/components/CaptionPositionEditor/captionEditor.ts +215 -0
  425. package/studio/web/pages/VideoPage/components/CaptionPositionEditor/useCaptionPosition.ts +154 -0
  426. package/studio/web/pages/VideoPage/components/CarouselChangeRequest.tsx +72 -0
  427. package/studio/web/pages/VideoPage/components/CarouselCompositionStage.tsx +80 -0
  428. package/studio/web/pages/VideoPage/components/CarouselCreate.tsx +63 -0
  429. package/studio/web/pages/VideoPage/components/CarouselMissing.tsx +19 -0
  430. package/studio/web/pages/VideoPage/components/CarouselPublishStage.tsx +173 -0
  431. package/studio/web/pages/VideoPage/components/CarouselScriptStage.tsx +82 -0
  432. package/studio/web/pages/VideoPage/components/CarouselSlideCard.tsx +49 -0
  433. package/studio/web/pages/VideoPage/components/CarouselViewer.tsx +55 -0
  434. package/studio/web/pages/VideoPage/components/ChangedVoices.tsx +53 -0
  435. package/studio/web/pages/VideoPage/components/CompositionStage.tsx +78 -0
  436. package/studio/web/pages/VideoPage/components/CoverPicker.tsx +32 -0
  437. package/studio/web/pages/VideoPage/components/EditorStage.tsx +53 -0
  438. package/studio/web/pages/VideoPage/components/LineTakes.tsx +160 -0
  439. package/studio/web/pages/VideoPage/components/LineTextEditor.tsx +124 -0
  440. package/studio/web/pages/VideoPage/components/LineVoicePicker.tsx +37 -0
  441. package/studio/web/pages/VideoPage/components/MediaImage.tsx +25 -0
  442. package/studio/web/pages/VideoPage/components/MotionScriptStage.tsx +39 -0
  443. package/studio/web/pages/VideoPage/components/NarrationStage.tsx +70 -0
  444. package/studio/web/pages/VideoPage/components/PublishForm.tsx +387 -0
  445. package/studio/web/pages/VideoPage/components/PublishStage.tsx +76 -0
  446. package/studio/web/pages/VideoPage/components/PublishText.tsx +149 -0
  447. package/studio/web/pages/VideoPage/components/ScenesApprovalPanel.tsx +278 -0
  448. package/studio/web/pages/VideoPage/components/ScheduledInfo.tsx +72 -0
  449. package/studio/web/pages/VideoPage/components/ScriptApprovalPanel.tsx +189 -0
  450. package/studio/web/pages/VideoPage/components/ScriptStage.tsx +17 -0
  451. package/studio/web/pages/VideoPage/components/SequencePlayer.tsx +146 -0
  452. package/studio/web/pages/VideoPage/components/SoundTimeline/AddSound.tsx +117 -0
  453. package/studio/web/pages/VideoPage/components/SoundTimeline/AreaSelection.tsx +74 -0
  454. package/studio/web/pages/VideoPage/components/SoundTimeline/BedPanel.tsx +94 -0
  455. package/studio/web/pages/VideoPage/components/SoundTimeline/BedRow.tsx +45 -0
  456. package/studio/web/pages/VideoPage/components/SoundTimeline/CaptionOverlay.tsx +44 -0
  457. package/studio/web/pages/VideoPage/components/SoundTimeline/CaptionSaveBar.tsx +32 -0
  458. package/studio/web/pages/VideoPage/components/SoundTimeline/CaptionsPanel.tsx +22 -0
  459. package/studio/web/pages/VideoPage/components/SoundTimeline/ClipBlock.tsx +111 -0
  460. package/studio/web/pages/VideoPage/components/SoundTimeline/ClipPanel.tsx +76 -0
  461. package/studio/web/pages/VideoPage/components/SoundTimeline/CutsPanel.tsx +44 -0
  462. package/studio/web/pages/VideoPage/components/SoundTimeline/EditorTabBar.tsx +73 -0
  463. package/studio/web/pages/VideoPage/components/SoundTimeline/EffectChoice.tsx +46 -0
  464. package/studio/web/pages/VideoPage/components/SoundTimeline/EffectItem.tsx +89 -0
  465. package/studio/web/pages/VideoPage/components/SoundTimeline/EffectPanel.tsx +232 -0
  466. package/studio/web/pages/VideoPage/components/SoundTimeline/EffectsRow.tsx +21 -0
  467. package/studio/web/pages/VideoPage/components/SoundTimeline/Lane.tsx +39 -0
  468. package/studio/web/pages/VideoPage/components/SoundTimeline/MarkerRow.tsx +40 -0
  469. package/studio/web/pages/VideoPage/components/SoundTimeline/MuteButton.tsx +36 -0
  470. package/studio/web/pages/VideoPage/components/SoundTimeline/NumberInput.tsx +37 -0
  471. package/studio/web/pages/VideoPage/components/SoundTimeline/PlanWarnings.tsx +19 -0
  472. package/studio/web/pages/VideoPage/components/SoundTimeline/PlayerResizeHandle.tsx +50 -0
  473. package/studio/web/pages/VideoPage/components/SoundTimeline/PreviewControls.tsx +51 -0
  474. package/studio/web/pages/VideoPage/components/SoundTimeline/SceneRow.tsx +54 -0
  475. package/studio/web/pages/VideoPage/components/SoundTimeline/SettingsPanel.tsx +65 -0
  476. package/studio/web/pages/VideoPage/components/SoundTimeline/SoundLibrary.tsx +99 -0
  477. package/studio/web/pages/VideoPage/components/SoundTimeline/SoundPreview.tsx +82 -0
  478. package/studio/web/pages/VideoPage/components/SoundTimeline/SpeedPanel.tsx +128 -0
  479. package/studio/web/pages/VideoPage/components/SoundTimeline/TimelineDock.tsx +56 -0
  480. package/studio/web/pages/VideoPage/components/SoundTimeline/TimelineFooter.tsx +26 -0
  481. package/studio/web/pages/VideoPage/components/SoundTimeline/TimelineSkeleton.tsx +21 -0
  482. package/studio/web/pages/VideoPage/components/SoundTimeline/TimelineToolbar.tsx +160 -0
  483. package/studio/web/pages/VideoPage/components/SoundTimeline/TimelineTracks.tsx +121 -0
  484. package/studio/web/pages/VideoPage/components/SoundTimeline/TrackSidebar.tsx +30 -0
  485. package/studio/web/pages/VideoPage/components/SoundTimeline/VoiceRow.tsx +53 -0
  486. package/studio/web/pages/VideoPage/components/SoundTimeline/Waveform.tsx +97 -0
  487. package/studio/web/pages/VideoPage/components/SoundTimeline/clipActions.ts +50 -0
  488. package/studio/web/pages/VideoPage/components/SoundTimeline/effectActions.ts +161 -0
  489. package/studio/web/pages/VideoPage/components/SoundTimeline/index.tsx +161 -0
  490. package/studio/web/pages/VideoPage/components/SoundTimeline/timelineGeometry.ts +45 -0
  491. package/studio/web/pages/VideoPage/components/SoundTimeline/types.ts +43 -0
  492. package/studio/web/pages/VideoPage/components/SoundTimeline/useAutosave.ts +107 -0
  493. package/studio/web/pages/VideoPage/components/SoundTimeline/useEditorShortcuts.ts +168 -0
  494. package/studio/web/pages/VideoPage/components/SoundTimeline/useFitHeight.ts +40 -0
  495. package/studio/web/pages/VideoPage/components/SoundTimeline/useFollowPlayhead.ts +21 -0
  496. package/studio/web/pages/VideoPage/components/SoundTimeline/useListenMix.ts +43 -0
  497. package/studio/web/pages/VideoPage/components/SoundTimeline/usePlayback.ts +231 -0
  498. package/studio/web/pages/VideoPage/components/SoundTimeline/usePlayerWidth.ts +27 -0
  499. package/studio/web/pages/VideoPage/components/SoundTimeline/useSoundData.ts +124 -0
  500. package/studio/web/pages/VideoPage/components/SoundTimeline/useSoundTimeline.ts +511 -0
  501. package/studio/web/pages/VideoPage/components/StageDrawer/CompareVersions.tsx +111 -0
  502. package/studio/web/pages/VideoPage/components/StageDrawer/VersionCard.tsx +81 -0
  503. package/studio/web/pages/VideoPage/components/StageDrawer/VersionMedia.tsx +24 -0
  504. package/studio/web/pages/VideoPage/components/StageDrawer/VersionText.tsx +15 -0
  505. package/studio/web/pages/VideoPage/components/StageDrawer/index.tsx +109 -0
  506. package/studio/web/pages/VideoPage/components/StageProcess.tsx +126 -0
  507. package/studio/web/pages/VideoPage/components/StageResult.tsx +69 -0
  508. package/studio/web/pages/VideoPage/components/SwapReference.tsx +92 -0
  509. package/studio/web/pages/VideoPage/components/ThumbnailPanel.tsx +252 -0
  510. package/studio/web/pages/VideoPage/components/TimingPanel.tsx +89 -0
  511. package/studio/web/pages/VideoPage/components/VideoPlayer.tsx +18 -0
  512. package/studio/web/pages/VideoPage/components/VideoSkeleton.tsx +50 -0
  513. package/studio/web/pages/VideoPage/components/VideoVoiceInfo.tsx +33 -0
  514. package/studio/web/pages/VideoPage/components/VideoVoicePicker.tsx +165 -0
  515. package/studio/web/pages/VideoPage/components/VoicePauseBanner.tsx +56 -0
  516. package/studio/web/pages/VideoPage/components/WithLogo.tsx +10 -0
  517. package/studio/web/pages/VideoPage/index.tsx +295 -0
  518. package/studio/web/pages/VideosPage/components/PublishedOn.tsx +85 -0
  519. package/studio/web/pages/VideosPage/components/ServicesPanel.tsx +81 -0
  520. package/studio/web/pages/VideosPage/components/SessionsPanel.tsx +62 -0
  521. package/studio/web/pages/VideosPage/components/VideoListSkeleton.tsx +25 -0
  522. package/studio/web/pages/VideosPage/components/VideoRow.tsx +122 -0
  523. package/studio/web/pages/VideosPage/index.tsx +134 -0
  524. package/studio/web/pages/VoicePage/index.tsx +28 -0
  525. package/studio/web/router.tsx +201 -0
  526. package/studio/web/styles/base/elements.css +55 -0
  527. package/studio/web/styles/base/utilities.css +25 -0
  528. package/studio/web/styles/components/badge.css +33 -0
  529. package/studio/web/styles/components/claude-running.css +27 -0
  530. package/studio/web/styles/components/claude-session.css +212 -0
  531. package/studio/web/styles/components/claude-walking.css +92 -0
  532. package/studio/web/styles/components/clone-voice-save.css +136 -0
  533. package/studio/web/styles/components/clone-voice.css +379 -0
  534. package/studio/web/styles/components/controls.css +70 -0
  535. package/studio/web/styles/components/data-and-image.css +54 -0
  536. package/studio/web/styles/components/forms.css +132 -0
  537. package/studio/web/styles/components/human-banner.css +99 -0
  538. package/studio/web/styles/components/lightbox.css +50 -0
  539. package/studio/web/styles/components/markdown.css +68 -0
  540. package/studio/web/styles/components/modal.css +70 -0
  541. package/studio/web/styles/components/omni-cards.css +105 -0
  542. package/studio/web/styles/components/omni.css +859 -0
  543. package/studio/web/styles/components/omnichat.css +538 -0
  544. package/studio/web/styles/components/panel.css +115 -0
  545. package/studio/web/styles/components/platform-card.css +140 -0
  546. package/studio/web/styles/components/provider-card.css +132 -0
  547. package/studio/web/styles/components/queue.css +185 -0
  548. package/studio/web/styles/components/reference-actions.css +133 -0
  549. package/studio/web/styles/components/reference-picker.css +146 -0
  550. package/studio/web/styles/components/results-drawer.css +224 -0
  551. package/studio/web/styles/components/segmented.css +115 -0
  552. package/studio/web/styles/components/side-panel-tab.css +23 -0
  553. package/studio/web/styles/components/status-signals.css +66 -0
  554. package/studio/web/styles/components/stepper.css +208 -0
  555. package/studio/web/styles/components/table.css +19 -0
  556. package/studio/web/styles/components/tabs.css +24 -0
  557. package/studio/web/styles/components/ui/accordion.css +71 -0
  558. package/studio/web/styles/components/ui/chart.css +162 -0
  559. package/studio/web/styles/components/ui/checkbox.css +37 -0
  560. package/studio/web/styles/components/ui/date-picker.css +153 -0
  561. package/studio/web/styles/components/ui/select.css +110 -0
  562. package/studio/web/styles/components/ui/skeleton.css +25 -0
  563. package/studio/web/styles/components/ui/slider.css +81 -0
  564. package/studio/web/styles/components/ui/switch.css +41 -0
  565. package/studio/web/styles/components/ui/tooltip.css +24 -0
  566. package/studio/web/styles/components/voice-carousel.css +255 -0
  567. package/studio/web/styles/components/voice-editor.css +179 -0
  568. package/studio/web/styles/components/voice-grid.css +56 -0
  569. package/studio/web/styles/components/voice-profiles.css +192 -0
  570. package/studio/web/styles/design-system.md +136 -0
  571. package/studio/web/styles/index.css +116 -0
  572. package/studio/web/styles/layout/page.css +41 -0
  573. package/studio/web/styles/layout/sidebar-groups.css +39 -0
  574. package/studio/web/styles/layout/sidebar.css +111 -0
  575. package/studio/web/styles/pages/agent.css +26 -0
  576. package/studio/web/styles/pages/analytics/dashboard.css +614 -0
  577. package/studio/web/styles/pages/analytics/insights.css +48 -0
  578. package/studio/web/styles/pages/getting-started.css +166 -0
  579. package/studio/web/styles/pages/new-video/form.css +32 -0
  580. package/studio/web/styles/pages/not-found.css +59 -0
  581. package/studio/web/styles/pages/references.css +204 -0
  582. package/studio/web/styles/pages/schedule/calendar.css +541 -0
  583. package/studio/web/styles/pages/schedule/chip-states.css +60 -0
  584. package/studio/web/styles/pages/settings/models.css +72 -0
  585. package/studio/web/styles/pages/settings/shell.css +361 -0
  586. package/studio/web/styles/pages/settings/usage.css +63 -0
  587. package/studio/web/styles/pages/studio-offline.css +105 -0
  588. package/studio/web/styles/pages/video/caption-controls.css +194 -0
  589. package/studio/web/styles/pages/video/captions-grid.css +70 -0
  590. package/studio/web/styles/pages/video/captions.css +79 -0
  591. package/studio/web/styles/pages/video/editor-chat.css +103 -0
  592. package/studio/web/styles/pages/video/export.css +50 -0
  593. package/studio/web/styles/pages/video/frames.css +29 -0
  594. package/studio/web/styles/pages/video/fullscreen-stage.css +400 -0
  595. package/studio/web/styles/pages/video/instagram-carousel.css +309 -0
  596. package/studio/web/styles/pages/video/layout.css +285 -0
  597. package/studio/web/styles/pages/video/narration.css +138 -0
  598. package/studio/web/styles/pages/video/preview-overview.css +11 -0
  599. package/studio/web/styles/pages/video/processing-notice.css +34 -0
  600. package/studio/web/styles/pages/video/publish-when.css +76 -0
  601. package/studio/web/styles/pages/video/publish.css +97 -0
  602. package/studio/web/styles/pages/video/result.css +96 -0
  603. package/studio/web/styles/pages/video/scenes.css +167 -0
  604. package/studio/web/styles/pages/video/sound-timeline.css +672 -0
  605. package/studio/web/styles/pages/video/sound-toolbar.css +149 -0
  606. package/studio/web/styles/pages/video/take-player.css +100 -0
  607. package/studio/web/styles/pages/video/thumbnail.css +157 -0
  608. package/studio/web/styles/pages/video/timeline.css +117 -0
  609. package/studio/web/styles/pages/video/voice-picker.css +14 -0
  610. package/studio/web/styles/pages/video-list/active-row.css +27 -0
  611. package/studio/web/styles/pages/video-list/card-link.css +49 -0
  612. package/studio/web/styles/pages/video-list/list.css +86 -0
  613. package/studio/web/styles/theme/identity/details.css +204 -0
  614. package/studio/web/styles/theme/identity/shell.css +249 -0
  615. package/studio/web/styles/theme/identity/surfaces.css +183 -0
  616. package/studio/web/styles/theme/identity/titles-and-buttons.css +120 -0
  617. package/studio/web/styles/theme/no-borders/blocks.css +208 -0
  618. package/studio/web/styles/theme/no-borders/controls.css +256 -0
  619. package/studio/web/styles/tokens.css +245 -0
  620. package/studio/web/types/api.ts +2 -0
  621. package/studio/web/types/assets.d.ts +9 -0
  622. package/templates/brief.md +27 -0
  623. package/templates/captions.html +204 -0
  624. package/templates/carousel-composition.md +47 -0
  625. package/templates/carousel-script.md +60 -0
  626. package/templates/components/voiceavatar/README.md +87 -0
  627. package/templates/components/voiceavatar/demo.html +50 -0
  628. package/templates/components/voiceavatar/voiceavatar.js +415 -0
  629. package/templates/motion.md +80 -0
  630. package/templates/playbook.md +43 -0
  631. package/templates/reference.md +123 -0
  632. package/templates/scene.html +112 -0
  633. package/templates/scenes.example.json +96 -0
  634. package/templates/script.md +63 -0
  635. package/templates/slide.html +71 -0
  636. package/templates/wireframe.html +91 -0
  637. package/tools/analysis.mjs +300 -0
  638. package/tools/approval.mjs +442 -0
  639. package/tools/assemble.mjs +942 -0
  640. package/tools/build-motion.mjs +437 -0
  641. package/tools/captions.mjs +246 -0
  642. package/tools/carousel.mjs +558 -0
  643. package/tools/compose.mjs +307 -0
  644. package/tools/cuts.mjs +74 -0
  645. package/tools/editor.mjs +419 -0
  646. package/tools/epidemic-capture.js +54 -0
  647. package/tools/fanout.mjs +69 -0
  648. package/tools/history.mjs +70 -0
  649. package/tools/history.py +61 -0
  650. package/tools/job-queue.mjs +180 -0
  651. package/tools/job_queue.py +430 -0
  652. package/tools/lib/agents.mjs +412 -0
  653. package/tools/lib/channel.mjs +399 -0
  654. package/tools/lib/claude-settings.mjs +67 -0
  655. package/tools/lib/cli.mjs +9 -0
  656. package/tools/lib/fanout.mjs +234 -0
  657. package/tools/lib/http.mjs +94 -0
  658. package/tools/lib/index.js +14 -0
  659. package/tools/lib/mcp-server.mjs +69 -0
  660. package/tools/lib/metrics/metrics.mjs +204 -0
  661. package/tools/lib/narration.mjs +15 -0
  662. package/tools/lib/oauth-client.mjs +295 -0
  663. package/tools/lib/omnichat/config.mjs +139 -0
  664. package/tools/lib/omnichat/errors.mjs +42 -0
  665. package/tools/lib/omnichat/extract.mjs +82 -0
  666. package/tools/lib/omnichat/gate.mjs +151 -0
  667. package/tools/lib/omnichat/manual.mjs +166 -0
  668. package/tools/lib/omnichat/match.mjs +78 -0
  669. package/tools/lib/omnichat/pass.mjs +393 -0
  670. package/tools/lib/omnichat/record.mjs +208 -0
  671. package/tools/lib/omnichat/text.mjs +315 -0
  672. package/tools/lib/omnichat/webhook.mjs +166 -0
  673. package/tools/lib/pairing.mjs +99 -0
  674. package/tools/lib/paths.mjs +41 -0
  675. package/tools/lib/platforms/instagram.mjs +469 -0
  676. package/tools/lib/platforms/tiktok.mjs +343 -0
  677. package/tools/lib/platforms/youtube.mjs +228 -0
  678. package/tools/lib/publish/publish.mjs +244 -0
  679. package/tools/lib/schedule/schedule.mjs +191 -0
  680. package/tools/lib/service.mjs +134 -0
  681. package/tools/lib/skill-client.mjs +126 -0
  682. package/tools/lib/skills-release.mjs +196 -0
  683. package/tools/lib/testing.mjs +52 -0
  684. package/tools/lib/up.mjs +187 -0
  685. package/tools/metrics.mjs +415 -0
  686. package/tools/models.mjs +134 -0
  687. package/tools/narrate.mjs +257 -0
  688. package/tools/omni.mjs +298 -0
  689. package/tools/omni_paths.py +12 -0
  690. package/tools/omnichat.mjs +1577 -0
  691. package/tools/omnivideo.py +1122 -0
  692. package/tools/package.mjs +130 -0
  693. package/tools/platforms.mjs +236 -0
  694. package/tools/premiere.mjs +533 -0
  695. package/tools/publish.mjs +1496 -0
  696. package/tools/reference.mjs +1408 -0
  697. package/tools/render.mjs +426 -0
  698. package/tools/runner.mjs +156 -0
  699. package/tools/setup.mjs +466 -0
  700. package/tools/skills-release.mjs +93 -0
  701. package/tools/sound.mjs +1323 -0
  702. package/tools/soundtrack.mjs +217 -0
  703. package/tools/thumbnail.mjs +271 -0
  704. package/tools/timing.mjs +108 -0
  705. package/tools/usage.py +628 -0
  706. package/tools/voice-envelope.mjs +185 -0
  707. package/tools/voice.py +448 -0
  708. package/tools/voice_sample.py +75 -0
  709. package/tools/wireframe.mjs +230 -0
@@ -0,0 +1,142 @@
1
+ /* Where Omni's panel sits: it opens from the sidebar button and lives in one of the window's four corners.
2
+ Its top bar drags it; on release it snaps to the nearest corner, remembered in localStorage. It never
3
+ covers the sidebar: the left corners sit against the sidebar's edge and follow it when the menu is
4
+ collapsed or expanded (the right ones do not move). Never dragged, it is at the bottom left. */
5
+
6
+ import {
7
+ useCallback,
8
+ useEffect,
9
+ useRef,
10
+ useState,
11
+ type CSSProperties,
12
+ type PointerEvent as ReactPointerEvent,
13
+ } from "react";
14
+
15
+ type Corner = { h: "left" | "right"; v: "top" | "bottom" };
16
+ /* the sidebar's reach, in window pixels: how far from the left (a side menu) or from the top (a bar) */
17
+ type Inset = { left: number; top: number };
18
+ type Rect = { left: number; top: number; w: number; h: number };
19
+
20
+ const KEY = "omni.dock.panel";
21
+ const MARGIN = 8;
22
+ const DRAG_PX = 5;
23
+ const HOME: Corner = { h: "left", v: "bottom" };
24
+
25
+ function load(): Corner {
26
+ try {
27
+ const d = JSON.parse(localStorage.getItem(KEY) ?? "null");
28
+ if ((d?.h === "left" || d?.h === "right") && (d?.v === "top" || d?.v === "bottom")) return { h: d.h, v: d.v };
29
+ } catch {
30
+ /* no storage or a broken value: the default place */
31
+ }
32
+ return HOME;
33
+ }
34
+
35
+ /* the menu is a side column on desktop and a bar on top on a narrow screen */
36
+ function measureInset(): Inset {
37
+ const menu = document.querySelector(".menu")?.getBoundingClientRect();
38
+ if (!menu) return { left: 0, top: 0 };
39
+ return menu.width < innerWidth / 2 ? { left: menu.right, top: 0 } : { left: 0, top: Math.max(0, menu.bottom) };
40
+ }
41
+
42
+ const clamp = (n: number, lo: number, hi: number) => Math.min(Math.max(n, lo), Math.max(lo, hi));
43
+
44
+ /* the nearest corner of the free area (the window minus the sidebar) to the card's center */
45
+ function nearest(r: Rect, inset: Inset): Corner {
46
+ const cx = r.left + r.w / 2;
47
+ const cy = r.top + r.h / 2;
48
+ return {
49
+ h: cx < (inset.left + innerWidth) / 2 ? "left" : "right",
50
+ v: cy < (inset.top + innerHeight) / 2 ? "top" : "bottom",
51
+ };
52
+ }
53
+
54
+ export function useOmniDock(open: boolean) {
55
+ const [saved, setSaved] = useState<Corner>(load);
56
+ /* while dragging, the card follows the pointer (window pixels) and the corner is only saved on release */
57
+ const [live, setLive] = useState<{ left: number; top: number } | null>(null);
58
+ const [inset, setInset] = useState<Inset>({ left: 0, top: 0 });
59
+ const [dragging, setDragging] = useState(false);
60
+ const latest = useRef({ live, inset, saved });
61
+ latest.current = { live, inset, saved };
62
+
63
+ /* the menu changes width when collapsed or expanded: follow it, the card slides with the CSS transition */
64
+ useEffect(() => {
65
+ const read = () =>
66
+ setInset(prev => {
67
+ const next = measureInset();
68
+ return prev.left === next.left && prev.top === next.top ? prev : next;
69
+ });
70
+ read();
71
+ addEventListener("resize", read);
72
+ const menu = document.querySelector(".menu");
73
+ const ro = menu && new ResizeObserver(read);
74
+ if (menu) ro?.observe(menu);
75
+ return () => {
76
+ removeEventListener("resize", read);
77
+ ro?.disconnect();
78
+ };
79
+ }, [open]);
80
+
81
+ /* the pointer went down on the panel's top bar (its own buttons are not handles) */
82
+ const start = useCallback((e: ReactPointerEvent) => {
83
+ if (e.button !== 0 || (e.pointerType === "mouse" && e.buttons !== 1)) return;
84
+ if ((e.target as HTMLElement).closest("button, a, input, textarea, select, [role=radio], [role=slider]")) return;
85
+ const card = (e.currentTarget as HTMLElement).closest(".omni-dock")?.getBoundingClientRect();
86
+ if (!card) return;
87
+ const from = { x: e.clientX, y: e.clientY, left: card.left, top: card.top, w: card.width, h: card.height };
88
+ let moved = false;
89
+ const move = (m: PointerEvent) => {
90
+ const dx = m.clientX - from.x;
91
+ const dy = m.clientY - from.y;
92
+ if (!moved && Math.hypot(dx, dy) < DRAG_PX) return;
93
+ if (!moved) {
94
+ moved = true;
95
+ setDragging(true);
96
+ }
97
+ const { inset } = latest.current;
98
+ /* never over the sidebar, not even while dragging */
99
+ setLive({
100
+ left: clamp(from.left + dx, inset.left + MARGIN, innerWidth - from.w - MARGIN),
101
+ top: clamp(from.top + dy, inset.top + MARGIN, innerHeight - from.h - MARGIN),
102
+ });
103
+ };
104
+ const end = () => {
105
+ removeEventListener("pointermove", move);
106
+ removeEventListener("pointerup", end);
107
+ removeEventListener("pointercancel", end);
108
+ if (!moved) return;
109
+ setDragging(false);
110
+ const { live, inset, saved } = latest.current;
111
+ const next = live ? nearest({ ...live, w: from.w, h: from.h }, inset) : saved;
112
+ setLive(null);
113
+ setSaved(next);
114
+ try {
115
+ localStorage.setItem(KEY, JSON.stringify(next));
116
+ } catch {
117
+ /* the position just is not remembered */
118
+ }
119
+ };
120
+ addEventListener("pointermove", move);
121
+ addEventListener("pointerup", end);
122
+ addEventListener("pointercancel", end);
123
+ }, []);
124
+
125
+ const left = inset.left + MARGIN;
126
+ const top = inset.top + MARGIN;
127
+ const cardStyle = (open && {
128
+ ...(live
129
+ ? { left: live.left, top: live.top, right: "auto", bottom: "auto" }
130
+ : {
131
+ left: saved.h === "left" ? left : "auto",
132
+ right: saved.h === "right" ? MARGIN : "auto",
133
+ top: saved.v === "top" ? top : "auto",
134
+ bottom: saved.v === "bottom" ? MARGIN : "auto",
135
+ }),
136
+ /* the card's room: what is left beside the sidebar, for the width and height in the CSS */
137
+ ["--omni-px" as string]: `${left}px`,
138
+ ["--omni-py" as string]: `${top}px`,
139
+ }) as CSSProperties | undefined;
140
+
141
+ return { dragging, start, cardStyle };
142
+ }
@@ -0,0 +1,60 @@
1
+ /* Omni's greeting when the panel opens: the saved voice sample (GET /api/agent/voices/:gender/sample,
2
+ cached in .agent/samples), so it costs nothing on xAI after the first time. The same player says the
3
+ recorded "no que posso ajudar?" (clip "prompt") when a call starts and the user stays quiet. The sound
4
+ goes through an analyser so the avatar follows it, like it does in a live conversation. */
5
+
6
+ import { useCallback, useEffect, useRef, useState } from "react";
7
+ import type { AgentGender } from "../types/api";
8
+
9
+ export type GreetingClip = "greeting" | "prompt";
10
+
11
+ export function useOmniGreeting() {
12
+ const [playing, setPlaying] = useState(false);
13
+ const analyser = useRef<AnalyserNode | null>(null);
14
+ const audio = useRef<HTMLAudioElement | null>(null);
15
+ const ctx = useRef<AudioContext | null>(null);
16
+
17
+ const stop = useCallback(() => {
18
+ audio.current?.pause();
19
+ audio.current = null;
20
+ setPlaying(false);
21
+ }, []);
22
+
23
+ const play = useCallback(
24
+ (gender: AgentGender, clip: GreetingClip = "greeting") => {
25
+ stop();
26
+ const a = new Audio(`/api/agent/voices/${gender}/sample${clip === "prompt" ? "?clip=prompt" : ""}`);
27
+ audio.current = a;
28
+ try {
29
+ ctx.current ??= new AudioContext();
30
+ void ctx.current.resume();
31
+ const an = ctx.current.createAnalyser();
32
+ an.fftSize = 512;
33
+ ctx.current.createMediaElementSource(a).connect(an);
34
+ an.connect(ctx.current.destination);
35
+ analyser.current = an;
36
+ } catch {
37
+ analyser.current = null;
38
+ }
39
+ const done = () => {
40
+ if (audio.current === a) stop();
41
+ };
42
+ a.onended = done;
43
+ a.onerror = done;
44
+ setPlaying(true);
45
+ /* the browser may refuse autoplay: then it is simply silent */
46
+ a.play().catch(done);
47
+ },
48
+ [stop],
49
+ );
50
+
51
+ useEffect(
52
+ () => () => {
53
+ audio.current?.pause();
54
+ void ctx.current?.close();
55
+ },
56
+ [],
57
+ );
58
+
59
+ return { playing, analyser, play, stop };
60
+ }
@@ -0,0 +1,128 @@
1
+ /* "Oi, Omni" (lib/omni-hotword.ts) in the app shell: the switch (per browser, the same in every tab),
2
+ and the listener, which runs in one studio tab only (a Web Lock: when that tab closes, another takes
3
+ over) and stops while Omni is busy in any tab (a call, the greeting, a Claude-mode recording), so the
4
+ microphone is free for the conversation and a second call never opens. */
5
+
6
+ import { useEffect, useRef, useState } from "react";
7
+ import {
8
+ HOTWORD_STORAGE_KEY,
9
+ HotwordListener,
10
+ type HotwordState,
11
+ loadHotwordOn,
12
+ saveHotwordOn,
13
+ } from "../lib/omni-hotword";
14
+
15
+ export type HotwordStatus = HotwordState | "off" | "paused" | "elsewhere";
16
+
17
+ const LOCK = "omni-hotword";
18
+ const CHANNEL = "omni-hotword";
19
+ /* a busy tab says so this often; one silent for STALE_MS (closed mid-call) no longer counts */
20
+ const BEAT_MS = 5000;
21
+ const STALE_MS = 12_000;
22
+
23
+ export function useOmniHotword({
24
+ available,
25
+ busy,
26
+ onCall,
27
+ }: {
28
+ /* Omni is on and reachable */
29
+ available: boolean;
30
+ /* this tab's Omni is talking or recording */
31
+ busy: boolean;
32
+ onCall: () => void;
33
+ }) {
34
+ const [on, setOnState] = useState(loadHotwordOn);
35
+ useEffect(() => {
36
+ const sync = (e: StorageEvent) => {
37
+ if (e.key === HOTWORD_STORAGE_KEY) setOnState(loadHotwordOn());
38
+ };
39
+ window.addEventListener("storage", sync);
40
+ return () => window.removeEventListener("storage", sync);
41
+ }, []);
42
+ const setOn = (v: boolean) => {
43
+ saveHotwordOn(v);
44
+ setOnState(v);
45
+ };
46
+ const wanted = on && available;
47
+
48
+ /* the tab that listens */
49
+ const [leader, setLeader] = useState(false);
50
+ useEffect(() => {
51
+ if (!wanted) return setLeader(false);
52
+ if (!navigator.locks) return setLeader(true);
53
+ const abort = new AbortController();
54
+ let release: (() => void) | null = null;
55
+ navigator.locks
56
+ .request(LOCK, { signal: abort.signal }, () => {
57
+ setLeader(true);
58
+ return new Promise<void>(r => {
59
+ release = r;
60
+ });
61
+ })
62
+ .catch(() => {});
63
+ return () => {
64
+ abort.abort();
65
+ release?.();
66
+ setLeader(false);
67
+ };
68
+ }, [wanted]);
69
+
70
+ /* the other tabs' calls */
71
+ const [othersBusy, setOthersBusy] = useState(false);
72
+ const me = useRef(Math.random().toString(36).slice(2));
73
+ const busyRef = useRef(busy);
74
+ busyRef.current = busy;
75
+ const channel = useRef<BroadcastChannel | null>(null);
76
+ useEffect(() => {
77
+ if (typeof BroadcastChannel === "undefined") return;
78
+ const ch = new BroadcastChannel(CHANNEL);
79
+ channel.current = ch;
80
+ const seen = new Map<string, number>();
81
+ const recount = () => {
82
+ const now = Date.now();
83
+ for (const [id, at] of seen) if (now - at > STALE_MS) seen.delete(id);
84
+ setOthersBusy(seen.size > 0);
85
+ };
86
+ ch.onmessage = (e: MessageEvent<{ id: string; busy: boolean }>) => {
87
+ if (e.data.busy) seen.set(e.data.id, Date.now());
88
+ else seen.delete(e.data.id);
89
+ recount();
90
+ };
91
+ const beat = setInterval(() => {
92
+ if (busyRef.current) ch.postMessage({ id: me.current, busy: true });
93
+ recount();
94
+ }, BEAT_MS);
95
+ return () => {
96
+ clearInterval(beat);
97
+ ch.postMessage({ id: me.current, busy: false });
98
+ ch.close();
99
+ channel.current = null;
100
+ };
101
+ }, []);
102
+ useEffect(() => {
103
+ channel.current?.postMessage({ id: me.current, busy });
104
+ }, [busy]);
105
+
106
+ const paused = busy || othersBusy;
107
+ const [state, setState] = useState<HotwordState | null>(null);
108
+ const [error, setError] = useState<string | null>(null);
109
+ const onCallRef = useRef(onCall);
110
+ onCallRef.current = onCall;
111
+ useEffect(() => {
112
+ setState(null);
113
+ setError(null);
114
+ if (!wanted || !leader || paused) return;
115
+ const listener = new HotwordListener({
116
+ onCall: () => onCallRef.current(),
117
+ onState: (s, e) => {
118
+ setState(s);
119
+ setError(e ?? null);
120
+ },
121
+ });
122
+ void listener.start();
123
+ return () => listener.stop();
124
+ }, [wanted, leader, paused]);
125
+
126
+ const status: HotwordStatus = !wanted ? "off" : !leader ? "elsewhere" : paused ? "paused" : (state ?? "starting");
127
+ return { on, setOn, status, error };
128
+ }
@@ -0,0 +1,26 @@
1
+ /* The SSE "omnichat" notice arrives when the rule, the record or the publication.json of a
2
+ video ({ slug }) or of an outside post ({ code }) changes, or the account pause ({}). */
3
+
4
+ import { api } from "../lib/api";
5
+ import { ruleApi, ruleApiBase, type RuleTargetRef } from "../lib/omnichat-api";
6
+ import { queryKeys } from "../lib/query-client";
7
+ import type { OmnichatRuleDetail, OmnichatState } from "../types/api";
8
+ import { useLiveQuery } from "./useLiveQuery";
9
+
10
+ /* rules = false: without the rule list (GET /api/omnichat?rules=0) */
11
+ export function useOmnichatState(rules = true) {
12
+ const { data, error, reload } = useLiveQuery(queryKeys.omnichat(rules), () =>
13
+ api.omnichat.$get({ query: rules ? {} : { rules: "0" } }),
14
+ );
15
+ const state: OmnichatState | null = data;
16
+ return { state, error, reload };
17
+ }
18
+
19
+ /* the rule of a video ({ slug }), of a content's carousel ({ slug, carousel }) or of a post ({ code }) */
20
+ export function useOmnichatRule(target: RuleTargetRef) {
21
+ const { data, error, reload } = useLiveQuery(queryKeys.omnichatRule(ruleApiBase(target)), () =>
22
+ ruleApi(target).get(),
23
+ );
24
+ const detail: OmnichatRuleDetail | null = data;
25
+ return { detail, error, reload };
26
+ }
@@ -0,0 +1,27 @@
1
+ /* Getting started (/api/onboarding): the docs/onboarding.md guide in steps, each with the
2
+ tools/setup.mjs check. `reload(true)` asks for a fresh check (?fresh=1). */
3
+
4
+ import { useCallback, useState } from "react";
5
+ import { api, errorText, read } from "../lib/api";
6
+ import { queryClient, queryKeys } from "../lib/query-client";
7
+ import type { Onboarding } from "../types/api";
8
+ import { useLiveQuery } from "./useLiveQuery";
9
+
10
+ export function useOnboarding() {
11
+ const { data, error, isFetching } = useLiveQuery(queryKeys.onboarding, () => api.onboarding.$get({ query: {} }));
12
+ const onboarding: Onboarding | null = data;
13
+ const [freshError, setFreshError] = useState<string | null>(null);
14
+ const [checking, setChecking] = useState(false);
15
+ const reload = useCallback(async (fresh = false) => {
16
+ setChecking(true);
17
+ setFreshError(null);
18
+ try {
19
+ const next: Onboarding = await read(api.onboarding.$get({ query: fresh ? { fresh: "1" } : {} }));
20
+ queryClient.setQueryData(queryKeys.onboarding, next);
21
+ } catch (e) {
22
+ setFreshError(errorText(e));
23
+ }
24
+ setChecking(false);
25
+ }, []);
26
+ return { onboarding, error: freshError ?? error, checking: checking || isFetching, reload };
27
+ }
@@ -0,0 +1,23 @@
1
+ /* A POST to the studio API as a TanStack mutation. On ok, the given query keys are
2
+ invalidated (the SSE stream refreshes most data anyway; this is for what it doesn't push:
3
+ settings, usage, accounts…). The variable is the typed client call:
4
+ post.mutateAsync(() => api.usage.refresh.$post({ json: {} })). */
5
+
6
+ import { useMutation, type QueryKey } from "@tanstack/react-query";
7
+ import type { ClientResponse } from "hono/client";
8
+ import { send, type Envelope } from "../lib/api";
9
+ import { invalidate } from "../lib/query-client";
10
+
11
+ type AnyResponse = ClientResponse<unknown, number, "json" | "text" | "redirect">;
12
+
13
+ export function usePost<R extends AnyResponse>(
14
+ options: { invalidate?: QueryKey[]; onDone?: (r: Envelope<R>) => void } = {},
15
+ ) {
16
+ return useMutation({
17
+ mutationFn: (request: () => Promise<R>) => send(request()),
18
+ onSuccess: async r => {
19
+ if (r.ok && options.invalidate?.length) await invalidate(...options.invalidate);
20
+ options.onDone?.(r);
21
+ },
22
+ });
23
+ }
@@ -0,0 +1,10 @@
1
+ import { api } from "../lib/api";
2
+ import { queryKeys } from "../lib/query-client";
3
+ import type { Queue } from "../types/api";
4
+ import { useLiveQuery } from "./useLiveQuery";
5
+
6
+ /* the machine queue; the SSE "queue" event pushes it whole */
7
+ export function useQueue(): Queue | null {
8
+ const queue: Queue | null = useLiveQuery(queryKeys.queue, () => api.queue.$get()).data;
9
+ return queue;
10
+ }
@@ -0,0 +1,20 @@
1
+ import { api, enc } from "../lib/api";
2
+ import { queryKeys } from "../lib/query-client";
3
+ import type { ReferenceDetail, ReferenceSummary } from "../types/api";
4
+ import { useLiveQuery } from "./useLiveQuery";
5
+
6
+ /* refetched on every SSE "reference" notice */
7
+ export function useReferences() {
8
+ const { data, error, reload } = useLiveQuery(queryKeys.references, () => api.references.$get());
9
+ const references: ReferenceSummary[] | null = data;
10
+ return { references, error, reload };
11
+ }
12
+
13
+ /* refetched on the SSE "reference" notice for this name */
14
+ export function useReference(name: string) {
15
+ const { data, error, reload } = useLiveQuery(queryKeys.reference(name), () =>
16
+ api.references[":name"].$get({ param: { name: enc(name) } }),
17
+ );
18
+ const reference: ReferenceDetail | null = data;
19
+ return { reference, error, reload };
20
+ }
@@ -0,0 +1,101 @@
1
+ /* The edit state of one omnichat rule (OmnichatRulePanel): the form against the saved rule,
2
+ the two-click confirmations (enable, save while enabled), the dry run and the requests. */
3
+
4
+ import { useEffect, useState } from "react";
5
+ import type { ClientResponse } from "hono/client";
6
+ import { send, type ActionResult } from "../lib/api";
7
+ import { ruleApi } from "../lib/omnichat-api";
8
+ import { ruleBodyOf, ruleFormOf, type RuleForm, type RuleTarget } from "../components/RuleForm";
9
+ import { useOmnichatRule } from "./useOmnichat";
10
+
11
+ export function useRuleEditor(target: RuleTarget) {
12
+ const { detail: d, error: readError, reload } = useOmnichatRule(target);
13
+ const rules = ruleApi(target);
14
+ const rule = d?.rule ?? null;
15
+ const initial = ruleFormOf(rule, d);
16
+ const key = JSON.stringify(initial);
17
+ /* the form restarts when the saved rule changes, not when the defaults that fill its empty fields
18
+ change: a new default in the config must not wipe what is being written */
19
+ const ruleKey = JSON.stringify([!!d, rule, d?.found_link ?? null]);
20
+ const [form, setForm] = useState<RuleForm>(initial);
21
+ useEffect(() => {
22
+ setForm(initial);
23
+ }, [ruleKey]);
24
+ const changed = JSON.stringify(form) !== key;
25
+ const [busy, setBusy] = useState<string | null>(null);
26
+ const [result, setResult] = useState<ActionResult>(null);
27
+ const [problems, setProblems] = useState<string[]>([]);
28
+ const [confirm, setConfirm] = useState(false);
29
+ /* an enabled rule that changes (another word, any comment) rereads the 7 days with the new rule:
30
+ saving also goes outside and asks for the second click */
31
+ const [confirmSave, setConfirmSave] = useState(false);
32
+ const [dryRun, setDryRun] = useState<string | null>(null);
33
+
34
+ /* several fields at once: a choice that sets more than one (the delivery mode) is a single edit */
35
+ const fields = (patch: Partial<RuleForm>) => {
36
+ setForm(s => ({ ...s, ...patch }));
37
+ setConfirm(false);
38
+ setConfirmSave(false);
39
+ };
40
+ const field = <K extends keyof RuleForm>(k: K, v: RuleForm[K]) => fields({ [k]: v });
41
+ const request = async <R extends ClientResponse<unknown, number, "json" | "text" | "redirect">>(
42
+ name: string,
43
+ call: () => Promise<R>,
44
+ ) => {
45
+ setBusy(name);
46
+ setResult(null);
47
+ const r = await send(call());
48
+ setBusy(null);
49
+ setResult(r);
50
+ if (r.ok) reload();
51
+ return r;
52
+ };
53
+
54
+ const save = async () => {
55
+ const r = await request("save", () => rules.save(ruleBodyOf(form)));
56
+ setConfirmSave(false);
57
+ if (r.ok) setProblems(r.problems ?? []);
58
+ };
59
+ const enable = async (on: boolean) => {
60
+ const r = await request(on ? "enable" : "disable", () => rules.enable(on));
61
+ setConfirm(false);
62
+ if (r.ok) setProblems([]);
63
+ };
64
+ const runDry = async () => {
65
+ setDryRun(null);
66
+ const r = await request("dry-run", () => rules.dryRun());
67
+ if (r.ok) {
68
+ setDryRun(r.output ?? "");
69
+ setResult(null);
70
+ }
71
+ };
72
+
73
+ return {
74
+ d,
75
+ readError,
76
+ reload,
77
+ rules,
78
+ rule,
79
+ initial,
80
+ changed,
81
+ form,
82
+ setForm,
83
+ busy,
84
+ result,
85
+ problems,
86
+ confirm,
87
+ setConfirm,
88
+ confirmSave,
89
+ setConfirmSave,
90
+ dryRun,
91
+ setDryRun,
92
+ field,
93
+ fields,
94
+ request,
95
+ save,
96
+ enable,
97
+ runDry,
98
+ };
99
+ }
100
+
101
+ export type RuleEditor = ReturnType<typeof useRuleEditor>;
@@ -0,0 +1,24 @@
1
+ import { api } from "../lib/api";
2
+ import { queryKeys } from "../lib/query-client";
3
+ import { useLiveQuery } from "./useLiveQuery";
4
+
5
+ /* Whether the studio answers. Opened at 127.0.0.1 always yes; in the hosted one, from the hub
6
+ (cloud/studio). While it isn't connected the page checks again every 5 s, and the SSE reconnecting
7
+ rereads everything. */
8
+ export function useRunner() {
9
+ const { data, reload, isFetching } = useLiveQuery(queryKeys.runner, () => api.runner.$get());
10
+ return {
11
+ /** the page is the hosted studio's (not 127.0.0.1): /runner and /exit exist */
12
+ hosted: data?.local === false,
13
+ /** null until the first answer */
14
+ online: data ? data.online : null,
15
+ name: data?.name ?? null,
16
+ /** while away: when it was last connected, and what kept it out (cloud/studio/src/presence.mjs) */
17
+ lastSeen: data?.last_seen ?? null,
18
+ problem: data?.problem ?? null,
19
+ /** a check is in flight (the "Tentar de novo" button) */
20
+ checking: isFetching,
21
+ /** asks now instead of waiting for the next check */
22
+ check: reload,
23
+ };
24
+ }
@@ -0,0 +1,23 @@
1
+ /* A settings POST with its result: `result` feeds ResultMessage, `onSave` runs after an ok.
2
+ `save` takes the typed client call (lib/api), e.g. save(() => api.settings.key.$post({ json })). */
3
+
4
+ import { useState } from "react";
5
+ import type { ClientResponse } from "hono/client";
6
+ import { send, type ActionResult } from "../lib/api";
7
+
8
+ export function useSaveAction(onSave: () => void) {
9
+ const [result, setResult] = useState<ActionResult>(null);
10
+ const [saving, setSaving] = useState(false);
11
+ const save = async <R extends ClientResponse<unknown, number, "json" | "text" | "redirect">>(
12
+ request: () => Promise<R>,
13
+ ) => {
14
+ setSaving(true);
15
+ setResult(null);
16
+ const r = await send(request());
17
+ setResult(r);
18
+ setSaving(false);
19
+ if (r.ok) onSave();
20
+ return r.ok;
21
+ };
22
+ return { result, saving, save };
23
+ }
@@ -0,0 +1,11 @@
1
+ import { api } from "../lib/api";
2
+ import { queryKeys } from "../lib/query-client";
3
+ import type { Schedule } from "../types/api";
4
+ import { useLiveQuery } from "./useLiveQuery";
5
+
6
+ /* GET /api/publish/schedule (`publish.mjs scheduler --json`); refetched on SSE "video" notices */
7
+ export function useSchedule() {
8
+ const { data, error, reload } = useLiveQuery(queryKeys.schedule, () => api.publish.schedule.$get());
9
+ const schedule: Schedule | null = data;
10
+ return { schedule, error, reload };
11
+ }
@@ -0,0 +1,10 @@
1
+ import { api } from "../lib/api";
2
+ import { queryKeys } from "../lib/query-client";
3
+ import type { Services } from "../types/api";
4
+ import { useLiveQuery } from "./useLiveQuery";
5
+
6
+ /* voice providers, VoiceStudio, Premiere and memory; the SSE "services" event pushes them whole */
7
+ export function useServices(): Services | null {
8
+ const services: Services | null = useLiveQuery(queryKeys.services, () => api.services.$get()).data;
9
+ return services;
10
+ }
@@ -0,0 +1,15 @@
1
+ import { api } from "../lib/api";
2
+ import { queryKeys } from "../lib/query-client";
3
+ import type { Session } from "../types/api";
4
+ import { useLiveQuery } from "./useLiveQuery";
5
+
6
+ /* the studio sessions; the SSE "sessions" event pushes the list whole */
7
+ export function useSessions(): Session[] | null {
8
+ const sessions: Session[] | null = useLiveQuery(queryKeys.sessions, () => api.sessions.$get()).data;
9
+ return sessions;
10
+ }
11
+
12
+ /* the most recent studio session for a video, a reference, the channel analysis or an edit */
13
+ export function useSessionFor(type: Session["type"], target: string): Session | null {
14
+ return useSessions()?.find(s => s.type === type && s.target === target) ?? null;
15
+ }
@@ -0,0 +1,11 @@
1
+ import { api } from "../lib/api";
2
+ import { queryKeys } from "../lib/query-client";
3
+ import type { Settings } from "../types/api";
4
+ import { useLiveQuery } from "./useLiveQuery";
5
+
6
+ /* GET /api/settings; reload (or invalidate(queryKeys.settings)) after a POST */
7
+ export function useSettings() {
8
+ const { data, error, reload } = useLiveQuery(queryKeys.settings, () => api.settings.$get());
9
+ const settings: Settings | null = data;
10
+ return { settings, error, reload };
11
+ }
@@ -0,0 +1,30 @@
1
+ /* One config section of Configurações → Publicação (`publish.<section>`): local values until
2
+ saved, with the changed flag, discard and save. */
3
+
4
+ import { useEffect, useState } from "react";
5
+ import { api } from "../lib/api";
6
+ import { useSaveAction } from "./useSaveAction";
7
+
8
+ export function useSettingsSection<T extends Record<string, unknown>>(
9
+ section: string,
10
+ current: T,
11
+ onSave: () => void,
12
+ toPayload: (v: T) => Record<string, unknown> = v => v,
13
+ ) {
14
+ const [values, setValues] = useState<T>(current);
15
+ const { result, saving, save } = useSaveAction(onSave);
16
+ const key = JSON.stringify(current);
17
+ useEffect(() => {
18
+ setValues(current);
19
+ }, [key]);
20
+ return {
21
+ values,
22
+ changed: JSON.stringify(values) !== key,
23
+ saving,
24
+ result,
25
+ set: <K extends keyof T>(k: K, value: T[K]) => setValues(s => ({ ...s, [k]: value })),
26
+ discard: () => setValues(current),
27
+ save: (over?: Partial<T>) =>
28
+ save(() => api.settings.publish.$post({ json: { section, values: toPayload({ ...values, ...over }) } })),
29
+ };
30
+ }