@gentbajko/slopify 4.0.0 → 4.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 (366) 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 +3 -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/providers.js +9 -10
  59. package/dist/edge/http/refusal.js +1 -1
  60. package/dist/edge/http/remote-access.js +4 -4
  61. package/dist/edge/http/remote-guard.js +2 -2
  62. package/dist/edge/http/reviews.js +1 -1
  63. package/dist/edge/http/revision-files.js +1 -1
  64. package/dist/edge/http/revisions.js +4 -4
  65. package/dist/edge/http/schedules.js +13 -11
  66. package/dist/edge/http/settings.js +6 -2
  67. package/dist/edge/http/social.js +5 -4
  68. package/dist/edge/http/staging.js +1 -1
  69. package/dist/edge/http/storage.js +12 -4
  70. package/dist/edge/http/studio.js +71 -19
  71. package/dist/edge/http/style-preview.js +2 -2
  72. package/dist/edge/http/template-studio.js +12 -9
  73. package/dist/edge/http/trash.js +1 -1
  74. package/dist/edge/http/youtube-edits.js +1 -1
  75. package/dist/edge/mcp/excluded-routes.js +4 -3
  76. package/dist/edge/mcp/instructions.js +3 -3
  77. package/dist/edge/mcp/library-tools.js +8 -5
  78. package/dist/edge/mcp/remake-tools.js +7 -4
  79. package/dist/edge/mcp/server.js +1 -1
  80. package/dist/edge/mcp/tools-channels.js +9 -9
  81. package/dist/edge/mcp/tools-designs.js +9 -9
  82. package/dist/edge/mcp/tools-library.js +7 -7
  83. package/dist/edge/mcp/tools-planning.js +18 -18
  84. package/dist/edge/mcp/tools-projects.js +10 -10
  85. package/dist/edge/mcp/tools-publishing.js +9 -9
  86. package/dist/edge/mcp/tools-runs.js +9 -9
  87. package/dist/edge/mcp/tools-schedules.js +6 -6
  88. package/dist/edge/mcp/tools-setup.js +20 -14
  89. package/dist/edge/mcp/tools-social.js +5 -4
  90. package/dist/edge/mcp/tools-storage.js +3 -3
  91. package/dist/edge/mcp/tools-template-studio.js +18 -18
  92. package/dist/edge/remote-access/model.js +1 -1
  93. package/dist/extension/slopify-studio-chrome.zip +0 -0
  94. package/dist/extension/slopify-studio-firefox.zip +0 -0
  95. package/dist/kernel/db/migrations/0078-uploaded-from-links.sql +7 -0
  96. package/dist/kernel/pipeline.js +12 -0
  97. package/dist/kernel/ports/settings-places.js +31 -0
  98. package/dist/kernel/ports/system-speech.js +31 -3
  99. package/dist/kernel/runner/attempt.js +11 -3
  100. package/dist/kernel/runner/providers.js +3 -2
  101. package/dist/kernel/runner/standalone.js +2 -2
  102. package/dist/main.js +5 -3
  103. package/dist/model-catalog.js +20 -0
  104. package/dist/patch-notes/4.0.0.md +13 -13
  105. package/dist/patch-notes/4.1.0.md +42 -0
  106. package/dist/patch-notes/index.json +6 -0
  107. package/dist/slices/admission/listing-facts.js +95 -0
  108. package/dist/slices/admission/repo.js +9 -6
  109. package/dist/slices/admission/rules.js +7 -7
  110. package/dist/slices/admission/state-words.js +34 -5
  111. package/dist/slices/assistant/cost.js +1 -1
  112. package/dist/slices/assistant/model.js +10 -3
  113. package/dist/slices/assistant/turn.js +5 -3
  114. package/dist/slices/autopilot/warnings.js +1 -1
  115. package/dist/slices/backups/service.js +5 -5
  116. package/dist/slices/channels/cast-images.js +1 -1
  117. package/dist/slices/channels/schema.js +1 -1
  118. package/dist/slices/channels/videos.js +1 -1
  119. package/dist/slices/control/providers.js +2 -2
  120. package/dist/slices/designs/budget.js +1 -1
  121. package/dist/slices/designs/calls.js +2 -2
  122. package/dist/slices/designs/check-context.js +4 -4
  123. package/dist/slices/designs/fields.js +4 -4
  124. package/dist/slices/designs/gate.js +7 -5
  125. package/dist/slices/designs/render-input.js +1 -1
  126. package/dist/slices/designs/requests.js +1 -1
  127. package/dist/slices/designs/runner.js +1 -1
  128. package/dist/slices/designs/sample.js +2 -1
  129. package/dist/slices/designs/service.js +1 -1
  130. package/dist/slices/designs/template-checks.js +2 -2
  131. package/dist/slices/designs/template-make.js +3 -3
  132. package/dist/slices/designs/templates.js +3 -3
  133. package/dist/slices/document/fonts.js +1 -1
  134. package/dist/slices/document/model.js +7 -4
  135. package/dist/slices/document/render.js +2 -1
  136. package/dist/slices/document/theme-schema.js +1 -1
  137. package/dist/slices/document/theme.js +49 -3
  138. package/dist/slices/episodes/summarize.js +2 -2
  139. package/dist/slices/estimate/index.js +28 -22
  140. package/dist/slices/estimate/requests.js +4 -4
  141. package/dist/slices/fixes/rules.js +3 -1
  142. package/dist/slices/fonts/catalog.js +2 -1
  143. package/dist/slices/fonts/coverage.js +2 -1
  144. package/dist/slices/images/appearance.js +2 -1
  145. package/dist/slices/images/scenes.js +2 -1
  146. package/dist/slices/keywords/digest.js +2 -1
  147. package/dist/slices/keywords/rules.js +2 -2
  148. package/dist/slices/keywords/specs.js +5 -5
  149. package/dist/slices/keywords/values.js +1 -1
  150. package/dist/slices/library/transfer.js +3 -3
  151. package/dist/slices/loudness/loudnorm.js +2 -1
  152. package/dist/slices/loudness/model.js +3 -2
  153. package/dist/slices/loudness/sentence-level.js +2 -1
  154. package/dist/slices/model-checks/rules.js +13 -0
  155. package/dist/slices/model-upkeep/model.js +1 -1
  156. package/dist/slices/model-upkeep/switch.js +3 -3
  157. package/dist/slices/model-upkeep/usage.js +13 -2
  158. package/dist/slices/narration/breaths.js +2 -1
  159. package/dist/slices/narration/clean-pieces.js +2 -1
  160. package/dist/slices/narration/pauses-model.js +3 -2
  161. package/dist/slices/narration/pronunciation.js +3 -2
  162. package/dist/slices/notifications/subject.js +2 -2
  163. package/dist/slices/onboarding/copy.js +19 -2
  164. package/dist/slices/onboarding/first-run.js +13 -38
  165. package/dist/slices/onboarding/install.js +2 -1
  166. package/dist/slices/onboarding/model.js +26 -21
  167. package/dist/slices/onboarding/preferred-model.js +14 -0
  168. package/dist/slices/onboarding/quick-short.js +103 -64
  169. package/dist/slices/onboarding/sample.js +4 -1
  170. package/dist/slices/onboarding/state.js +7 -0
  171. package/dist/slices/onboarding/template.js +6 -5
  172. package/dist/slices/outputs/add.js +3 -2
  173. package/dist/slices/outputs/service.js +2 -2
  174. package/dist/slices/patch-notes/library.js +1 -1
  175. package/dist/slices/play-drafts/readiness.js +3 -3
  176. package/dist/slices/play-drafts/review-inputs.js +1 -1
  177. package/dist/slices/play-drafts/service.js +17 -7
  178. package/dist/slices/project-templates/next-chapter.js +2 -1
  179. package/dist/slices/project-templates/provider-defaults.js +22 -0
  180. package/dist/slices/project-templates/setup-sections.js +26 -0
  181. package/dist/slices/project-templates/setup.js +6 -1
  182. package/dist/slices/rebuild/preview-details.js +5 -5
  183. package/dist/slices/rebuild/recipe-preparation.js +1 -1
  184. package/dist/slices/rebuild/recipe-save.js +2 -2
  185. package/dist/slices/rebuild/recipe-validation.js +5 -4
  186. package/dist/slices/rebuild/recipe-voices.js +3 -2
  187. package/dist/slices/rebuild/recipe-work.js +4 -4
  188. package/dist/slices/rebuild/recovery.js +6 -6
  189. package/dist/slices/rebuild/remake-outdated.js +5 -5
  190. package/dist/slices/rebuild/review-redo.js +1 -1
  191. package/dist/slices/rebuild/runtime-animate.js +2 -2
  192. package/dist/slices/rebuild/runtime-article.js +4 -3
  193. package/dist/slices/rebuild/runtime-document.js +5 -4
  194. package/dist/slices/rebuild/runtime-export-bed.js +2 -1
  195. package/dist/slices/rebuild/runtime-export-edit.js +2 -1
  196. package/dist/slices/rebuild/runtime-export-inputs.js +5 -4
  197. package/dist/slices/rebuild/runtime-export-short.js +1 -1
  198. package/dist/slices/rebuild/runtime-export.js +4 -3
  199. package/dist/slices/rebuild/runtime-image.js +4 -3
  200. package/dist/slices/rebuild/runtime-local.js +3 -2
  201. package/dist/slices/rebuild/runtime-narration-text.js +3 -2
  202. package/dist/slices/rebuild/runtime-provider.js +7 -6
  203. package/dist/slices/rebuild/runtime-review.js +6 -5
  204. package/dist/slices/rebuild/runtime-shorts.js +6 -5
  205. package/dist/slices/rebuild/runtime-store.js +17 -2
  206. package/dist/slices/rebuild/runtime-subtitles.js +13 -12
  207. package/dist/slices/rebuild/runtime-voices.js +2 -1
  208. package/dist/slices/rebuild/runtime-youtube.js +3 -2
  209. package/dist/slices/rebuild/service-readiness.js +11 -3
  210. package/dist/slices/research/synthesis.js +3 -2
  211. package/dist/slices/reviews/external-model.js +1 -1
  212. package/dist/slices/reviews/holds.js +5 -4
  213. package/dist/slices/reviews/model.js +8 -5
  214. package/dist/slices/reviews/rules.js +3 -3
  215. package/dist/slices/reviews/verdict.js +2 -1
  216. package/dist/slices/revisions/image-plan.js +1 -1
  217. package/dist/slices/revisions/mutation-assets.js +2 -1
  218. package/dist/slices/revisions/mutations.js +3 -2
  219. package/dist/slices/schedules/agenda.js +7 -5
  220. package/dist/slices/schedules/design-check.js +4 -4
  221. package/dist/slices/schedules/design-run.js +3 -3
  222. package/dist/slices/schedules/repo.js +6 -5
  223. package/dist/slices/schedules/schema.js +3 -0
  224. package/dist/slices/schedules/topics.js +5 -5
  225. package/dist/slices/settings/first-run.js +6 -32
  226. package/dist/slices/settings/health.js +30 -15
  227. package/dist/slices/settings/key-test.js +1 -1
  228. package/dist/slices/settings/model.js +4 -2
  229. package/dist/slices/settings/playback.js +2 -2
  230. package/dist/slices/shorts/keep.js +2 -1
  231. package/dist/slices/shorts/pick.js +4 -3
  232. package/dist/slices/shorts/prompts.js +2 -1
  233. package/dist/slices/shorts/render.js +3 -2
  234. package/dist/slices/social/media.js +5 -4
  235. package/dist/slices/social/pack.js +1 -1
  236. package/dist/slices/social/post-prompt.js +2 -1
  237. package/dist/slices/social/reshape.js +2 -2
  238. package/dist/slices/social/serve.js +1 -1
  239. package/dist/slices/social/service.js +3 -2
  240. package/dist/slices/social/state.js +1 -1
  241. package/dist/slices/storage/backup-import.js +2 -2
  242. package/dist/slices/storage/portable.js +1 -1
  243. package/dist/slices/storage/trim.js +23 -13
  244. package/dist/slices/studio/calendar.js +3 -1
  245. package/dist/slices/studio/disclosure.js +4 -4
  246. package/dist/slices/studio/model.js +1 -1
  247. package/dist/slices/studio/pack.js +9 -8
  248. package/dist/slices/studio/plan.js +1 -1
  249. package/dist/slices/studio/settings.js +6 -6
  250. package/dist/slices/studio/videos.js +23 -4
  251. package/dist/slices/style-preview/render.js +3 -3
  252. package/dist/slices/style-preview/schema.js +1 -1
  253. package/dist/slices/subtitles/captions.js +1 -1
  254. package/dist/slices/template-studio/anatomy-view.js +46 -3
  255. package/dist/slices/template-studio/bakeoff.js +3 -3
  256. package/dist/slices/template-studio/budget.js +6 -3
  257. package/dist/slices/template-studio/cards.js +78 -0
  258. package/dist/slices/template-studio/checks.js +99 -50
  259. package/dist/slices/template-studio/for-all.js +5 -5
  260. package/dist/slices/template-studio/free-video.js +1 -1
  261. package/dist/slices/template-studio/free.js +5 -5
  262. package/dist/slices/template-studio/model.js +19 -15
  263. package/dist/slices/template-studio/pack-import.js +14 -14
  264. package/dist/slices/template-studio/pack.js +9 -7
  265. package/dist/slices/template-studio/paid.js +12 -5
  266. package/dist/slices/template-studio/quote.js +18 -11
  267. package/dist/slices/template-studio/ready.js +7 -7
  268. package/dist/slices/template-studio/runner.js +1 -1
  269. package/dist/slices/template-studio/save.js +12 -12
  270. package/dist/slices/template-studio/say-it-as.js +3 -3
  271. package/dist/slices/template-studio/schedule-gate.js +1 -1
  272. package/dist/slices/template-studio/schedule-price.js +3 -1
  273. package/dist/slices/template-studio/schedule-sample.js +5 -1
  274. package/dist/slices/template-studio/schema.js +25 -0
  275. package/dist/slices/template-studio/service.js +3 -1
  276. package/dist/slices/template-studio/status.js +26 -9
  277. package/dist/slices/template-studio/topic.js +3 -3
  278. package/dist/slices/thumbnail/by-llm.js +1 -1
  279. package/dist/slices/uploads/repo.js +37 -1
  280. package/dist/slices/uploads/rules.js +3 -2
  281. package/dist/slices/video/edit-list.js +2 -2
  282. package/dist/slices/video/edit-settings.js +1 -1
  283. package/dist/slices/video/encoding.js +14 -5
  284. package/dist/slices/video/ffmpeg.js +2 -2
  285. package/dist/slices/video/plan.js +2 -1
  286. package/dist/slices/video/slideshow.js +1 -1
  287. package/dist/slices/voices/attribution.js +3 -2
  288. package/dist/slices/voices/model.js +9 -8
  289. package/dist/slices/voices/script.js +2 -1
  290. package/dist/slices/youtube/answer.js +2 -1
  291. package/dist/slices/youtube/edits-repo.js +1 -1
  292. package/dist/tutorials/AI-CLIs.md +25 -25
  293. package/dist/tutorials/Agents-MCP.md +32 -32
  294. package/dist/tutorials/Assistant.md +5 -5
  295. package/dist/tutorials/Backups.md +7 -7
  296. package/dist/tutorials/Calendar.md +17 -25
  297. package/dist/tutorials/Cast-Library.md +19 -19
  298. package/dist/tutorials/Channels.md +40 -35
  299. package/dist/tutorials/Costs-and-Run-Cost.md +38 -40
  300. package/dist/tutorials/Create-Article.md +114 -0
  301. package/dist/tutorials/Create-Extras.md +112 -0
  302. package/dist/tutorials/Create-Images.md +165 -0
  303. package/dist/tutorials/Create-Narration.md +178 -0
  304. package/dist/tutorials/Create-Overview.md +244 -0
  305. package/dist/tutorials/Create-Video-and-Style.md +144 -0
  306. package/dist/tutorials/Docker.md +3 -3
  307. package/dist/tutorials/Document-Themes.md +32 -32
  308. package/dist/tutorials/Editing-a-Project.md +94 -94
  309. package/dist/tutorials/FAQ.md +18 -18
  310. package/dist/tutorials/First-Launch-and-Welcome.md +50 -64
  311. package/dist/tutorials/Home-and-Projects.md +101 -76
  312. package/dist/tutorials/Home.md +33 -33
  313. package/dist/tutorials/Install.md +13 -13
  314. package/dist/tutorials/Intros-and-Outros.md +33 -34
  315. package/dist/tutorials/Keyboard-Shortcuts-and-Command-Palette.md +62 -58
  316. package/dist/tutorials/Keywords-Slopify-Fills.md +21 -21
  317. package/dist/tutorials/Models.md +27 -27
  318. package/dist/tutorials/Multiple-Voices.md +41 -40
  319. package/dist/tutorials/Narration-Aliases-and-Glossary.md +14 -14
  320. package/dist/tutorials/Notifications.md +21 -17
  321. package/dist/tutorials/Other-Languages.md +15 -15
  322. package/dist/tutorials/PDF-Documents.md +38 -29
  323. package/dist/tutorials/Posting-to-Social-Sites.md +12 -12
  324. package/dist/tutorials/Project-Page.md +118 -120
  325. package/dist/tutorials/Prompts-and-Templates.md +84 -0
  326. package/dist/tutorials/Prompts.md +45 -45
  327. package/dist/tutorials/Providers-and-Keys.md +105 -98
  328. package/dist/tutorials/Publishing-to-YouTube.md +31 -31
  329. package/dist/tutorials/Recovery-and-Retries.md +31 -31
  330. package/dist/tutorials/Remote-Access.md +2 -2
  331. package/dist/tutorials/Reviews-and-Checkpoints.md +90 -93
  332. package/dist/tutorials/Schedules.md +27 -28
  333. package/dist/tutorials/Settings-Reference.md +182 -204
  334. package/dist/tutorials/Shorts.md +34 -33
  335. package/dist/tutorials/Start-at-Login.md +3 -3
  336. package/dist/tutorials/Studio-Extension.md +22 -24
  337. package/dist/tutorials/Template-Studio.md +45 -42
  338. package/dist/tutorials/Templates.md +50 -79
  339. package/dist/tutorials/Trash.md +4 -4
  340. package/dist/tutorials/Troubleshooting.md +44 -44
  341. package/dist/tutorials/Uninstalling-and-Moving.md +5 -5
  342. package/dist/tutorials/Updating-and-Patch-Notes.md +18 -18
  343. package/dist/tutorials/Video-Editing.md +34 -34
  344. package/dist/tutorials/Where-Your-Files-Live.md +10 -10
  345. package/dist/tutorials/YouTube-Description.md +26 -26
  346. package/dist/tutorials/Your-First-Short.md +35 -35
  347. package/dist/tutorials/_Sidebar.md +15 -15
  348. package/dist/web/assets/index-BNftKQrA.css +1 -0
  349. package/dist/web/assets/index-Cpa58woP.js +459 -0
  350. package/dist/web/assets/pdf-BKh8f7R6.js +56 -0
  351. package/dist/web/assets/pdf.worker.min-BmVo14Nb.mjs +28 -0
  352. package/dist/web/assets/pdf.worker.min-D7i8o3wR.js +1 -0
  353. package/dist/web/index.html +2 -2
  354. package/package.json +2 -2
  355. package/dist/tutorials/Library-Overview.md +0 -76
  356. package/dist/tutorials/Play-Images.md +0 -165
  357. package/dist/tutorials/Play-Narration.md +0 -167
  358. package/dist/tutorials/Play-Outputs.md +0 -109
  359. package/dist/tutorials/Play-Overview.md +0 -227
  360. package/dist/tutorials/Play-Title-and-Article.md +0 -136
  361. package/dist/tutorials/Play-Video-and-Style.md +0 -143
  362. package/dist/web/assets/index-CkAI_2E7.css +0 -1
  363. package/dist/web/assets/index-DcD0cdfo.js +0 -334
  364. package/dist/web/assets/pdf-TODRwF31.js +0 -54
  365. package/dist/web/assets/pdf.worker.min-Dswkl-cV.mjs +0 -29
  366. package/dist/web/assets/pdf.worker.min-zC6nfbxG.js +0 -1
