@gentbajko/slopify 4.0.0 → 4.2.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 (375) hide show
  1. package/dist/adapters/account-limits.js +1 -1
  2. package/dist/adapters/alignment/audio.js +1 -1
  3. package/dist/adapters/alignment/cache.js +1 -1
  4. package/dist/adapters/alignment/multilingual.js +2 -1
  5. package/dist/adapters/alignment/runner.js +1 -1
  6. package/dist/adapters/alignment/worker.js +1 -1
  7. package/dist/adapters/explain.js +10 -9
  8. package/dist/adapters/host-cli/index.js +2 -1
  9. package/dist/adapters/host-cli/transport.js +2 -1
  10. package/dist/adapters/image/bytes.js +2 -1
  11. package/dist/adapters/image/codex.js +6 -5
  12. package/dist/adapters/image/fal.js +4 -3
  13. package/dist/adapters/image/google.js +3 -2
  14. package/dist/adapters/image/models.js +2 -2
  15. package/dist/adapters/image/openai.js +2 -1
  16. package/dist/adapters/image/replicate.js +3 -2
  17. package/dist/adapters/image/video.js +2 -1
  18. package/dist/adapters/key-probes.js +1 -1
  19. package/dist/adapters/llm/antigravity-models.js +1 -1
  20. package/dist/adapters/llm/antigravity.js +7 -6
  21. package/dist/adapters/llm/assistant-cli.js +1 -1
  22. package/dist/adapters/llm/claude-code-models.js +1 -1
  23. package/dist/adapters/llm/claude-code.js +3 -2
  24. package/dist/adapters/llm/codex-models.js +1 -1
  25. package/dist/adapters/llm/document-workspace.js +3 -2
  26. package/dist/adapters/llm/image-workspace.js +3 -2
  27. package/dist/adapters/llm/openrouter.js +2 -1
  28. package/dist/adapters/llm/run-cli.js +3 -2
  29. package/dist/adapters/tts/gemini.js +2 -1
  30. package/dist/adapters/tts/inworld-async.js +2 -1
  31. package/dist/adapters/tts/inworld.js +3 -2
  32. package/dist/adapters/tts/system.js +2 -1
  33. package/dist/catalog/registry.js +20 -13
  34. package/dist/catalog/schema.js +2 -1
  35. package/dist/catalog/validate.js +5 -2
  36. package/dist/edge/http/actions.js +7 -7
  37. package/dist/edge/http/app.js +5 -3
  38. package/dist/edge/http/assistant.js +3 -1
  39. package/dist/edge/http/backups.js +1 -1
  40. package/dist/edge/http/channel-memory.js +2 -2
  41. package/dist/edge/http/channels.js +1 -1
  42. package/dist/edge/http/checkpoints.js +7 -5
  43. package/dist/edge/http/design-templates.js +3 -3
  44. package/dist/edge/http/designs.js +1 -1
  45. package/dist/edge/http/document-themes.js +3 -3
  46. package/dist/edge/http/draft-problem.js +1 -1
  47. package/dist/edge/http/extension-auth.js +1 -1
  48. package/dist/edge/http/home.js +9 -1
  49. package/dist/edge/http/model-checks.js +4 -4
  50. package/dist/edge/http/onboarding.js +29 -12
  51. package/dist/edge/http/outputs.js +3 -2
  52. package/dist/edge/http/patch-notes.js +2 -2
  53. package/dist/edge/http/planning.js +1 -1
  54. package/dist/edge/http/project-create.js +1 -1
  55. package/dist/edge/http/project-templates.js +2 -2
  56. package/dist/edge/http/projects.js +22 -4
  57. package/dist/edge/http/prompts.js +2 -2
  58. package/dist/edge/http/pronunciation-check.js +133 -0
  59. package/dist/edge/http/pronunciations.js +26 -0
  60. package/dist/edge/http/providers.js +9 -10
  61. package/dist/edge/http/refusal.js +1 -1
  62. package/dist/edge/http/remote-access.js +4 -4
  63. package/dist/edge/http/remote-guard.js +2 -2
  64. package/dist/edge/http/reviews.js +1 -1
  65. package/dist/edge/http/revision-files.js +1 -1
  66. package/dist/edge/http/revisions.js +4 -4
  67. package/dist/edge/http/schedules.js +13 -11
  68. package/dist/edge/http/settings.js +6 -2
  69. package/dist/edge/http/social.js +5 -4
  70. package/dist/edge/http/staging.js +1 -1
  71. package/dist/edge/http/storage.js +12 -4
  72. package/dist/edge/http/studio.js +71 -19
  73. package/dist/edge/http/style-preview.js +2 -2
  74. package/dist/edge/http/template-studio.js +12 -9
  75. package/dist/edge/http/trash.js +1 -1
  76. package/dist/edge/http/youtube-edits.js +1 -1
  77. package/dist/edge/mcp/excluded-routes.js +4 -3
  78. package/dist/edge/mcp/instructions.js +3 -3
  79. package/dist/edge/mcp/library-tools.js +8 -5
  80. package/dist/edge/mcp/remake-tools.js +7 -4
  81. package/dist/edge/mcp/server.js +1 -1
  82. package/dist/edge/mcp/tools-channels.js +9 -9
  83. package/dist/edge/mcp/tools-designs.js +9 -9
  84. package/dist/edge/mcp/tools-library.js +31 -7
  85. package/dist/edge/mcp/tools-planning.js +18 -18
  86. package/dist/edge/mcp/tools-projects.js +10 -10
  87. package/dist/edge/mcp/tools-publishing.js +9 -9
  88. package/dist/edge/mcp/tools-runs.js +9 -9
  89. package/dist/edge/mcp/tools-schedules.js +6 -6
  90. package/dist/edge/mcp/tools-setup.js +20 -14
  91. package/dist/edge/mcp/tools-social.js +5 -4
  92. package/dist/edge/mcp/tools-storage.js +3 -3
  93. package/dist/edge/mcp/tools-template-studio.js +18 -18
  94. package/dist/edge/remote-access/model.js +1 -1
  95. package/dist/extension/slopify-studio-chrome.zip +0 -0
  96. package/dist/extension/slopify-studio-firefox.zip +0 -0
  97. package/dist/kernel/db/migrations/0078-uploaded-from-links.sql +7 -0
  98. package/dist/kernel/pipeline.js +12 -0
  99. package/dist/kernel/ports/settings-places.js +31 -0
  100. package/dist/kernel/ports/system-speech.js +31 -3
  101. package/dist/kernel/runner/attempt.js +11 -3
  102. package/dist/kernel/runner/providers.js +3 -2
  103. package/dist/kernel/runner/standalone.js +2 -2
  104. package/dist/main.js +5 -3
  105. package/dist/model-catalog.js +20 -0
  106. package/dist/patch-notes/4.0.0.md +13 -13
  107. package/dist/patch-notes/4.1.0.md +42 -0
  108. package/dist/patch-notes/4.2.0.md +28 -0
  109. package/dist/patch-notes/index.json +12 -0
  110. package/dist/slices/admission/listing-facts.js +95 -0
  111. package/dist/slices/admission/repo.js +9 -6
  112. package/dist/slices/admission/rules.js +7 -7
  113. package/dist/slices/admission/schema.js +2 -0
  114. package/dist/slices/admission/start.js +7 -0
  115. package/dist/slices/admission/state-words.js +34 -5
  116. package/dist/slices/assistant/cost.js +1 -1
  117. package/dist/slices/assistant/gate.js +1 -0
  118. package/dist/slices/assistant/model.js +10 -3
  119. package/dist/slices/assistant/turn.js +5 -3
  120. package/dist/slices/autopilot/warnings.js +1 -1
  121. package/dist/slices/backups/service.js +5 -5
  122. package/dist/slices/channels/cast-images.js +1 -1
  123. package/dist/slices/channels/schema.js +1 -1
  124. package/dist/slices/channels/videos.js +1 -1
  125. package/dist/slices/control/providers.js +2 -2
  126. package/dist/slices/designs/budget.js +1 -1
  127. package/dist/slices/designs/calls.js +2 -2
  128. package/dist/slices/designs/check-context.js +4 -4
  129. package/dist/slices/designs/fields.js +4 -4
  130. package/dist/slices/designs/gate.js +7 -5
  131. package/dist/slices/designs/render-input.js +1 -1
  132. package/dist/slices/designs/requests.js +1 -1
  133. package/dist/slices/designs/runner.js +1 -1
  134. package/dist/slices/designs/sample.js +2 -1
  135. package/dist/slices/designs/service.js +1 -1
  136. package/dist/slices/designs/template-checks.js +2 -2
  137. package/dist/slices/designs/template-make.js +3 -3
  138. package/dist/slices/designs/templates.js +3 -3
  139. package/dist/slices/document/fonts.js +1 -1
  140. package/dist/slices/document/model.js +7 -4
  141. package/dist/slices/document/render.js +2 -1
  142. package/dist/slices/document/theme-schema.js +1 -1
  143. package/dist/slices/document/theme.js +49 -3
  144. package/dist/slices/episodes/summarize.js +2 -2
  145. package/dist/slices/estimate/index.js +28 -22
  146. package/dist/slices/estimate/requests.js +4 -4
  147. package/dist/slices/fixes/rules.js +3 -1
  148. package/dist/slices/fonts/catalog.js +2 -1
  149. package/dist/slices/fonts/coverage.js +2 -1
  150. package/dist/slices/images/appearance.js +2 -1
  151. package/dist/slices/images/scenes.js +2 -1
  152. package/dist/slices/keywords/digest.js +2 -1
  153. package/dist/slices/keywords/rules.js +2 -2
  154. package/dist/slices/keywords/specs.js +5 -5
  155. package/dist/slices/keywords/values.js +1 -1
  156. package/dist/slices/library/transfer.js +3 -3
  157. package/dist/slices/loudness/loudnorm.js +2 -1
  158. package/dist/slices/loudness/model.js +3 -2
  159. package/dist/slices/loudness/sentence-level.js +2 -1
  160. package/dist/slices/model-checks/rules.js +13 -0
  161. package/dist/slices/model-upkeep/model.js +1 -1
  162. package/dist/slices/model-upkeep/switch.js +3 -3
  163. package/dist/slices/model-upkeep/usage.js +13 -2
  164. package/dist/slices/narration/breaths.js +2 -1
  165. package/dist/slices/narration/clean-pieces.js +2 -1
  166. package/dist/slices/narration/pauses-model.js +3 -2
  167. package/dist/slices/narration/pronunciation-check.js +226 -0
  168. package/dist/slices/narration/pronunciation.js +7 -2
  169. package/dist/slices/narration/pronunciations-library.js +94 -0
  170. package/dist/slices/notifications/subject.js +2 -2
  171. package/dist/slices/onboarding/copy.js +19 -2
  172. package/dist/slices/onboarding/first-run.js +13 -38
  173. package/dist/slices/onboarding/install.js +2 -1
  174. package/dist/slices/onboarding/model.js +26 -21
  175. package/dist/slices/onboarding/preferred-model.js +14 -0
  176. package/dist/slices/onboarding/quick-short.js +103 -64
  177. package/dist/slices/onboarding/sample.js +4 -1
  178. package/dist/slices/onboarding/state.js +7 -0
  179. package/dist/slices/onboarding/template.js +6 -5
  180. package/dist/slices/outputs/add.js +3 -2
  181. package/dist/slices/outputs/service.js +2 -2
  182. package/dist/slices/patch-notes/library.js +1 -1
  183. package/dist/slices/play-drafts/readiness.js +3 -3
  184. package/dist/slices/play-drafts/review-inputs.js +1 -1
  185. package/dist/slices/play-drafts/service.js +17 -7
  186. package/dist/slices/project-templates/next-chapter.js +2 -1
  187. package/dist/slices/project-templates/provider-defaults.js +22 -0
  188. package/dist/slices/project-templates/setup-sections.js +26 -0
  189. package/dist/slices/project-templates/setup.js +6 -1
  190. package/dist/slices/rebuild/preview-details.js +5 -5
  191. package/dist/slices/rebuild/recipe-preparation.js +1 -1
  192. package/dist/slices/rebuild/recipe-save.js +2 -2
  193. package/dist/slices/rebuild/recipe-text.js +16 -4
  194. package/dist/slices/rebuild/recipe-validation.js +5 -4
  195. package/dist/slices/rebuild/recipe-voices.js +3 -2
  196. package/dist/slices/rebuild/recipe-work.js +4 -4
  197. package/dist/slices/rebuild/recovery.js +6 -6
  198. package/dist/slices/rebuild/remake-outdated.js +5 -5
  199. package/dist/slices/rebuild/review-redo.js +1 -1
  200. package/dist/slices/rebuild/runtime-animate.js +2 -2
  201. package/dist/slices/rebuild/runtime-article.js +4 -3
  202. package/dist/slices/rebuild/runtime-document.js +5 -4
  203. package/dist/slices/rebuild/runtime-export-bed.js +2 -1
  204. package/dist/slices/rebuild/runtime-export-edit.js +2 -1
  205. package/dist/slices/rebuild/runtime-export-inputs.js +5 -4
  206. package/dist/slices/rebuild/runtime-export-short.js +1 -1
  207. package/dist/slices/rebuild/runtime-export.js +4 -3
  208. package/dist/slices/rebuild/runtime-image.js +4 -3
  209. package/dist/slices/rebuild/runtime-local.js +3 -2
  210. package/dist/slices/rebuild/runtime-narration-text.js +3 -2
  211. package/dist/slices/rebuild/runtime-provider.js +7 -6
  212. package/dist/slices/rebuild/runtime-review.js +6 -5
  213. package/dist/slices/rebuild/runtime-shorts.js +6 -5
  214. package/dist/slices/rebuild/runtime-store.js +17 -2
  215. package/dist/slices/rebuild/runtime-subtitles.js +13 -12
  216. package/dist/slices/rebuild/runtime-voices.js +2 -1
  217. package/dist/slices/rebuild/runtime-youtube.js +3 -2
  218. package/dist/slices/rebuild/service-readiness.js +11 -3
  219. package/dist/slices/research/synthesis.js +3 -2
  220. package/dist/slices/reviews/external-model.js +1 -1
  221. package/dist/slices/reviews/holds.js +5 -4
  222. package/dist/slices/reviews/model.js +8 -5
  223. package/dist/slices/reviews/rules.js +3 -3
  224. package/dist/slices/reviews/verdict.js +2 -1
  225. package/dist/slices/revisions/image-plan.js +1 -1
  226. package/dist/slices/revisions/mutation-assets.js +2 -1
  227. package/dist/slices/revisions/mutations.js +3 -2
  228. package/dist/slices/schedules/agenda.js +7 -5
  229. package/dist/slices/schedules/design-check.js +4 -4
  230. package/dist/slices/schedules/design-run.js +3 -3
  231. package/dist/slices/schedules/repo.js +6 -5
  232. package/dist/slices/schedules/schema.js +3 -0
  233. package/dist/slices/schedules/topics.js +5 -5
  234. package/dist/slices/settings/first-run.js +6 -32
  235. package/dist/slices/settings/health.js +30 -15
  236. package/dist/slices/settings/key-test.js +1 -1
  237. package/dist/slices/settings/model.js +4 -2
  238. package/dist/slices/settings/playback.js +2 -2
  239. package/dist/slices/shorts/keep.js +2 -1
  240. package/dist/slices/shorts/pick.js +4 -3
  241. package/dist/slices/shorts/prompts.js +2 -1
  242. package/dist/slices/shorts/render.js +3 -2
  243. package/dist/slices/social/media.js +5 -4
  244. package/dist/slices/social/pack.js +1 -1
  245. package/dist/slices/social/post-prompt.js +2 -1
  246. package/dist/slices/social/reshape.js +2 -2
  247. package/dist/slices/social/serve.js +1 -1
  248. package/dist/slices/social/service.js +3 -2
  249. package/dist/slices/social/state.js +1 -1
  250. package/dist/slices/storage/backup-import.js +2 -2
  251. package/dist/slices/storage/portable.js +1 -1
  252. package/dist/slices/storage/trim.js +23 -13
  253. package/dist/slices/studio/calendar.js +3 -1
  254. package/dist/slices/studio/disclosure.js +4 -4
  255. package/dist/slices/studio/model.js +1 -1
  256. package/dist/slices/studio/pack.js +9 -8
  257. package/dist/slices/studio/plan.js +1 -1
  258. package/dist/slices/studio/settings.js +6 -6
  259. package/dist/slices/studio/videos.js +23 -4
  260. package/dist/slices/style-preview/render.js +3 -3
  261. package/dist/slices/style-preview/schema.js +1 -1
  262. package/dist/slices/subtitles/captions.js +1 -1
  263. package/dist/slices/template-studio/anatomy-view.js +46 -3
  264. package/dist/slices/template-studio/bakeoff.js +3 -3
  265. package/dist/slices/template-studio/budget.js +6 -3
  266. package/dist/slices/template-studio/cards.js +78 -0
  267. package/dist/slices/template-studio/checks.js +99 -50
  268. package/dist/slices/template-studio/for-all.js +5 -5
  269. package/dist/slices/template-studio/free-video.js +1 -1
  270. package/dist/slices/template-studio/free.js +5 -5
  271. package/dist/slices/template-studio/model.js +19 -15
  272. package/dist/slices/template-studio/pack-import.js +14 -14
  273. package/dist/slices/template-studio/pack.js +9 -7
  274. package/dist/slices/template-studio/paid.js +12 -5
  275. package/dist/slices/template-studio/quote.js +18 -11
  276. package/dist/slices/template-studio/ready.js +7 -7
  277. package/dist/slices/template-studio/runner.js +1 -1
  278. package/dist/slices/template-studio/save.js +12 -12
  279. package/dist/slices/template-studio/say-it-as.js +3 -3
  280. package/dist/slices/template-studio/schedule-gate.js +1 -1
  281. package/dist/slices/template-studio/schedule-price.js +3 -1
  282. package/dist/slices/template-studio/schedule-sample.js +5 -1
  283. package/dist/slices/template-studio/schema.js +25 -0
  284. package/dist/slices/template-studio/service.js +3 -1
  285. package/dist/slices/template-studio/status.js +26 -9
  286. package/dist/slices/template-studio/topic.js +3 -3
  287. package/dist/slices/thumbnail/by-llm.js +1 -1
  288. package/dist/slices/uploads/repo.js +37 -1
  289. package/dist/slices/uploads/rules.js +3 -2
  290. package/dist/slices/video/edit-list.js +2 -2
  291. package/dist/slices/video/edit-settings.js +1 -1
  292. package/dist/slices/video/encoding.js +14 -5
  293. package/dist/slices/video/ffmpeg.js +2 -2
  294. package/dist/slices/video/plan.js +2 -1
  295. package/dist/slices/video/slideshow.js +1 -1
  296. package/dist/slices/voices/attribution.js +3 -2
  297. package/dist/slices/voices/model.js +9 -8
  298. package/dist/slices/voices/script.js +2 -1
  299. package/dist/slices/youtube/answer.js +2 -1
  300. package/dist/slices/youtube/edits-repo.js +1 -1
  301. package/dist/tutorials/AI-CLIs.md +25 -25
  302. package/dist/tutorials/Agents-MCP.md +32 -32
  303. package/dist/tutorials/Assistant.md +5 -5
  304. package/dist/tutorials/Backups.md +7 -7
  305. package/dist/tutorials/Calendar.md +17 -25
  306. package/dist/tutorials/Cast-Library.md +19 -19
  307. package/dist/tutorials/Channels.md +40 -35
  308. package/dist/tutorials/Costs-and-Run-Cost.md +38 -40
  309. package/dist/tutorials/Create-Article.md +114 -0
  310. package/dist/tutorials/Create-Extras.md +112 -0
  311. package/dist/tutorials/Create-Images.md +165 -0
  312. package/dist/tutorials/Create-Narration.md +178 -0
  313. package/dist/tutorials/Create-Overview.md +244 -0
  314. package/dist/tutorials/Create-Video-and-Style.md +144 -0
  315. package/dist/tutorials/Docker.md +3 -3
  316. package/dist/tutorials/Document-Themes.md +32 -32
  317. package/dist/tutorials/Editing-a-Project.md +95 -94
  318. package/dist/tutorials/FAQ.md +18 -18
  319. package/dist/tutorials/First-Launch-and-Welcome.md +50 -64
  320. package/dist/tutorials/Home-and-Projects.md +101 -76
  321. package/dist/tutorials/Home.md +33 -33
  322. package/dist/tutorials/Install.md +13 -13
  323. package/dist/tutorials/Intros-and-Outros.md +33 -34
  324. package/dist/tutorials/Keyboard-Shortcuts-and-Command-Palette.md +62 -58
  325. package/dist/tutorials/Keywords-Slopify-Fills.md +21 -21
  326. package/dist/tutorials/Models.md +27 -27
  327. package/dist/tutorials/Multiple-Voices.md +41 -40
  328. package/dist/tutorials/Narration-Aliases-and-Glossary.md +101 -19
  329. package/dist/tutorials/Notifications.md +21 -17
  330. package/dist/tutorials/Other-Languages.md +15 -15
  331. package/dist/tutorials/PDF-Documents.md +38 -29
  332. package/dist/tutorials/Posting-to-Social-Sites.md +12 -12
  333. package/dist/tutorials/Project-Page.md +118 -120
  334. package/dist/tutorials/Prompts-and-Templates.md +84 -0
  335. package/dist/tutorials/Prompts.md +45 -45
  336. package/dist/tutorials/Providers-and-Keys.md +105 -98
  337. package/dist/tutorials/Publishing-to-YouTube.md +31 -31
  338. package/dist/tutorials/Recovery-and-Retries.md +31 -31
  339. package/dist/tutorials/Remote-Access.md +2 -2
  340. package/dist/tutorials/Reviews-and-Checkpoints.md +90 -93
  341. package/dist/tutorials/Schedules.md +27 -28
  342. package/dist/tutorials/Settings-Reference.md +183 -204
  343. package/dist/tutorials/Shorts.md +34 -33
  344. package/dist/tutorials/Start-at-Login.md +3 -3
  345. package/dist/tutorials/Studio-Extension.md +22 -24
  346. package/dist/tutorials/Template-Studio.md +45 -42
  347. package/dist/tutorials/Templates.md +50 -79
  348. package/dist/tutorials/Trash.md +4 -4
  349. package/dist/tutorials/Troubleshooting.md +44 -44
  350. package/dist/tutorials/Uninstalling-and-Moving.md +5 -5
  351. package/dist/tutorials/Updating-and-Patch-Notes.md +18 -18
  352. package/dist/tutorials/Video-Editing.md +34 -34
  353. package/dist/tutorials/Where-Your-Files-Live.md +10 -10
  354. package/dist/tutorials/YouTube-Description.md +26 -26
  355. package/dist/tutorials/Your-First-Short.md +35 -35
  356. package/dist/tutorials/_Sidebar.md +15 -15
  357. package/dist/web/assets/index-BNftKQrA.css +1 -0
  358. package/dist/web/assets/index-CQASqwjT.js +460 -0
  359. package/dist/web/assets/pdf-BKh8f7R6.js +56 -0
  360. package/dist/web/assets/pdf.worker.min-BmVo14Nb.mjs +28 -0
  361. package/dist/web/assets/pdf.worker.min-D7i8o3wR.js +1 -0
  362. package/dist/web/index.html +2 -2
  363. package/package.json +2 -2
  364. package/dist/tutorials/Library-Overview.md +0 -76
  365. package/dist/tutorials/Play-Images.md +0 -165
  366. package/dist/tutorials/Play-Narration.md +0 -167
  367. package/dist/tutorials/Play-Outputs.md +0 -109
  368. package/dist/tutorials/Play-Overview.md +0 -227
  369. package/dist/tutorials/Play-Title-and-Article.md +0 -136
  370. package/dist/tutorials/Play-Video-and-Style.md +0 -143
  371. package/dist/web/assets/index-CkAI_2E7.css +0 -1
  372. package/dist/web/assets/index-DcD0cdfo.js +0 -334
  373. package/dist/web/assets/pdf-TODRwF31.js +0 -54
  374. package/dist/web/assets/pdf.worker.min-Dswkl-cV.mjs +0 -29
  375. package/dist/web/assets/pdf.worker.min-zC6nfbxG.js +0 -1