@@ -2,7 +2,7 @@ import { randomUUID } from "node:crypto";
2
2
  import { zValidator } from "@hono/zod-validator";
3
3
  import { Hono } from "hono";
4
4
  import { z } from "zod";
5
- import { defaultSpeechVoice, detectSpeechCached, primaryLanguage, } from "../../kernel/ports/system-speech.js";
5
+ import { defaultSpeechVoice, detectSpeechCached, forgetDetectedSpeech, primaryLanguage, speechInstall, } from "../../kernel/ports/system-speech.js";
6
6
  import { loudnessOfDefault } from "../../slices/loudness/model.js";
7
7
  import { firstRunView } from "../../slices/onboarding/first-run.js";
8
8
  import { fullVideoDraft } from "../../slices/onboarding/full-video.js";
@@ -15,9 +15,10 @@ import { dismissFirstRun, recordShortRequest, shortRequestProject, } from "../..
15
15
  import { systemVoiceProvider } from "../../slices/settings/model.js";
16
16
  import { readSettings } from "../../slices/settings/playback.js";
17
17
  import { providerStatuses } from "../../slices/settings/readiness.js";
18
- import { listVoices } from "../../slices/settings/repo.js";
18
+ import { listVoices, writeSetting } from "../../slices/settings/repo.js";
19
19
  import { addVoice } from "../../slices/settings/voices.js";
20
20
  import { projectTitle } from "../../slices/storage/repo.js";
21
+ import { autostartAnsweredKey } from "../autostart/model.js";
21
22
  import { onInvalid, problem } from "./problem.js";
22
23
  import { createProject } from "./project-create.js";
23
24
  const packParam = z.object({ id: z.string().min(1).max(40) });
@@ -30,7 +31,16 @@ export function onboardingRoutes(deps) {
30
31
  hostCliStatus: deps.hostCliStatus,
31
32
  host: deps.speechHost,
32
33
  });
33
- const speech = () => detectSpeechCached(deps.probe, deps.speechHost ?? process);
34
+ const host = deps.speechHost ?? process;
35
+ const speech = () => detectSpeechCached(deps.probe, host);
36
+ const view = async () => firstRunView(deps.db, await statuses(), speechInstall(host));
37
+ // Done with the first-run screen (Skip, Done, a first short, or a project made elsewhere):
38
+ // it also answers "Start Slopify when I log in" as No, so a fresh install is never asked it
39
+ // in a dialog over Home. The screen's own row and Settings → General still turn it on.
40
+ const doneWithFirstRun = () => {
41
+ dismissFirstRun(deps.db, deps.clock.now().toISOString());
42
+ writeSetting(deps.db, autostartAnsweredKey, "1");
43
+ };
34
44
  // The best speech program found and its English voice, for a short made without a voice key.