@@ -11,7 +11,7 @@ import { findRevisionDownload } from "../revisions/downloads.js";
11
11
  import { currentRevisionId } from "../revisions/repo.js";
12
12
  import { getRevisionView } from "../revisions/view.js";
13
13
  import { listVoices } from "../settings/repo.js";
14
- import { checkedVersion } from "./gate.js";
14
+ import { runnableVersion } from "./gate.js";
15
15
  import { gatesOf, sampleOf } from "./repo.js";
16
16
  // A template pack: a template set up in the studio, as one zip someone else imports and looks
17
17
  // at before using it. It carries the version marked ready (its prompts' and intros' text, the
@@ -146,22 +146,24 @@ export async function exportPack(deps, templateId, samples = (id) => sampleFiles
146
146
  return {
147
147
  ok: false,
148
148
  reason: "not-found",
149
- message: "This template no longer exists; it may have been deleted. Go to Library → Templates to see your templates.",
149
+ message: "This template no longer exists; it may have been deleted. Go to Templates to see your templates.",
150
150
  };
151
- const runs = checkedVersion(gatesOf(deps.db, templateId), head.version);
151
+ // What its schedules run: the newest version, or once it was first approved for schedules,
152
+ // the newest approved one.
153
+ const runs = runnableVersion(gatesOf(deps.db, templateId), head.version);
152
154
  const template = runs === undefined ? undefined : templateById(deps.db, templateId, runs);
153
155
  if (template === undefined)
154
156
  return {
155
157
  ok: false,
156
158
  reason: "not-ready",
157
- message: "No version of this template is marked ready, so there is nothing to put in a pack: a pack carries what you checked. Press Mark ready in the studio, then Export pack.",
159
+ message: "No version of this template is approved for schedules, so there is nothing to put in the file: it carries what its schedules run. Approve a version in Edit template, then export it again.",
158
160
  };
159
161
  const setup = templateSetup(deps, template.document);
160
162
  if (!setup.ok)
161
163
  return {
162
164
  ok: false,
163
165
  reason: "invalid-input",
164
- message: `Version ${String(template.version)} names a prompt or intro/outro that was deleted, so its text can't go into the pack. Choose another in the studio, Save, Mark ready, then Export pack.`,
166
+ message: `Version ${String(template.version)} names a prompt or intro/outro that was deleted, so its text can't go into the file. Choose another in Edit template, Save, then export it again.`,
165
167
  };
166
168
  const document = setup.value;
167
169
  const { audio } = document.form;
@@ -187,7 +189,7 @@ export async function exportPack(deps, templateId, samples = (id) => sampleFiles
187
189
  return {
188
190
  ok: false,
189
191
  reason: "invalid-input",
190
- message: "Something in this template looks like an API key or a login token (a keyword's value or a prompt's text), so no pack was made: a pack is meant to be shared. Remove it in the studio, Save, Mark ready, then press Export pack again.",
192
+ message: "Something in this template looks like an API key or a login token (a keyword's value or a prompt's text), so no file was made: it is meant to be shared. Remove it in Edit template, Save, then export it again.",
191
193
  };
192
194
  const entries = { "manifest.json": [strToU8(json), { level: 6 }] };
193
195
  for (const file of named) {
@@ -197,7 +199,7 @@ export async function exportPack(deps, templateId, samples = (id) => sampleFiles
197
199
  return {
198
200
  ok: true,
199
201
  value: {
200
- filename: `${slug(template.name)}.slopify-pack.zip`,
202
+ filename: `${slug(template.name)}.slopify-template.zip`,
201
203
  bytes: zipSync(entries),
202
204
  version: template.version,
203
205
  },
@@ -1,6 +1,7 @@
1
1
  import { setTimeout as sleep } from "node:timers/promises";
2
2
  import { transact } from "../../kernel/db/tx.js";
3
3
  import { internalError } from "../../kernel/errors.js";
4
+ import { editSettings } from "../../kernel/ports/settings-places.js";
4
5
  import { derive } from "../../kernel/runner/graph.js";
5
6
  import { pendingAdmitted } from "../admission/admitted-work.js";
6
7
  import { projectExists, projectPaused, stagesOf } from "../admission/repo.js";
@@ -31,8 +32,11 @@ export function holdReason(deps, job, cost) {
31
32
  if (cost.unknown > 0 && !deps.acks.accepted(job.templateId, job.templateVersion))
32
33
  return `Waiting: ${String(cost.unknown)} ${cost.unknown === 1 ? "step of this sample has" : "steps of this sample have"} no known price. Press Make sample to accept that and make it.`;
33
34
  const room = budgetRoom(deps.db, deps.now, deps.timeZone);
35
+ // Tomorrow brings a new budget, never the plan setting.
34
36
  if (!fitsBudget(room, cost.high, cost.usesPlan))
35
- return `Waiting: ${budgetWords(room, cost, "this sample")}, or press Make sample tomorrow.`;
37
+ return cost.usesPlan && !room.plans
38
+ ? `Waiting: ${budgetWords(room, cost, "this sample")}.`
39
+ : `Waiting: ${budgetWords(room, cost, "this sample")}, or press Make sample tomorrow.`;
36
40
  return undefined;
37
41
  }
38
42
  // Whether the sample project still has work going: a stage running or waiting, or work admitted
@@ -88,13 +92,16 @@ function outcome(db, projectId) {
88
92
  return { failed: "The sample stopped before it finished. Press Make sample to make it again." };
89
93
  return { failed: null };
90
94
  }
95
+ // A step's sentence sends a project to its settings for another model; a template's section
96
+ // changes its model with Change on its "Made with …" line.
97
+ const modelPlaces = new RegExp(`choose another (model|image model) in (?:${[editSettings.writer, editSettings.narration, editSettings.images].join("|")})`, "g");
91
98
  // A step's sentence names a project's Try again (on its stage); a studio's own button for it
92
99
  // is another: Make sample for a sample, Compare again for a model comparison. `then` is what
93
100
  // follows the button's name in the sentence.
94
101
  export function studioWords(reason, button = "Make sample", then = " to try again") {
95
102
  return reason
96
- .replace(/choose another model in the Providers section of Edit project/g, "choose another model with Change on that section's “Made with …” line")
97
- .replace(/(\b[UuPp](?:se|ress) )?Try again(?: on [A-Z][a-z]+)?/g, (_, verb, at) => {
103
+ .replace(modelPlaces, "choose another $1 with Change on that section's “Made with …” line")
104
+ .replace(/(\b[UuPp](?:se|ress) )?Try again(?: on [A-Z][A-Za-z]+)?/g, (_, verb, at) => {
98
105
  const opens = verb === undefined ? /(^|[.;:!?]\s+)$/.test(reason.slice(0, at)) : /^[UP]/.test(verb);
99
106
  return `${opens ? "Press" : "press"} ${button}${then}`;
100
107
  });
@@ -102,7 +109,7 @@ export function studioWords(reason, button = "Make sample", then = " to try agai
102
109
  const refusedSentence = {
103
110
  conflict: "The sample project changed while the sample was being set up. Press Make sample again.",
104
111
  "stale-preview": "What the sample needed changed while it was being set up. Press Make sample again.",
105
- readiness: "A provider this template uses isn't set up, so the sample can't start. Set it up in Settings → Providers, then press Make sample.",
112
+ readiness: "A provider this template uses isn't set up, so the sample can't start. Set it up in Settings → Keys & services, then press Make sample.",
106
113
  "cost-ack-required": "Some steps of this sample have no known price. Press Make sample to accept that and make it.",
107
114
  };
108
115
  export function refused(reason, fields) {
@@ -127,7 +134,7 @@ export async function makePaidSample(deps, job, signal) {
127
134
  if (!resolved.ok) {
128
135
  mark({
129
136
  state: "failed",
130
- heldReason: `${resolved.problem} Change it in the studio, then press Save.`,
137
+ heldReason: `${resolved.problem} Change it, press Save, then Make sample.`,
131
138
  });
132
139
  return;
133
140
  }
@@ -1,14 +1,20 @@
1
1
  import { projectExists } from "../admission/repo.js";
2
2
  import { estimateRun } from "../estimate/index.js";
3
3
  import { resolvePlayReview } from "../play-drafts/review.js";
4
+ import { withProviderDefaults } from "../project-templates/provider-defaults.js";
4
5
  import { templateById } from "../project-templates/repo.js";
6
+ import { readProviderDefaults } from "../settings/first-run.js";
5
7
  import { budgetRoom, fitsBudget } from "./budget.js";
8
+ import { sectionLabel } from "./model.js";
6
9
  import { sampleOf } from "./repo.js";
7
10
  import { sampleDocument } from "./scope.js";
8
11
  import { affectedSections } from "./sections.js";
9
12
  import { defaultSampleTopic } from "./service.js";
10
- // The sample's single run as Play's review resolves it, on a draft that is never saved.
11
- export async function resolveSampleRun(review, document, fullScript) {
13
+ // The sample's single run as Play's review resolves it, on a draft that is never saved. Like a
14
+ // video made from the template, it uses this computer's writer and image service where the
15
+ // template chose none.
16
+ export async function resolveSampleRun(review, scoped, fullScript) {
17
+ const document = withProviderDefaults(scoped, readProviderDefaults(review.db));
12
18
  const at = review.clock.now().toISOString();
13
19
  const view = {
14
20
  draft: { id: review.uuid(), version: 1, createdAt: at, updatedAt: at, document },
@@ -22,14 +28,14 @@ export async function resolveSampleRun(review, document, fullScript) {
22
28
  return {
23
29
  ok: false,
24
30
  problem: resolved.fields.length === 0
25
- ? "Play's review refused this setup, so no sample can be made. Open the template in Play (Use in Play) to see why."
31
+ ? "Create's review refused this setup, so no sample can be made. Press Make a video to see why on Create."
26
32
  : `No sample can be made of this setup: ${[...new Set(resolved.fields.map(fieldSentence))].join(" ")}`,
27
33
  };
28
34
  const run = resolved.value.runs[0];
29
35
  if (run === undefined)
30
36
  return {
31
37
  ok: false,
32
- problem: "Play's review found no video to make, so no sample can be made.",
38
+ problem: "Create's review found no video to make, so no sample can be made.",
33
39
  };
34
40
  return {
35
41
  ok: true,
@@ -40,16 +46,16 @@ export async function resolveSampleRun(review, document, fullScript) {
40
46
  // A refused field in the studio's words: an empty keyword is named, with where it is filled.
41
47
  export function fieldSentence(field) {
42
48
  if (field.field.startsWith("values."))
43
- return `The keyword {{${field.field.slice("values.".length)}}} is empty. Fill it in Subject & research.`;
49
+ return `The keyword {{${field.field.slice("values.".length)}}} is empty. Fill it in ${sectionLabel.subject}.`;
44
50
  return field.message;
45
51
  }
46
52
  const sectionOfRow = {
47
53
  Research: "subject",
48
54
  Article: "script",
49
55
  "Intro / outro text": "script",
50
- "Narration descriptions": "voice",
51
- "Narration Preparation": "voice",
52
- "Delivery cue overhead": "voice",
56
+ "Describing tables and figures": "voice",
57
+ "Narration preparation": "voice",
58
+ "Delivery cues": "voice",
53
59
  Narration: "voice",
54
60
  "Speaker split": "voice",
55
61
  "Conversation script": "voice",
@@ -58,13 +64,14 @@ const sectionOfRow = {
58
64
  "Establishing image": "pictures",
59
65
  Thumbnail: "thumbnail",
60
66
  "Thumbnail prompt": "thumbnail",
61
- "Export / subtitles": "video",
67
+ Video: "video",
68
+ "Audio export": "video",
62
69
  "Animated images": "video",
63
70
  "YouTube description": "upload",
64
71
  Posts: "upload",
65
72
  Shorts: "shorts",
66
73
  Reviews: "reviews",
67
- Document: "document",
74
+ PDF: "document",
68
75
  };
69
76
  export function totals(rows) {
70
77
  const priced = rows.filter((row) => row.high !== null && row.high > 0);
@@ -87,7 +94,7 @@ export async function quoteSample(deps, templateId, ask) {
87
94
  return {
88
95
  ok: false,
89
96
  reason: "not-found",
90
- message: "This template no longer exists; it may have been deleted. Go to Library → Templates to see your templates.",
97
+ message: "This template no longer exists; it may have been deleted. Go to Templates to see your templates.",
91
98
  };
92
99
  const document = ask.document ?? template.document;
93
100
  const sample = sampleOf(deps.db, templateId);
@@ -13,11 +13,11 @@ export const markReadySchema = z
13
13
  .readonly();
14
14
  const checkLabel = {
15
15
  keywords: "Keywords",
16
- providers: "Providers",
16
+ providers: "Services",
17
17
  voice: "Voice",
18
- "release-slots": "Release slots",
18
+ "release-slots": "Release times",
19
19
  "monthly-cost": "Monthly cost",
20
- answerers: "Checkpoints answered",
20
+ answerers: "Pauses answered",
21
21
  };
22
22
  // The section states Mark ready accepts: nothing about them is left to look at.
23
23
  const settled = new Set(["current", "off", "provided"]);
@@ -43,12 +43,12 @@ function sentenceOf(problem) {
43
43
  const fix = problem.fix === undefined ? "" : ` ${problem.fix.label}.`;
44
44
  return `${problem.label}: ${problem.sentence}${fix}`;
45
45
  }
46
- const missingTemplate = "This template no longer exists; it may have been deleted. Go to Library → Templates to see your templates.";
46
+ const missingTemplate = "This template no longer exists; it may have been deleted. Go to Templates to see your templates.";
47
47
  function notNewest(version, head) {
48
48
  return {
49
49
  ok: false,
50
50
  reason: "conflict",
51
- message: `Version ${String(version)} is no longer the newest: the template is at version ${String(head)}. Look over version ${String(head)}'s samples, then press Mark ready.`,
51
+ message: `Version ${String(version)} is no longer the newest: the template is at version ${String(head)}. Look over version ${String(head)}'s samples, then approve it for schedules.`,
52
52
  problems: [],
53
53
  };
54
54
  }
@@ -58,7 +58,7 @@ export async function markReady(deps, templateId, raw) {
58
58
  return {
59
59
  ok: false,
60
60
  reason: "invalid-input",
61
- message: "The studio sent a request Slopify couldn't read. Reload the page and try again.",
61
+ message: "Edit template sent a request Slopify couldn't read. Reload the page and try again.",
62
62
  problems: [],
63
63
  };
64
64
  const { version } = parsed.data;
@@ -83,7 +83,7 @@ export async function markReady(deps, templateId, raw) {
83
83
  return {
84
84
  ok: false,
85
85
  reason: "not-ready",
86
- message: `Version ${String(version)} can't be marked ready yet. ${problems.map(sentenceOf).join(" ")}`,
86
+ message: `Version ${String(version)} can't be approved for schedules yet. ${problems.map(sentenceOf).join(" ")}`,
87
87
  problems,
88
88
  };
89
89
  return transact(deps.db, () => {
@@ -56,7 +56,7 @@ export function createSampleRunner(deps) {
56
56
  recoverJobs(deps.db);
57
57
  loop = run().catch((error) => {
58
58
  deps.log.write("error", "template-studio.runner", {
59
- detail: `The template studio's sample loop stopped: ${errorText(error)}. Restart Slopify to make samples again.`,
59
+ detail: `The loop that makes Edit template's samples stopped: ${errorText(error)}. Restart Slopify to make samples again.`,
60
60
  });
61
61
  });
62
62
  },
@@ -64,7 +64,7 @@ export async function saveStudio(deps, id, raw) {
64
64
  return {
65
65
  ok: false,
66
66
  reason: "invalid-input",
67
- message: "The studio sent a Save Slopify couldn't read. Reload the page and press Save again.",
67
+ message: "Edit template sent a Save Slopify couldn't read. Reload the page and press Save again.",
68
68
  };
69
69
  const input = parsed.data;
70
70
  const head = templateById(deps.db, id);
@@ -72,7 +72,7 @@ export async function saveStudio(deps, id, raw) {
72
72
  return {
73
73
  ok: false,
74
74
  reason: "not-found",
75
- message: "This template no longer exists; it may have been deleted. Go to Library → Templates to see your templates.",
75
+ message: "This template no longer exists; it may have been deleted. Go to Templates to see your templates.",
76
76
  };
77
77
  const update = {
78
78
  id,
@@ -89,7 +89,7 @@ export async function saveStudio(deps, id, raw) {
89
89
  return {
90
90
  ok: false,
91
91
  reason: "conflict",
92
- message: "This Save was already sent with different settings. Reload the studio to see what was saved.",
92
+ message: "This Save was already sent with different settings. Reload the page to see what was saved.",
93
93
  };
94
94
  return {
95
95
  ok: true,
@@ -105,7 +105,7 @@ export async function saveStudio(deps, id, raw) {
105
105
  return {
106
106
  ok: false,
107
107
  reason: "conflict",
108
- message: `This template changed since you opened it: it is now version ${String(head.version)}. Reload the studio to see the change, then make yours again.`,
108
+ message: `This template changed since you opened it: it is now version ${String(head.version)}. Reload the page to see the change, then make yours again.`,
109
109
  };
110
110
  let quote = null;
111
111
  if (input.sample === "make") {
@@ -117,28 +117,28 @@ export async function saveStudio(deps, id, raw) {
117
117
  return {
118
118
  ok: false,
119
119
  reason: "invalid-input",
120
- message: `${quote.problem} Press Save without a sample to keep the change.`,
120
+ message: `${quote.problem} Save keeps the change without a sample.`,
121
121
  quote,
122
122
  };
123
123
  if (exceedsAcknowledged(quote.high, input.acknowledgedHigh))
124
124
  return {
125
125
  ok: false,
126
126
  reason: "cost-changed",
127
- message: `The sample now costs about ${money(quote.high)}, more than the ${money(input.acknowledgedHigh)} you saw. Press Save again to accept the new price, or Save without a sample.`,
127
+ message: `The sample now costs about ${money(quote.high)}, more than the ${money(input.acknowledgedHigh)} you saw. Press Make sample again to accept the new price.`,
128
128
  quote,
129
129
  };
130
130
  if (quote.unknown > 0 && input.acknowledgedUnknown !== true)
131
131
  return {
132
132
  ok: false,
133
133
  reason: "cost-changed",
134
- message: `${String(quote.unknown)} ${quote.unknown === 1 ? "step of this sample has" : "steps of this sample have"} no known price. Tick that you accept unknown costs, then press Save again, or Save without a sample.`,
134
+ message: `${String(quote.unknown)} ${quote.unknown === 1 ? "step of this sample has" : "steps of this sample have"} no known price. Tick that you accept unknown costs, then press Make sample again.`,
135
135
  quote,
136
136
  };
137
137
  if (!quote.fits)
138
138
  return {
139
139
  ok: false,
140
140
  reason: "over-budget",
141
- message: `${budgetWords(quote.budget, quote, "This sample")}, or press Save without a sample.`,
141
+ message: `${budgetWords(quote.budget, quote, "This sample")}.`,
142
142
  quote,
143
143
  };
144
144
  }
@@ -154,7 +154,7 @@ export async function saveStudio(deps, id, raw) {
154
154
  message: saved.reason === "missing-prompt"
155
155
  ? "A prompt or intro/outro entry this template uses was deleted. Choose another in the template, then press Save again."
156
156
  : saved.reason === "conflict"
157
- ? "This template changed while you were editing. Reload the studio to see the latest, then make your change again."
157
+ ? "This template changed while you were editing. Reload the page to see the latest, then make your change again."
158
158
  : "Some template settings are not valid, for example an image you supplied yourself. Check the template's settings and press Save again.",
159
159
  };
160
160
  const template = saved.value;
@@ -201,20 +201,20 @@ export function requestSample(deps, id, raw) {
201
201
  return {
202
202
  ok: false,
203
203
  reason: "invalid-input",
204
- message: "The studio sent a request Slopify couldn't read. Reload the page and press Make sample again.",
204
+ message: "Edit template sent a request Slopify couldn't read. Reload the page and press Make sample again.",
205
205
  };
206
206
  const head = templateById(deps.db, id);
207
207
  if (head === undefined)
208
208
  return {
209
209
  ok: false,
210
210
  reason: "not-found",
211
- message: "This template no longer exists; it may have been deleted. Go to Library → Templates to see your templates.",
211
+ message: "This template no longer exists; it may have been deleted. Go to Templates to see your templates.",
212
212
  };
213
213
  if (head.version !== parsed.data.version)
214
214
  return {
215
215
  ok: false,
216
216
  reason: "conflict",
217
- message: `This template is now version ${String(head.version)}, not the version ${String(parsed.data.version)} you saw. Reload the studio, then press Make sample.`,
217
+ message: `This template is now version ${String(head.version)}, not the version ${String(parsed.data.version)} you saw. Reload the page, then press Make sample.`,
218
218
  };
219
219
  const at = deps.clock.now().toISOString();
220
220
  if (parsed.data.acknowledgedUnknown === true)
@@ -87,7 +87,7 @@ function sampleTexts(deps, templateId) {
87
87
  glossary: textOf(deps, sample.projectId, "glossary"),
88
88
  };
89
89
  }
90
- const missingTemplate = "This template no longer exists; it may have been deleted. Go to Library → Templates to see your templates.";
90
+ const missingTemplate = "This template no longer exists; it may have been deleted. Go to Templates to see your templates.";
91
91
  export function pronunciationsOf(deps, templateId) {
92
92
  const head = templateById(deps.db, templateId);
93
93
  if (head === undefined)
@@ -125,7 +125,7 @@ export const sayItAsSchema = z
125
125
  })
126
126
  .strict()
127
127
  .readonly();
128
- const aliasesOff = "This template's voice doesn't use Library → Narration aliases, so “Say it as” would change nothing. Turn on Use narration aliases in Voice → Fine-tune, press Save, then try again.";
128
+ const aliasesOff = "This template's voice doesn't use your pronunciations (Settings → Voices → Pronunciation), so “Say it as” would change nothing. Turn on Use your pronunciation list under All Narration settings → More narration settings, press Save, then try again.";
129
129
  export function quoteSayItAs(deps, templateId, ask) {
130
130
  const head = templateById(deps.db, templateId);
131
131
  if (head === undefined)
@@ -222,7 +222,7 @@ export async function sayItAs(deps, templateId, raw) {
222
222
  return {
223
223
  ok: false,
224
224
  reason: "invalid-input",
225
- message: `The alias wasn't saved: ${saved.fields.map((field) => field.message).join(" ")} Fix it in Library → Narration aliases, then try again.`,
225
+ message: `The alias wasn't saved: ${saved.fields.map((field) => field.message).join(" ")} Fix it in Settings → Voices → Pronunciation, then try again.`,
226
226
  };
227
227
  if (quote.lines === 0)
228
228
  return { ok: true, value: { alias, queued: false, quote } };
@@ -30,5 +30,5 @@ export function notReadySentence(db, templateId) {
30
30
  if (gate === undefined || gate.runs !== null)
31
31
  return undefined;
32
32
  const name = templateById(db, templateId)?.name ?? "This schedule's template";
33
- return `Not started: ${name} has no version marked ready. Open it in Library → Templates → Open studio and press Mark ready.`;
33
+ return `Not started: ${name} has no version approved for schedules. Open it in Templates → Edit template and choose More → Approve for schedules.`;
34
34
  }
@@ -21,10 +21,12 @@ export async function priceDocument(deps, document) {
21
21
  const review = await resolvePlayReview(deps, view, { unsaved: true });
22
22
  if (!review.ok)
23
23
  return review.fields.length === 0
24
- ? "Play's review refused this setup. Open the template in Play (Use in Play) to see why."
24
+ ? "Create's review refused this setup. Press Make a video on the template to see why on Create."
25
25
  : [...new Set(review.fields.map((field) => field.message))].join(" ");
26
+ const rows = review.value.estimates.flatMap((estimate) => estimate.rows);
26
27
  return {
27
28
  high: review.value.estimates.reduce((sum, estimate) => sum + estimate.high, 0),
28
29
  unknown: review.value.estimates.reduce((sum, estimate) => sum + estimate.unknown, 0),
30
+ unpriced: [...new Set(rows.filter((row) => row.low === null).map((row) => row.stage))],
29
31
  };
30
32
  }
@@ -61,6 +61,7 @@ export function monthlyCost(schedules, prices, now) {
61
61
  let runs = 0;
62
62
  let high = 0;
63
63
  let unknown = 0;
64
+ const unpriced = new Set();
64
65
  let problem = null;
65
66
  for (const schedule of active) {
66
67
  const perMonth = runsPerMonth(schedule, now);
@@ -71,16 +72,19 @@ export function monthlyCost(schedules, prices, now) {
71
72
  problem ??=
72
73
  price === undefined
73
74
  ? `The cost of the runs of ${schedule.name} isn't worked out yet: it is, with the free samples.`
74
- : `The runs of ${schedule.name} couldn't be priced: ${price} Fix it in this studio, press Save, then Make samples.`;
75
+ : `The runs of ${schedule.name} couldn't be priced: ${price} Fix it here, then press Save.`;
75
76
  continue;
76
77
  }
77
78
  high += price.high * perMonth;
78
79
  unknown += price.unknown;
80
+ for (const stage of price.unpriced ?? [])
81
+ unpriced.add(stage);
79
82
  }
80
83
  return {
81
84
  runs: Math.round(runs * 10) / 10,
82
85
  high: Math.round(high * 100) / 100,
83
86
  unknown,
87
+ unpriced: [...unpriced],
84
88
  problem,
85
89
  paused,
86
90
  };
@@ -68,6 +68,7 @@ export const scheduleSampleSchema = z
68
68
  runs: z.number(),
69
69
  high: z.number(),
70
70
  unknown: z.number(),
71
+ unpriced: z.array(z.string()).readonly(),
71
72
  problem: z.string().nullable(),
72
73
  paused: z
73
74
  .object({ schedules: z.number(), runs: z.number(), high: z.number() })
@@ -216,3 +217,27 @@ export const anatomyViewSchema = z
216
217
  })
217
218
  .strict()
218
219
  .readonly();
220
+ // GET /api/template-studio/cards: what each template makes, for the Templates page (`cards.ts`).
221
+ export const templateCardSchema = z
222
+ .object({
223
+ id: z.string(),
224
+ // A picture of what it makes, served by the app, or null when there is none yet.
225
+ still: z.string().nullable(),
226
+ format: z.enum(["16:9", "9:16", "1:1"]),
227
+ // Whether it makes a video at all (a narration-only setup does not).
228
+ video: z.boolean(),
229
+ words: z.number().int().nonnegative().nullable(),
230
+ voice: z
231
+ .object({ provider: z.string(), voiceId: z.string(), name: z.string() })
232
+ .strict()
233
+ .nullable(),
234
+ captions: z.enum(["off", "burn-in", "files"]),
235
+ shorts: z.number().int().nonnegative(),
236
+ pdf: z.boolean(),
237
+ // The keywords each video asks for: the topic keywords of its title ("Topic").
238
+ asks: z.array(z.string()),
239
+ // The schedules that run it now (paused ones too).
240
+ schedules: z.number().int().nonnegative(),
241
+ })
242
+ .strict();
243
+ export const templateCardsSchema = z.object({ cards: z.array(templateCardSchema) }).strict();
@@ -5,6 +5,7 @@ import { snapshotEntry, snapshotPrompt } from "../library/snapshot.js";
5
5
  import { readNotificationUrl } from "../notifications/settings.js";
6
6
  import { templateById } from "../project-templates/repo.js";
7
7
  import { scheduleRows } from "../schedules/repo.js";
8
+ import { readProviderDefaults } from "../settings/first-run.js";
8
9
  import { listVoices, readJsonSetting } from "../settings/repo.js";
9
10
  import { freeChecks } from "./checks.js";
10
11
  import { checkedVersion, gateOf, isGated, runnableVersion } from "./gate.js";
@@ -82,7 +83,7 @@ export function createReadinessCache(load, now = Date.now, ttlMs = 30_000) {
82
83
  return value;
83
84
  };
84
85
  }
85
- const missingTemplate = "This template no longer exists; it may have been deleted. Go to Library → Templates to see your templates.";
86
+ const missingTemplate = "This template no longer exists; it may have been deleted. Go to Templates to see your templates.";
86
87
  // The studio page's content, answered without waiting for the providers' readiness: the
87
88
  // checks that need it are read on their own (`getStudioChecks`).
88
89
  export async function getStudio(deps, id) {
@@ -161,6 +162,7 @@ export async function getStudioChecks(deps, id) {
161
162
  status.id,
162
163
  { name: status.displayName, ready: readinessIsUsable(status.readiness) },
163
164
  ])),
165
+ defaults: readProviderDefaults(deps.db),
164
166
  schedules: schedules.map(fitOf),
165
167
  voices: listVoices(deps.db),
166
168
  monthlyCost: cost,
@@ -36,17 +36,32 @@ function sourceOf(document, section) {
36
36
  function heldText(source, text) {
37
37
  return source === "provide" && text.trim() !== "" ? "held" : source;
38
38
  }
39
+ // One press of Make sample makes every paid section's sample at once, so its sentence names
40
+ // them: "Make sample makes the samples of Title and keywords, Article and Narration together,
41
+ // about the sample topic."
42
+ export function makeSampleSentence(document) {
43
+ const made = paidSections
44
+ .filter((id) => {
45
+ const source = sourceOf(document, id);
46
+ return source !== "off" && source !== "provide";
47
+ })
48
+ .map((id) => sectionLabel[id]);
49
+ if (made.length <= 1)
50
+ return "Make sample shows what this section makes, about the sample topic.";
51
+ const names = `${made.slice(0, -1).join(", ")} and ${made.at(-1) ?? ""}`;
52
+ return `Make sample makes the samples of ${names} together, about the sample topic.`;
53
+ }
39
54
  const offSentence = {
40
55
  subject: "Research is off: the script is written from the topic alone.",
41
- script: "The script is off.",
56
+ script: "The article is off.",
42
57
  voice: "The narration is off.",
43
- pictures: "Pictures are off.",
58
+ pictures: "Images are off.",
44
59
  thumbnail: "The thumbnail is off.",
45
60
  video: "The video is off.",
46
61
  shorts: "Shorts are off.",
47
- document: "The PDF document is off.",
48
- reviews: "No review or checkpoint is on.",
49
- upload: "No video, so there is no upload pack.",
62
+ document: "The PDF is off.",
63
+ reviews: "No pause or AI check is on.",
64
+ upload: "No video, so there is no YouTube description or post.",
50
65
  schedule: "",
51
66
  };
52
67
  // Each section's state before any sample: off, provided at run time (no sample, never blocks
@@ -69,7 +84,9 @@ export function baseStatuses(document, made = {}) {
69
84
  id,
70
85
  label,
71
86
  state: "stale",
72
- sentence: "No sample yet. Press Save to make one, or Make sample.",
87
+ sentence: paidSections.includes(id)
88
+ ? makeSampleSentence(document)
89
+ : "Make sample shows what this section makes, about the sample topic.",
73
90
  });
74
91
  });
75
92
  }
@@ -93,7 +110,7 @@ export function madeStatuses(free, schedules, cost) {
93
110
  id: "reviews",
94
111
  label: sectionLabel.reviews,
95
112
  state: "current",
96
- sentence: "Where a run stops, and who answers each stop.",
113
+ sentence: "Where a run pauses for you, and which AI checks run.",
97
114
  };
98
115
  made.schedule =
99
116
  schedules === 0
@@ -101,7 +118,7 @@ export function madeStatuses(free, schedules, cost) {
101
118
  id: "schedule",
102
119
  label: sectionLabel.schedule,
103
120
  state: "off",
104
- sentence: "No schedule runs this template yet. Make one in Calendar → Schedules.",
121
+ sentence: "No schedule runs this template yet.",
105
122
  }
106
123
  : {
107
124
  id: "schedule",
@@ -167,7 +184,7 @@ export function paidStatuses(input) {
167
184
  made[id] = status(id, "needs-fix", row.heldReason ?? "The sample failed. Press Make sample to try again.");
168
185
  else
169
186
  made[id] = status(id, "stale", done === undefined
170
- ? "No sample yet. Press Make sample to make one."
187
+ ? makeSampleSentence(input.headDocument)
171
188
  : `Changed since the sample of version ${String(done.templateVersion)}. Press Make sample to see it again.`);
172
189
  }
173
190
  return made;
@@ -57,7 +57,7 @@ async function change(deps, id, next, sections, ack, kind) {
57
57
  return {
58
58
  ok: false,
59
59
  reason: "not-found",
60
- message: "This template no longer exists; it may have been deleted. Go to Library → Templates to see your templates.",
60
+ message: "This template no longer exists; it may have been deleted. Go to Templates to see your templates.",
61
61
  };
62
62
  const quoted = await quoteSample(deps, id, {
63
63
  topic: next.topic,
@@ -159,7 +159,7 @@ export async function setSampleTopic(deps, id, raw) {
159
159
  return {
160
160
  ok: false,
161
161
  reason: "conflict",
162
- message: "This topic change was already sent with another topic or price. Reload the studio to see the sample's topic, then change it again.",
162
+ message: "This topic change was already sent with another topic or price. Reload the page to see the sample's topic, then change it again.",
163
163
  };
164
164
  const replayed = await quoteSample(deps, id, {
165
165
  topic: last.topic,
@@ -212,7 +212,7 @@ export async function setFullScript(deps, id, raw) {
212
212
  return {
213
213
  ok: false,
214
214
  reason: "invalid-input",
215
- message: "The studio sent a request Slopify couldn't read. Reload the page and try again.",
215
+ message: "Edit template sent a request Slopify couldn't read. Reload the page and try again.",
216
216
  };
217
217
  const record = sampleOf(deps.db, id);
218
218
  return change(deps, id, {
@@ -27,5 +27,5 @@ export function thumbnailMessages(brief) {
27
27
  // Empty output is a failed attempt. Returned as the sentence the stage would show rather than
28
28
  // thrown, because the wrapper's `check` hook is what makes it a retry.
29
29
  export function writtenPrompt(text) {
30
- return text.trim() === "" ? "the LLM wrote no thumbnail prompt" : undefined;
30
+ return text.trim() === "" ? "the AI model wrote no thumbnail prompt" : undefined;
31
31
  }