35
45
  const systemVoicePick = async () => {
36
46
  const engine = (await speech()).engines[0];
@@ -53,10 +63,16 @@ export function onboardingRoutes(deps) {
53
63
  return new Hono()
54
64
  .get("/", async (c) => {
55
65
  c.header("Cache-Control", "no-store");
56
- return c.json(firstRunView(deps.db, await statuses()));
66
+ return c.json(await view());
67
+ })
68
+ .post("/check", async (c) => {
69
+ // The checklist's Check again: looks for a speech program afresh instead of answering
70
+ // from the last minute's look, so a program installed a moment ago is found.
71
+ forgetDetectedSpeech(deps.probe);
72
+ return c.json(await view());
57
73
  })
58
74
  .post("/dismiss", (c) => {
59
- dismissFirstRun(deps.db, deps.clock.now().toISOString());
75
+ doneWithFirstRun();
60
76
  return c.json({ dismissed: true });
61
77
  })
62
78
  .post("/packs/:id", zValidator("param", packParam, onInvalid), (c) => {
@@ -64,7 +80,7 @@ export function onboardingRoutes(deps) {
64
80
  if (!installed.ok)
65
81
  return problem(c, {
66
82
  status: 404,
67
- detail: "Slopify doesn't have that starter pack. Reload the page and pick one of the packs listed.",
83
+ detail: "Slopify doesn't have that starter template. Reload the page and pick one of the templates listed.",
68
84
  });
69
85
  return c.json(installed.value);
70
86
  })
@@ -77,7 +93,7 @@ export function onboardingRoutes(deps) {
77
93
  if (pack === undefined)
78
94
  return problem(c, {
79
95
  status: 404,
80
- detail: "Slopify doesn't have that starter pack. Reload the page and pick one of the packs listed.",
96
+ detail: "Slopify doesn't have that starter template. Reload the page and pick one of the templates listed.",
81
97
  });
82
98
  const modelsFor = deps.modelsFor;
83
99
  if (modelsFor === undefined)
@@ -91,6 +107,7 @@ export function onboardingRoutes(deps) {
91
107
  modelsFor,
92
108
  suggested: pack.voice,
93
109
  systemVoice: await systemVoicePick(),
110
+ install: speechInstall(host),
94
111
  });
95
112
  if (!plan.ok)
96
113
  return problem(c, {
@@ -102,7 +119,7 @@ export function onboardingRoutes(deps) {
102
119
  if (!installed.ok)
103
120
  return problem(c, {
104
121
  status: 404,
105
- detail: "Slopify doesn't have that starter pack. Reload the page and pick one of the packs listed.",
122
+ detail: "Slopify doesn't have that starter template. Reload the page and pick one of the templates listed.",
106
123
  });
107
124
  // The computer's voice is saved like any other, so Play and a later rebuild offer it.
108
125
  const audio = plan.providers.audio;
@@ -135,7 +152,7 @@ export function onboardingRoutes(deps) {
135
152
  });
136
153
  recordShortRequest(deps.db, input.requestId, created.project.id);
137
154
  // A real project now exists, so the first-run screen is done with.
138
- dismissFirstRun(deps.db, deps.clock.now().toISOString());
155
+ doneWithFirstRun();
139
156
  return c.json({ projectId: created.project.id, replayed: false }, 201);
140
157
  })
141
158
  .post("/full-video", zValidator("json", fullVideoInputSchema, onInvalid), (c) => {
@@ -145,16 +162,16 @@ export function onboardingRoutes(deps) {
145
162
  if (made.reason === "not-found")
146
163
  return problem(c, {
147
164
  status: 404,
148
- detail: "This short or its starter pack is no longer here, so Play couldn't be set up from it. Open Play and pick a template yourself.",
165
+ detail: "This short or its starter template is no longer here, so Create couldn't be set up from it. Open Create and pick a template yourself.",
149
166
  });
150
167
  if (made.reason === "not-short")
151
168
  return problem(c, {
152
169
  status: 409,
153
- detail: "This project is already a long video. Open Play to start another one on a new topic.",
170
+ detail: "This project is already a long video. Open Create to start another one on a new topic.",
154
171
  });
155
172
  return problem(c, {
156
173
  status: 409,
157
- detail: "Slopify couldn't open a Play draft for the full video. Press Make the full video on this topic again; if it keeps failing, open Play and pick the pack's template in Library → Templates.",
174
+ detail: "Slopify couldn't open a draft on Create for the full video. Press Make the full video on this topic again; if it keeps failing, press Make a video on the starter template in Templates.",
158
175
  extensions: { reason: made.reason },
159
176
  });
160
177
  })
@@ -1,5 +1,6 @@
1
1
  import { zValidator } from "@hono/zod-validator";
2
2
  import { Hono } from "hono";
3
+ import { editSettings } from "../../kernel/ports/settings-places.js";
3
4
  import { addOutputPreviewRequestSchema, addOutputRequestSchema, } from "../../slices/outputs/model.js";
4
5
  import { addOutput, previewAddedOutput, } from "../../slices/outputs/service.js";
5
6
  import { currentRevisionId } from "../../slices/revisions/repo.js";
@@ -21,10 +22,10 @@ const details = {
21
22
  "no-revision": "This saved version of the project no longer exists. Reload the page, then open Add another output again.",
22
23
  conflict: "This project changed while you were choosing. Reload the page, then open Add another output again.",
23
24
  "idempotency-conflict": "This output was already requested with different details. Reload the page, then try again.",
24
- "invalid-edit": "This project is missing a choice the new output needs. Set the fields listed in this project's Settings, save, then add the output again.",
25
+ "invalid-edit": "This project is missing a choice the new output needs. Set the fields listed in Edit settings, save, then add the output again.",
25
26
  already: "This project already makes that output. Find it in its section on the left.",
26
27
  "needs-article": "That output is made from the article, which this project does not have yet. Wait for the Article step to finish, then add it.",
27
- "narration-taken": "This project already has narration, and a project has one. Keep it, or change its format under Speakers in this project's Settings → Providers.",
28
+ "narration-taken": `This project already has narration, and a project has one. Keep it, or change its format under Speakers in ${editSettings.narration}.`,
28
29
  "adaptation-not-accepted": 'This output rewrites your article as a conversation. Tick "Adapt my article into a conversation" in Add another output, then add it.',
29
30
  };
30
31
  // Add another output: what it reuses, makes and costs, then the revision that switches it on.
@@ -3,7 +3,7 @@ import { bundledPatchNotesDir, PatchNotesMissing, readPatchNote, } from "../../s
3
3
  import { markPatchNotesSeen, readPatchNotes } from "../../slices/patch-notes/seen.js";
4
4
  import { problem } from "./problem.js";
5
5
  import { bundleMissing } from "./refusal.js";
6
- // Settings → Patch notes: the list (with which note is due to open by itself), one note's
6
+ // Settings → What's new: the list (with which note is due to open by itself), one note's
7
7
  // Markdown, and recording that the note that opened by itself was closed.
8
8
  // Preserve Hono's inferred route types for the web client.
9
9
  export function patchNotesRoutes(deps) {
@@ -32,7 +32,7 @@ export function patchNotesRoutes(deps) {
32
32
  if (markdown === undefined)
33
33
  return problem(c, {
34
34
  status: 404,
35
- detail: "These patch notes are not in this version of Slopify. Open Settings → Patch notes and pick one from the list.",
35
+ detail: "These patch notes are not in this version of Slopify. Open Settings → What's new and pick one from the list.",
36
36
  });
37
37
  return c.body(markdown, 200, { "content-type": "text/markdown; charset=utf-8" });
38
38
  });
@@ -94,7 +94,7 @@ export function planningRoutes(deps) {
94
94
  if (input.draft.checkpoints?.length)
95
95
  return problem(c, {
96
96
  status: 409,
97
- detail: "Videos with checkpoints turned on cannot be queued. Start them one at a time from Play with Review and start.",
97
+ detail: "Videos that pause for your review (Pauses and checks) can't be queued. Make them one at a time from Create.",
98
98
  });
99
99
  if (batchExists(deps.db, input.requestId))
100
100
  return c.json({ queue: queueEntries(deps.db, input.requestId) });
@@ -18,7 +18,7 @@ export async function createProject(deps, sent) {
18
18
  return {
19
19
  ok: false,
20
20
  status: 409,
21
- detail: "This project has checkpoints turned on, so it must be started from Play with Review and start.",
21
+ detail: "This project pauses for your review (Pauses and checks), so it starts from Create: press Make the video there.",
22
22
  fields: [],
23
23
  };
24
24
  const subtitles = draft.subtitles;
@@ -23,11 +23,11 @@ function refused(c, result) {
23
23
  statuses: { "not-found": 404, conflict: 409, "referenced-by-schedule": 409 },
24
24
  otherwise: 400,
25
25
  detail: result.reason === "referenced-by-schedule"
26
- ? "Some schedules still use this template. In Library > Schedules, cancel and delete them first; their run history is kept."
26
+ ? "Some schedules still use this template. In Calendar → Schedules, cancel and delete them first; their run history is kept."
27
27
  : result.reason === "missing-prompt"
28
28
  ? "A prompt or intro/outro entry this template uses was deleted. Choose another in the template, then save again."
29
29
  : result.reason === "not-found"
30
- ? "This template no longer exists; it may have been deleted. Go to Library > Templates to see your templates."
30
+ ? "This template no longer exists; it may have been deleted. Go to Templates to see your templates."
31
31
  : result.reason === "conflict"
32
32
  ? "This template or its project changed while you were editing. Reload the page to see the latest, then make your change again."
33
33
  : "Some template settings are not valid, for example an image you supplied yourself. Check the template's settings and try again.",
@@ -4,13 +4,15 @@ import { z } from "zod";
4
4
  import { transact } from "../../kernel/db/tx.js";
5
5
  import { derive, progressOf } from "../../kernel/runner/graph.js";
6
6
  import { listProjectHeads } from "../../slices/admission/heads.js";
7
+ import { listingFacts } from "../../slices/admission/listing-facts.js";
7
8
  import { projectById, projectTrashed, runDraftSchema, stageStandingsByProject, stagesOf, stepFailuresByProject, } from "../../slices/admission/repo.js";
8
- import { readsByWaitBesideFailure, stepFailedWhileWaiting, } from "../../slices/admission/state-words.js";
9
+ import { readsByWaitBesideFailure, retryingAt, stepFailedWhileWaiting, } from "../../slices/admission/state-words.js";
9
10
  import { lastStarter } from "../../slices/agents/repo.js";
10
11
  import { defaultChannelId } from "../../slices/channels/model.js";
11
12
  import { channelById, projectChannels, setProjectChannel } from "../../slices/channels/repo.js";
12
13
  import { withProjectControl } from "../../slices/control/lock.js";
13
14
  import { stagesWithEta } from "../../slices/eta/view.js";
15
+ import { sampleProjectIds } from "../../slices/onboarding/sample.js";
14
16
  import { videoActivity } from "../../slices/rebuild/activity.js";
15
17
  import { projectHolds } from "../../slices/reviews/holds.js";
16
18
  import { adoptBaseline } from "../../slices/revisions/adopt.js";
@@ -18,7 +20,7 @@ import { currentRevisionId } from "../../slices/revisions/repo.js";
18
20
  import { limitWaitsByProject, listingWait } from "../../slices/run-cost/panel.js";
19
21
  import { outputsOf } from "../../slices/storage/repo.js";
20
22
  import { trashProject } from "../../slices/trash/service.js";
21
- import { setAsideProjects, uploadedAndSettled, uploadedProjects, } from "../../slices/uploads/repo.js";
23
+ import { setAsideProjects, settledProjects, uploadedProjects } from "../../slices/uploads/repo.js";
22
24
  import { settledStatus } from "../../slices/uploads/rules.js";
23
25
  import { idParam } from "./ids.js";
24
26
  import { onInvalid, problem } from "./problem.js";
@@ -35,7 +37,7 @@ export function projectRoutes(deps) {
35
37
  };
36
38
  const summarise = (project) => {
37
39
  const stages = stagesOf(deps.db, project.id);
38
- const status = settledStatus(derive(stages, project.paused), uploadedAndSettled(deps.db, project.id).has(project.id));
40
+ const status = settledStatus(derive(stages, project.paused), settledProjects(deps.db, project.id).has(project.id));
39
41
  return {
40
42
  ...project,
41
43
  status,
@@ -65,15 +67,20 @@ export function projectRoutes(deps) {
65
67
  const channels = projectChannels(deps.db);
66
68
  const uploads = uploadedProjects(deps.db);
67
69
  const aside = setAsideProjects(deps.db);
68
- const settled = uploadedAndSettled(deps.db);
70
+ const settled = settledProjects(deps.db);
69
71
  const holds = projectHolds(deps.db, { standings, settled });
70
72
  const waits = limitWaitsByProject(deps.db);
71
73
  const failures = stepFailuresByProject(deps.db);
74
+ const samples = new Set(Object.values(sampleProjectIds(deps.db)));
75
+ const facts = listingFacts(deps.db);
72
76
  const projects = listProjectHeads(deps.db).map((project) => {
73
77
  const stages = standings.get(project.id) ?? [];
74
78
  const waiting = waits.get(project.id);
75
79
  const holding = holds.holding.get(project.id);
76
80
  const status = settledStatus(derive(stages, project.paused), settled.has(project.id));
81
+ const made = facts.get(project.id);
82
+ const finished = status === "done" || status === "partial";
83
+ const retry = status === "running" ? retryingAt(stages) : undefined;
77
84
  return {
78
85
  ...project,
79
86
  status,
@@ -89,7 +96,18 @@ export function projectRoutes(deps) {
89
96
  ? { stepFailures: failures.get(project.id) ?? [] }
90
97
  : {}),
91
98
  ...(holding === undefined ? {} : { holding }),
99
+ ...(retry === undefined ? {} : { retryAt: retry }),
92
100
  ...(waiting === undefined ? {} : { limitWaits: waiting.map(listingWait) }),
101
+ ...(samples.has(project.id) ? { sample: true } : {}),
102
+ ...(finished && made?.finishedAt !== undefined ? { finishedAt: made.finishedAt } : {}),
103
+ ...(made?.durationSeconds === undefined
104
+ ? {}
105
+ : { durationSeconds: made.durationSeconds }),
106
+ ...(made?.outputs === undefined ? {} : { outputs: made.outputs }),
107
+ ...(status === "partial" && made?.failedSteps !== undefined
108
+ ? { failedSteps: made.failedSteps }
109
+ : {}),
110
+ ...(made?.outdated === true ? { outdated: true } : {}),
93
111
  };
94
112
  });
95
113
  return c.json({ projects });
@@ -47,7 +47,7 @@ export function promptRoutes(deps) {
47
47
  ? refused(c, { ok: false, reason: "not-found" }, "prompt")
48
48
  : problem(c, {
49
49
  status: 400,
50
- detail: "Only an Image prompt can be marked photorealistic. Open the prompt from the Image tab in Library → Prompts.",
50
+ detail: "Only an Image prompt can be marked photorealistic. Open the prompt under Image in Prompts.",
51
51
  });
52
52
  })
53
53
  // Every saved version, newest first (`slices/library/history.ts`).
@@ -103,7 +103,7 @@ export function refused(c, failure, noun) {
103
103
  case "not-found":
104
104
  return problem(c, {
105
105
  status: 404,
106
- detail: `This ${noun} no longer exists; it may have been deleted. Go back to the Library to pick another.`,
106
+ detail: `This ${noun} no longer exists; it may have been deleted. Go back to Prompts to pick another.`,
107
107
  });
108
108
  }
109
109
  }
@@ -5,11 +5,11 @@ import { z } from "zod";
5
5
  import { keyProbes } from "../../adapters/key-probes.js";
6
6
  import { catalogueModelInfo } from "../../catalog/model-info.js";
7
7
  import { videoModelsOf } from "../../catalog/schema.js";
8
- import { defaultSpeechVoice, detectSpeechCached } from "../../kernel/ports/system-speech.js";
8
+ import { defaultSpeechVoice, detectSpeechCached, speechInstall, } from "../../kernel/ports/system-speech.js";
9
9
  import { switchAllRetired, switchRetiredModel } from "../../slices/model-upkeep/switch.js";
10
10
  import { retiredModelUsage, usageKinds, usageSlots } from "../../slices/model-upkeep/usage.js";
11
11
  import { cliPathMaxLength, saveCliPath } from "../../slices/settings/cli-paths.js";
12
- import { dismissFirstRun, firstRunStatus } from "../../slices/settings/first-run.js";
12
+ import { firstRunStatus } from "../../slices/settings/first-run.js";
13
13
  import { checkProviderHealth } from "../../slices/settings/health.js";
14
14
  import { keyGuides } from "../../slices/settings/key-guides.js";
15
15
  import { testProviderKey } from "../../slices/settings/key-test.js";
@@ -63,16 +63,12 @@ export function providerRoutes(deps) {
63
63
  };
64
64
  const standingsOf = (id, models) => deps.modelChecks?.standings(id, models);
65
65
  return (new Hono()
66
- // The step lists Settings → Providers shows beside each key field.
66
+ // The step lists Settings → Keys & services shows beside each key field.
67
67
  .get("/key-guides", (c) => c.json({ guides: keyGuides }))
68
68
  // What the first launch found, and the providers Play picks by default.
69
69
  .get("/first-run", async (c) => {
70
70
  c.header("Cache-Control", "no-store");
71
71
  return c.json(await firstRunStatus({ db: deps.db, ...(deps.modelsFor === undefined ? {} : { modelsFor: deps.modelsFor }) }, await providerStatuses(readiness)));
72
- })
73
- .post("/first-run/dismiss", (c) => {
74
- dismissFirstRun(deps.db);
75
- return c.body(null, 204);
76
72
  })
77
73
  // "Check all": every CLI signed in, every key valid, every chosen model still offered and
78
74
  // answering for the key. `?provider=codex` checks that one only (its row's Check again,
@@ -146,9 +142,11 @@ export function providerRoutes(deps) {
146
142
  // What Settings draws its rails from and Play its dropdowns: every provider, with
147
143
  // the one fact that decides whether it is selectable.
148
144
  .get("/", async (c) => c.json({ providers: await providerStatuses(readiness) }))
149
- // The system voice's speech programs and their voices, for Settings → Voices' picker.
145
+ // The system voice's speech programs and their voices, for Settings → Voices' picker, and
146
+ // with none, how to install one from a terminal (the commands apart from the sentence).
150
147
  .get("/system-voice/voices", async (c) => {
151
- const found = await detectSpeechCached(deps.probe, deps.speechHost ?? process);
148
+ const host = deps.speechHost ?? process;
149
+ const found = await detectSpeechCached(deps.probe, host);
152
150
  return c.json({
153
151
  engines: found.engines.map((engine) => ({
154
152
  id: engine.id,
@@ -157,6 +155,7 @@ export function providerRoutes(deps) {
157
155
  defaultVoice: defaultSpeechVoice(engine)?.id ?? null,
158
156
  })),
159
157
  issue: found.issue ?? null,
158
+ install: found.engines.length === 0 ? (speechInstall(host) ?? null) : null,
160
159
  });
161
160
  })
162
161
  .get("/:id/models", zValidator("param", providerParam, onInvalid), async (c) => {
@@ -228,7 +227,7 @@ export function providerRoutes(deps) {
228
227
  if (candidate !== undefined && !candidate.success)
229
228
  return problem(c, {
230
229
  status: 400,
231
- detail: `The pasted ${providerById(id).displayName} key could not be tested: it is empty or longer than any key. Paste the whole key in Settings → Providers → ${providerById(id).displayName}, then choose Test again.`,
230
+ detail: `The pasted ${providerById(id).displayName} key could not be tested: it is empty or longer than any key. Paste the whole key in Settings → Keys & services → ${providerById(id).displayName}, then choose Test again.`,
232
231
  });
233
232
  return c.json(await testProviderKey({ db: deps.db, clock: deps.clock, fetch: deps.fetch, probes: keyProbes }, id, candidate?.data.key));
234
233
  })
@@ -9,7 +9,7 @@ export function refusal(c, reason, rules) {
9
9
  export function revisionRequired(c) {
10
10
  return problem(c, {
11
11
  status: 409,
12
- detail: "This change is now made from the Edit tab. Choose Edit project, save your change, then use Choose what to remake (in the project's More menu).",
12
+ detail: "This change is now made in Edit settings: save it there, then use Choose what to make again (in the project's More menu).",
13
13
  extensions: { reason: "revision-required" },
14
14
  });
15
15
  }
@@ -8,11 +8,11 @@ const body = z.object({
8
8
  enabled: z.boolean(),
9
9
  httpsPort: z.number().int().min(1).max(65535).optional(),
10
10
  });
11
- // Settings → General → Remote access: Slopify's page on the person's other devices through
11
+ // Settings → Remote access: Slopify's page on the person's other devices through
12
12
  // Tailscale serve. Reading it is open to agents (get_remote_access); turning it on or off is
13
13
  // the person's alone, from this computer.
14
14
  export function remoteAccessRoutes(service) {
15
- const unavailable = "Remote access can't be set from this Slopify. Start it with npx @gentbajko/slopify and try Settings → General → Remote access again.";
15
+ const unavailable = "Remote access can't be set from this Slopify. Start it with npx @gentbajko/slopify and try Settings → Remote access again.";
16
16
  return new Hono()
17
17
  .get("/", async (c) => {
18
18
  if (service === undefined)
@@ -27,12 +27,12 @@ export function remoteAccessRoutes(service) {
27
27
  if (actorOf(c) !== "person")
28
28
  return problem(c, {
29
29
  status: 403,
30
- detail: "Remote access is the person's to turn on or off, in Settings → General → Remote access. Agents and the assistant can only read it (get_remote_access).",
30
+ detail: "Remote access is the person's to turn on or off, in Settings → Remote access. Agents and the assistant can only read it (get_remote_access).",
31
31
  });
32
32
  if (viaProxy(c.req.raw.headers))
33
33
  return problem(c, {
34
34
  status: 403,
35
- detail: "Remote access is turned on or off only on the computer Slopify runs on. Open Settings → General → Remote access there, at http://127.0.0.1 and Slopify's port.",
35
+ detail: "Remote access is turned on or off only on the computer Slopify runs on. Open Settings → Remote access there, at http://127.0.0.1 and Slopify's port.",
36
36
  });
37
37
  const { enabled, httpsPort } = c.req.valid("json");
38
38
  return c.json(await service.set(enabled, httpsPort));
@@ -5,12 +5,12 @@ export const hostOnlyWrites = [
5
5
  {
6
6
  method: "PUT|DELETE",
7
7
  path: new RegExp(`^/api/providers/${id}/key/?$`),
8
- what: "Provider keys are saved and removed (Settings → Providers)",
8
+ what: "Provider keys are saved and removed (Settings → Keys & services)",
9
9
  },
10
10
  {
11
11
  method: "PUT",
12
12
  path: new RegExp(`^/api/providers/${id}/path/?$`),
13
- what: "A CLI's path is set (Settings → Providers)",
13
+ what: "A CLI's path is set (Settings → Keys & services)",
14
14
  },
15
15
  {
16
16
  method: "PUT",
@@ -134,7 +134,7 @@ export function reviewRoutes(deps) {
134
134
  const lead = result.reason === "running"
135
135
  ? "This item, or work that depends on it, is still running. Wait for it to finish, or Pause the project, then use Redo again."
136
136
  : result.reason === "readiness"
137
- ? "Slopify can't make this item again yet. Fix what is listed in Settings → Providers or Edit project, then use Redo again."
137
+ ? "Slopify can't make this item again yet. Fix what is listed in Settings → Keys & services or Edit settings, then use Redo again."
138
138
  : result.reason === "conflict" || result.reason === "control-changed"
139
139
  ? "This project changed since the page loaded. Reload the project page, then use Redo again."
140
140
  : result.reason === "invalid-selection"
@@ -80,7 +80,7 @@ function unavailable(c, reason) {
80
80
  return problem(c, {
81
81
  status: 404,
82
82
  detail: reason === "missing-file"
83
- ? "This file was deleted or moved from the project folder. In the Edit tab, use Choose what to remake (in the project's More menu) to make it again."
83
+ ? "This file was deleted or moved from the project folder. Use Choose what to make again (in the project's More menu) to make it again."
84
84
  : reason === "no-images"
85
85
  ? "There are no saved files of this kind to put in the zip yet. Wait for the step that makes them to finish, then download again."
86
86
  : "This file is no longer part of the project. Reload the page to see its current files.",
@@ -36,11 +36,11 @@ const details = {
36
36
  conflict: "This project changed while you were editing. Reload the page to see the latest, then make your change again.",
37
37
  "idempotency-conflict": "This change was already sent with different details. Reload the page, then try again.",
38
38
  "invalid-edit": "Some of your changes are not valid. Fix the highlighted fields and save again.",
39
- "stale-preview": "What needs rebuilding changed since you opened this review. Cancel it and open Choose what to remake (in the project's More menu) again to see the latest.",
39
+ "stale-preview": "What needs rebuilding changed since you opened this review. Cancel it and open Choose what to make again (in the project's More menu) again to see the latest.",
40
40
  "invalid-selection": "Some of the outputs you picked can no longer be rebuilt. Reload the page and choose again.",
41
- "review-required": 'This rebuild touches content you supplied yourself. Tick "Keep the provided content" for each item in the review, then choose Start rebuild.',
42
- "cost-ack-required": "Some costs for this rebuild are unknown. Tick the box confirming you understand, then choose Start rebuild.",
43
- readiness: "Slopify cannot run this rebuild yet. Fix the problems listed, then try again.",
41
+ "review-required": "This touches content you supplied yourself. Tick “Keep the provided content” for each item in the review, then press the button that makes them again.",
42
+ "cost-ack-required": "Some prices aren't known in advance. Tick “Start even though …” in the review, then press the button that makes them again.",
43
+ readiness: "This can't start yet. Fix the items listed, then try again.",
44
44
  };
45
45
  function refused(c, deps, projectId, value) {
46
46
  return refusal(c, value.reason, {
@@ -2,6 +2,7 @@ import { zValidator } from "@hono/zod-validator";
2
2
  import { Hono } from "hono";
3
3
  import { HTTPException } from "hono/http-exception";
4
4
  import { z } from "zod";
5
+ import { setupSectionNames } from "../../slices/project-templates/setup-sections.js";
5
6
  import { calendarRange } from "../../slices/schedules/agenda.js";
6
7
  import { rejectHeldTopics, restoreRejectedTopics } from "../../slices/schedules/held-bulk.js";
7
8
  import { preparedTopics, prepareTopic } from "../../slices/schedules/prepare.js";
@@ -40,14 +41,14 @@ function refused(c, result) {
40
41
  conflict: "This schedule changed while you were editing, possibly because a run just started. Reload the page to see the latest, then make your change again.",
41
42
  "cancel-required": "This schedule is still active. Cancel it first, then delete it. Completed schedules can be deleted straight away.",
42
43
  "missing-template": "The template you picked was deleted or changed. Choose a template again, then save.",
43
- "unsupported-media": "This template uses audio, images, a thumbnail, shorts background music or an ambient sound file you supplied, so it cannot run on a schedule: a scheduled run has no one to attach the file again. Pick a template that generates these instead, or on Play remove the music under Outputs → More shorts options (or pick Rain, Fireplace or Wind under Video and style → Ambient sound) and save the template again.",
44
+ "unsupported-media": `This template uses audio, images, a thumbnail, shorts background music or an ambient sound file you supplied, so it cannot run on a schedule: a scheduled run has no one to attach the file again. Pick a template that generates these instead, or open this one in Edit template, remove the music under ${setupSectionNames.shorts} (or pick Rain, Fireplace or Wind as the Ambient sound under ${setupSectionNames.video}) and save it again.`,
44
45
  "not-due": "This one-off time has already passed. Choose a time in the future.",
45
46
  "spend-limit": "The estimated cost is above this schedule's spend limit, or some prices are unknown. Raise the spend limit or choose models with known prices.",
46
- readiness: "A provider this template needs is not ready, for example a missing API key, model or voice. Check Settings → Providers, then try again.",
47
+ readiness: "A provider this template needs is not ready, for example a missing API key, model or voice. Check Settings → Keys & services, then try again.",
47
48
  "queue-full": `A schedule's topic queue holds at most ${String(queueMax)} topics. Remove some under Schedules → Edit, then try again.`,
48
49
  "topic-not-found": "This topic was already approved, turned down or used. Reload the page to see the topics waiting now.",
49
50
  busy: "Slopify is already generating topics for this schedule. Wait for it to finish, then try again.",
50
- "not-ready": "This schedule's template has no version marked ready. Open it in Library → Templates → Open studio and press Mark ready.",
51
+ "not-ready": "This schedule's template has no version approved for schedules. Open it from Templates with Edit template, then choose More → Approve for schedules.",
51
52
  "invalid-topics": "Some topics name keywords this template does not use. Check each topic under Schedules → Edit and save again.",
52
53
  };
53
54
  return refusal(c, result.reason, {
@@ -165,7 +166,8 @@ function routes(deps) {
165
166
  })
166
167
  // Which queued topics are prepared ahead, and their projects.
167
168
  .get("/:id/prepared", zValidator("param", id, onInvalid), (c) => c.json({ prepared: preparedTopics(service(), c.req.valid("param").id) }))
168
- // Prepare one queued topic now: everything but the video, which its scheduled day renders.
169
+ // Make ahead one queued topic: everything but the video now, which its scheduled day puts
170
+ // together.
169
171
  .post("/:id/topics/prepare", zValidator("param", id, onInvalid), zValidator("json", prepareTopicBody, onInvalid), async (c) => {
170
172
  const result = await prepareTopic(service(), {
171
173
  scheduleId: c.req.valid("param").id,
@@ -175,19 +177,19 @@ function routes(deps) {
175
177
  return c.json({ projectId: result.projectId, title: result.title }, 201);
176
178
  const detail = {
177
179
  "not-found": "This topic is no longer in the schedule's queue. Reload the page to see the queue as it is now.",
178
- "already-prepared": "This topic is already prepared: its project is in Projects under the same title.",
179
- paused: "Its schedule is paused, so it makes nothing, ahead or on its day. Resume it under Calendar → Schedules, then press Prepare again.",
180
- "missing-template": "This schedule's template was deleted, so there is nothing to prepare from. Choose another template under Edit.",
181
- "spend-limit": "Preparing it would go over this schedule's spend limit. Raise the limit under Edit, or prepare it from Play instead.",
182
- design: "A design schedule makes each piece on its day, so there is nothing to prepare ahead. To have a piece now, open the design template (Library → Designs → Templates) and press Make a piece; to have it sooner on a schedule, give its row an earlier date in the Schedule section.",
180
+ "already-prepared": "This topic is already made ahead: its project is in Projects under the same title.",
181
+ paused: "Its schedule is paused, so it makes nothing, ahead or on its day. Resume it under Calendar → Schedules, then press Make ahead again.",
182
+ "missing-template": "This schedule's template was deleted, so there is nothing to make it from. Choose another template under Edit.",
183
+ "spend-limit": "Making it ahead would go over this schedule's spend limit. Raise the limit under Edit, or make it from Create instead.",
184
+ design: "A poster schedule makes each poster on its day, so there is nothing to make ahead. To have one now, open its poster template (Templates → Poster templates) and press Make a poster; to have it sooner on a schedule, give its row an earlier date in the Schedule section.",
183
185
  };
184
186
  return problem(c, {
185
187
  status: result.reason === "not-found" ? 404 : 409,
186
188
  detail: (result.reason === "not-ready" ? result.detail : undefined) ??
187
189
  detail[result.reason] ??
188
190
  (result.detail === undefined
189
- ? `This topic couldn't be prepared (${result.reason}). Open Play with the schedule's template to see what the setup needs, then try again.`
190
- : `This topic couldn't be prepared: ${result.detail} Fix it in the schedule's template (Library → Templates), then try again.`),
191
+ ? `This topic couldn't be made ahead (${result.reason}). Open Create with the schedule's template to see what the setup needs, then try again.`
192
+ : `This topic couldn't be made ahead: ${result.detail} Fix it in the schedule's template (Templates → Edit template), then try again.`),
191
193
  extensions: { reason: result.reason },
192
194
  });
193
195
  })
@@ -88,7 +88,11 @@ export function settingsRoutes(deps) {
88
88
  return c.json({
89
89
  nvidia: status.available
90
90
  ? { available: true }
91
- : { available: false, reason: status.reason },
91
+ : {
92
+ available: false,
93
+ reason: status.reason,
94
+ ...(status.detail === undefined ? {} : { detail: status.detail }),
95
+ },
92
96
  });
93
97
  })
94
98
  // The named links `{{Name}}` placeholders in YouTube descriptions fill from
@@ -100,7 +104,7 @@ export function settingsRoutes(deps) {
100
104
  if (refused !== undefined)
101
105
  return problem(c, {
102
106
  status: 400,
103
- detail: `The channel links weren't saved: ${refused} Then press Save in Settings → Channel links.`,
107
+ detail: `The channel links weren't saved: ${refused} Then press Save in Settings → YouTube Studio and posting.`,
104
108
  });
105
109
  writeChannelLinks(deps.db, links);
106
110
  return c.json({ links: readChannelLinks(deps.db) });
@@ -5,6 +5,7 @@ import { projectById } from "../../slices/admission/repo.js";
5
5
  import { webhookBody } from "../../slices/notifications/address.js";
6
6
  import { sendFailureText } from "../../slices/notifications/send.js";
7
7
  import { readNotificationUrl } from "../../slices/notifications/settings.js";
8
+ import { setupSectionNames } from "../../slices/project-templates/setup-sections.js";
8
9
  import { waitsForRelease } from "../../slices/social/due.js";
9
10
  import { postingExtension, writeExtensionSites } from "../../slices/social/extension.js";
10
11
  import { platformLabels } from "../../slices/social/model.js";
@@ -50,7 +51,7 @@ export function socialRoutes(deps) {
50
51
  : {
51
52
  ok: false,
52
53
  sentence: drawn?.message ??
53
- "The design couldn't be drawn. Open it in Library → Designs to check it, then reload Prepare upload.",
54
+ "The design couldn't be drawn. Open it in Designs to check it, then close Copy for YouTube Studio and open it again.",
54
55
  };
55
56
  },
56
57
  }),
@@ -110,7 +111,7 @@ export function socialRoutes(deps) {
110
111
  config?.text !== "prompt")
111
112
  return problem(c, {
112
113
  status: 409,
113
- detail: `A post written by a post prompt is a paid step, so it is picked in the template, in Template Studio → Upload pack, where Save shows its cost. For this project, pick a free text source in Prepare upload → ${platformLabels[platform]} → Settings.`,
114
+ detail: `A post written by a post prompt is a paid step, so it is picked in the template, in Edit template → ${setupSectionNames.youtube}, where Save shows its cost. For this project, pick a free text source in Copy for YouTube Studio → ${platformLabels[platform]} → Settings.`,
114
115
  });
115
116
  writeChoice(deps.db, { projectId, platform }, choice, deps.clock.now());
116
117
  changed(projectId);
@@ -204,7 +205,7 @@ export function socialRoutes(deps) {
204
205
  if (next === undefined)
205
206
  return problem(c, {
206
207
  status: 404,
207
- detail: "Nothing is waiting to be filled on this site. In Slopify, open the project, press Prepare upload, then Fill in the site's post.",
208
+ detail: "Nothing is waiting to be filled on this site. In Slopify, open the project, press Copy for YouTube Studio, then Fill in the site's post.",
208
209
  });
209
210
  changed(next.pack.projectId);
210
211
  return c.json(next);
@@ -277,6 +278,6 @@ function unknownProject(c) {
277
278
  function unknownPost(c) {
278
279
  return problem(c, {
279
280
  status: 404,
280
- detail: "That project no longer posts to this site. In Slopify, open the project's Prepare upload and press Fill again.",
281
+ detail: "That project no longer posts to this site. In Slopify, open the project's Copy for YouTube Studio and press Fill again.",
281
282
  });
282
283
  }
@@ -47,7 +47,7 @@ export function stagingRoutes(deps) {
47
47
  return problem(c, {
48
48
  status: result.reason === "in-use" ? 409 : 404,
49
49
  detail: result.reason === "in-use"
50
- ? "This file is still attached to a draft in Play. Remove it from the draft first."
50
+ ? "This file is still attached to a draft on Create. Remove it from the draft first."
51
51
  : "This uploaded file no longer exists; it may have been removed already. Reload the page.",
52
52
  });
53
53
  }