@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
@@ -21,6 +21,7 @@ const standingRow = z.object({
21
21
  progress_current: z.number().nullable(),
22
22
  progress_total: z.number().nullable(),
23
23
  source: z.enum(stageSources),
24
+ retry_at: z.string().nullable(),
24
25
  admitted: z.number(),
25
26
  parked: z.number(),
26
27
  });
@@ -83,14 +84,15 @@ export function listProjects(db) {
83
84
  .all()
84
85
  .map((row) => toProject(projectRow.parse(row)));
85
86
  }
86
- // What 07 Projects needs of every project's stages and no more: the state each one is in
87
- // and how far through itself a running one is. One statement for the whole list rather
88
- // than `stagesOf` per row, and five columns rather than the whole stage table, because
89
- // the screen shows a status word and a meter and reads nothing else from a stage. One project's
90
- // alone when `projectId` is given.
87
+ // What 07 Projects needs of every project's stages and no more: the state each one is in,
88
+ // how far through itself a running one is, and when one waiting to run again by itself tries
89
+ // (without it, `derive` reads that run as waiting its turn while its own page says Running).
90
+ // One statement for the whole list rather than `stagesOf` per row, and a few columns rather
91
+ // than the whole stage table, because the screen shows a status word and a meter and reads
92
+ // nothing else from a stage. One project's alone when `projectId` is given.
91
93
  export function stageStandingsByProject(db, projectId) {
92
94
  const grouped = new Map();
93
- const select = `SELECT project_id, kind, state, progress_current, progress_total, source, ${admittedInItsState("stages")} AS admitted, ${parkedOnReview("stages")} AS parked FROM stages${projectId === undefined ? "" : " WHERE project_id=?"} ORDER BY rowid`;
95
+ const select = `SELECT project_id, kind, state, progress_current, progress_total, source, retry_at, ${admittedInItsState("stages")} AS admitted, ${parkedOnReview("stages")} AS parked FROM stages${projectId === undefined ? "" : " WHERE project_id=?"} ORDER BY rowid`;
94
96
  const rows = projectId === undefined ? db.prepare(select).all() : db.prepare(select).all(projectId);
95
97
  for (const row of rows) {
96
98
  const parsed = standingRow.parse(row);
@@ -100,6 +102,7 @@ export function stageStandingsByProject(db, projectId) {
100
102
  progressCurrent: parsed.progress_current,
101
103
  progressTotal: parsed.progress_total,
102
104
  source: parsed.source,
105
+ ...(parsed.retry_at === null ? {} : { retryAt: parsed.retry_at }),
103
106
  ...(parsed.admitted === 1 ? { admitted: true } : {}),
104
107
  ...(parsed.parked === 1 ? { parked: true } : {}),
105
108
  };
@@ -88,7 +88,7 @@ export function admit(input) {
88
88
  }
89
89
  }
90
90
  if (needsLlmFor(draft, input.keywordUses) && !chosen(draft.llm)) {
91
- fields.push({ field: "llm", message: "Choose a text (LLM) provider and model." });
91
+ fields.push({ field: "llm", message: "Choose a Service and a Model under Written by." });
92
92
  }
93
93
  if (sources.article === "generate" && blank(draft.articlePrompt)) {
94
94
  fields.push({ field: "articlePrompt", message: "Pick an article prompt." });
@@ -98,7 +98,7 @@ export function admit(input) {
98
98
  fields.push(...voicesFields(draft));
99
99
  if (sources.audio === "generate") {
100
100
  if (voiced === undefined || !chosen(voiced)) {
101
- fields.push({ field: "audio", message: "Choose a narration provider and model." });
101
+ fields.push({ field: "audio", message: "Choose a Service and a Model under Read by." });
102
102
  }
103
103
  else if (voiced.voice.trim() === "") {
104
104
  fields.push({ field: "audio.voice", message: "Choose a voice for the narration." });
@@ -113,7 +113,7 @@ export function admit(input) {
113
113
  usesShorts(draft) ||
114
114
  usesAnimation(draft)) &&
115
115
  !chosen(draft.images)) {
116
- fields.push({ field: "images", message: "Choose an image provider and model." });
116
+ fields.push({ field: "images", message: "Choose a Service and a Model under Images by." });
117
117
  }
118
118
  if ((sources.thumbnail === "from_prompt" || sources.thumbnail === "prompt_by_llm") &&
119
119
  blank(draft.thumbnailPrompt)) {
@@ -122,7 +122,7 @@ export function admit(input) {
122
122
  if (sources.audio === "off" && draft.subtitles !== undefined && draft.subtitles.mode !== "off") {
123
123
  fields.push({
124
124
  field: "subtitles.mode",
125
- message: "Subtitles need narration. Turn narration on, or turn subtitles off.",
125
+ message: "Captions need narration. Turn narration on, or turn captions off.",
126
126
  });
127
127
  }
128
128
  fields.push(...youtubeDescriptionFields(draft));
@@ -381,7 +381,7 @@ export function voicesFields(draft) {
381
381
  !draft.voices.speakers.some((speaker) => speaker.voice.provider === "inworld" && speaker.voice.model === "inworld-tts-2"))
382
382
  fields.push({
383
383
  field: "narrationPrompt",
384
- message: "Narration preparation adds delivery cues only to speakers on Inworld's TTS-2 model, and no speaker uses it. Choose that model for a speaker under Speakers, or clear Narration Preparation under Narration → Audio Advanced.",
384
+ message: "Narration preparation adds delivery cues only to speakers on Inworld's TTS-2 model, and no speaker uses it. Choose that model for a speaker under Speakers, or set Narration preparation to Off under Narration → More narration settings.",
385
385
  });
386
386
  return fields;
387
387
  }
@@ -489,8 +489,8 @@ export function ambientBedFields(draft, staged) {
489
489
  function chosen(choice) {
490
490
  return choice !== undefined && choice.provider.trim() !== "" && choice.model.trim() !== "";
491
491
  }
492
- // The aliases a run narrates with: its copied Library → Aliases while Use narration aliases is
493
- // on for generated audio, otherwise none. Any voice provider: an alias is plain text.
492
+ // The aliases a run narrates with: its copied Pronunciation list while Use your pronunciation
493
+ // list is on for generated audio, otherwise none. Any voice provider: an alias is plain text.
494
494
  export function narrationAliasesOf(draft) {
495
495
  return draft.sources.audio === "generate" && draft.audio?.useNarrationAliases === true
496
496
  ? (draft.narrationAliases ?? [])
@@ -28,8 +28,13 @@ export const stageStateWords = {
28
28
  export const heldWord = "Waiting for your review";
29
29
  export const waitingForAgentWord = "Waiting for the agent";
30
30
  export const stoppedWord = "Stopped";
31
- export const preparedWord = "Prepared";
31
+ // Made ahead by its schedule (Make ahead): everything but the video is made, and the video is
32
+ // put together on the run's day.
33
+ export const preparedWord = "Waiting for its day";
32
34
  export const keptAsIsWord = "Kept as is";
35
+ // A running run whose only movement is a step that failed on something time can fix (a rate
36
+ // limit, a dropped connection) and runs again by itself at a set time.
37
+ export const retryingWord = "Waiting to try again";
33
38
  export const heldStateWords = [
34
39
  heldWord,
35
40
  waitingForAgentWord,
@@ -38,15 +43,25 @@ export const heldStateWords = [
38
43
  keptAsIsWord,
39
44
  ];
40
45
  // A run that ran and is held: a waiting project with some of its run done (`progress` is the
41
- // run's share done, `progressOf`) or a step failed, or one stopped or parked on an agent's review
42
- // before it got going. One with none done that waits for neither is Queued, waiting its turn.
46
+ // run's share done, `progressOf`) or a step failed, one a checkpoint holds (it reached it with
47
+ // every step before it supplied), or one stopped or parked on an agent's review before it got
48
+ // going. One with none done that waits for none of these is Queued, waiting its turn.
43
49
  export function heldRun(project) {
44
50
  return (project.status === "pending" &&
45
51
  (project.progress > 0 ||
46
52
  project.stepFailed === true ||
53
+ (project.holding?.length ?? 0) > 0 ||
47
54
  project.stopped === true ||
48
55
  project.waitingForAgent === true));
49
56
  }
57
+ // When the run tries again by itself: the soonest wait of a step that failed on something time
58
+ // can fix, while no step runs. Undefined while one runs (the run reads Running) or none waits.
59
+ export function retryingAt(stages) {
60
+ if (stages.some((stage) => stage.state === "running"))
61
+ return undefined;
62
+ const waits = stages.flatMap((stage) => stage.state === "pending" && typeof stage.retryAt === "string" ? [stage.retryAt] : []);
63
+ return waits.toSorted()[0];
64
+ }
50
65
  // Whether the person is the one it waits for (to approve, or to Continue the run): not when it
51
66
  // is Kept as is, waits for an agent or for its scheduled day.
52
67
  function heldForPerson(project) {
@@ -78,9 +93,18 @@ export function runningNow(project) {
78
93
  export function queuedRun(project) {
79
94
  return project.status === "pending" && !heldRun(project);
80
95
  }
81
- // An uploaded project already reads "done" from the projects route (`uploadedAndSettled`).
96
+ // A settled project already reads "done" from the projects route (`settledProjects`). A
97
+ // running run that only waits to try a step again says so.
82
98
  export function projectStateWord(project) {
83
- return heldRun(project) ? heldStateWord(project) : projectStateWords[project.status];
99
+ if (heldRun(project))
100
+ return heldStateWord(project);
101
+ if (retryingRun(project))
102
+ return retryingWord;
103
+ return projectStateWords[project.status];
104
+ }
105
+ // Running, and the one thing it does is wait to try a failed step again at a set time.
106
+ export function retryingRun(project) {
107
+ return project.status === "running" && project.retryAt !== undefined;
84
108
  }
85
109
  // A run that ran and now waits for the person, held or paused: Home's Needs you, the Projects
86
110
  // "Needs you" filter and its waiting-first sort.
@@ -108,3 +132,8 @@ export function stepFailedWhileWaiting(status, stages) {
108
132
  export function failedRun(project) {
109
133
  return project.status === "failed" || project.stepFailed === true;
110
134
  }
135
+ // Everything a run asks of the person, the one set Home's Needs you lists and the Projects
136
+ // "Needs you" filter holds: a decision (approve, Continue the run, resume) or a failure to fix.
137
+ export function needsYou(project) {
138
+ return waitsForPerson(project) || failedRun(project);
139
+ }
@@ -1,5 +1,5 @@
1
1
  import { z } from "zod";
2
- // The cost sentence on an ask card, from the estimate the preview (Choose what to remake) or
2
+ // The cost sentence on an ask card, from the estimate the preview (Choose what to make again) or
3
3
  // Play's review already showed for this exact work.
4
4
  const money = (value) => `$${value.toFixed(2)}`;
5
5
  function range(low, high) {
@@ -37,7 +37,7 @@ export const turnStates = ["running", "done", "failed", "stopped"];
37
37
  const providersFix = {
38
38
  kind: "settings",
39
39
  section: "providers",
40
- label: "Open Settings → Providers",
40
+ label: "Open Settings → Keys & services",
41
41
  };
42
42
  const signIn = {
43
43
  "claude-code": "claude auth login",
@@ -46,13 +46,20 @@ const signIn = {
46
46
  function blocked(cli, state) {
47
47
  const name = assistantCliNames[cli];
48
48
  if (!state.installed)
49
- return `The assistant can't start: ${name} isn't installed on this computer. Install it, then set it up in Settings → Providers.`;
49
+ return `The assistant can't start: ${name} isn't installed on this computer. Install it, then set it up in Settings → Keys & services.`;
50
50
  if (state.issue !== undefined)
51
51
  return `The assistant can't start: ${state.issue}`;
52
52
  if (state.login === "signed-out")
53
53
  return `The assistant can't start: ${name} is signed out. Run "${signIn[cli]}" in a terminal, then send your message again.`;
54
54
  return undefined;
55
55
  }
56
+ // The CLIs that could answer now, in Auto's order: the panel offers "Answer with" only when
57
+ // there are two to choose between.
58
+ export function readyClis(states, container) {
59
+ if (container)
60
+ return [];
61
+ return assistantClis.filter((cli) => blocked(cli, states[cli]) === undefined);
62
+ }
56
63
  // Auto takes Claude Code when it is ready, else Codex. A sign-in that couldn't be read is not
57
64
  // a refusal: the CLI says so itself if it really is signed out.
58
65
  export function assistantCliOf(states, preference, container) {
@@ -73,7 +80,7 @@ export function assistantCliOf(states, preference, container) {
73
80
  return { ok: true, cli };
74
81
  return {
75
82
  ok: false,
76
- reason: "The assistant runs on Claude Code or Codex, and neither is ready on this computer. Set up Claude Code or Codex in Settings → Providers.",
83
+ reason: "The assistant runs on Claude Code or Codex, and neither is ready on this computer. Set up Claude Code or Codex in Settings → Keys & services.",
77
84
  fix: providersFix,
78
85
  };
79
86
  }
@@ -1,7 +1,7 @@
1
1
  import { causedBy } from "../../kernel/errors.js";
2
2
  import { redact } from "../../kernel/log.js";
3
3
  import { asksOf, consumeGrant, decideAsk, openAsk } from "./asks.js";
4
- import { assistantCliNames, assistantCliOf, } from "./model.js";
4
+ import { assistantCliNames, assistantCliOf, readyClis, } from "./model.js";
5
5
  import { screenNames } from "./names.js";
6
6
  import { assistantSystemPrompt, turnPrompt } from "./prompt.js";
7
7
  import { addMessage, cliPreferenceOf, createSession, latestSessionId, messagesOf, sessionExists, transcriptOf, } from "./repo.js";
@@ -182,7 +182,7 @@ export function createAssistant(deps) {
182
182
  if (self.stoppedBy === undefined) {
183
183
  const detail = redact(causedBy(error));
184
184
  deps.log.write("warn", "assistant.turn", { detail });
185
- say(sessionId, turnId, "error", `Slopify couldn't run the assistant's CLI: ${detail}. Check it in Settings → Providers, then send your message again.`, { kind: "cli" });
185
+ say(sessionId, turnId, "error", `Slopify couldn't run the assistant's CLI: ${detail}. Check it in Settings → Keys & services, then send your message again.`, { kind: "cli" });
186
186
  }
187
187
  }
188
188
  if (self.stoppedBy !== undefined) {
@@ -264,9 +264,11 @@ export function createAssistant(deps) {
264
264
  tokens: deps.tokens,
265
265
  status: async () => {
266
266
  const preference = cliPreferenceOf(deps.db);
267
+ const states = await deps.clis();
267
268
  return {
268
269
  preference,
269
- choice: assistantCliOf(await deps.clis(), preference, deps.container),
270
+ choice: assistantCliOf(states, preference, deps.container),
271
+ ready: readyClis(states, deps.container),
270
272
  };
271
273
  },
272
274
  session: (id) => {
@@ -51,7 +51,7 @@ export function warningsOf(signals, snoozes, now) {
51
51
  found.push({
52
52
  key: `template-not-ready:${schedule.id}`,
53
53
  severity: "problem",
54
- text: `“${schedule.name}” skips its runs because its template “${schedule.notReadyTemplate}” has no version marked ready. Open it in Library → Templates → Open studio and press Mark ready.`,
54
+ text: `“${schedule.name}” skips its runs because its template “${schedule.notReadyTemplate}” has no version approved for schedules. Open it from Templates with Edit template, then choose More → Approve for schedules.`,
55
55
  fix: { kind: "templates", label: "Open Templates" },
56
56
  });
57
57
  if (schedule.topicsError !== null && schedule.generation !== "off")
@@ -60,7 +60,7 @@ export function createBackupService(deps) {
60
60
  }
61
61
  catch (error) {
62
62
  deps.log.write("warn", "backups.prune", { detail: String(error) });
63
- detail = `The backup was saved, but older backups in ${folder} could not be removed (${errnoCode(error) ?? "unknown error"}), so more than ${settings.keep} are kept. Check Slopify can delete files in that folder, or pick another Folder in Settings → Backups.`;
63
+ detail = `The backup was saved, but older backups in ${folder} could not be removed (${errnoCode(error) ?? "unknown error"}), so more than ${settings.keep} are kept. Check Slopify can delete files in that folder, or pick another Folder in Settings → Backup & storage.`;
64
64
  }
65
65
  record({
66
66
  ...attempt,
@@ -273,17 +273,17 @@ function named(projects) {
273
273
  .join(", ");
274
274
  return projects.length > 3 ? `${titles} and ${projects.length - 3} more` : titles;
275
275
  }
276
- // What failed, why, and what to change - the sentence Settings → Backups shows.
276
+ // What failed, why, and what to change - the sentence Settings → Backup & storage shows.
277
277
  export function explain(error, folder, retries, signal) {
278
- const fix = "Pick another Folder in Settings → Backups, or leave it empty to use the default";
278
+ const fix = "Pick another Folder in Settings → Backup & storage, or leave it empty to use the default";
279
279
  if (signal?.aborted === true && error === signal.reason)
280
280
  return "The backup was not saved: Slopify stopped while writing it, and the unfinished file was removed. It runs again after Slopify starts.";
281
281
  if (error instanceof NoSpaceError)
282
- return `The backup was not saved: it needs about ${bytes(error.needed)}, but the disk holding ${folder} has ${bytes(error.free)} free. Free up space there, lower "Keep last" in Settings → Backups, or pick another Folder.`;
282
+ return `The backup was not saved: it needs about ${bytes(error.needed)}, but the disk holding ${folder} has ${bytes(error.free)} free. Free up space there, lower "Keep last" in Settings → Backup & storage, or pick another Folder.`;
283
283
  switch (errnoCode(error)) {
284
284
  case "ENOSPC":
285
285
  case "EDQUOT":
286
- return `The backup was not saved: the disk holding ${folder} ran out of space. Free up space there, lower "Keep last" in Settings → Backups, or pick another Folder.`;
286
+ return `The backup was not saved: the disk holding ${folder} ran out of space. Free up space there, lower "Keep last" in Settings → Backup & storage, or pick another Folder.`;
287
287
  case "EACCES":
288
288
  case "EPERM":
289
289
  case "EROFS":
@@ -61,7 +61,7 @@ export function generateCastImage(deps, memberId, input) {
61
61
  }, (error) => {
62
62
  const reason = errorText(error);
63
63
  deps.log?.write("warn", "channels.cast-image.failed", { detail: reason.slice(0, 500) });
64
- fail(deps, id, `The picture couldn't be made: ${reason.slice(0, 500)} Check the image provider in Settings → Providers, then press Generate again.`);
64
+ fail(deps, id, `The picture couldn't be made: ${reason.slice(0, 500)} Check the image service in Settings → Keys & services, then press Make the picture again.`);
65
65
  });
66
66
  return { ok: true, value: imageRow(deps, id) };
67
67
  }
@@ -10,7 +10,7 @@ import { castKinds } from "./model.js";
10
10
  const id = z.uuid();
11
11
  const colour = z
12
12
  .string()
13
- .regex(/^#[0-9a-fA-F]{6}$/, "Use a colour like #FFD700.")
13
+ .regex(/^#[0-9a-fA-F]{6}$/, "Use a color like #FFD700.")
14
14
  .transform((value) => value.toUpperCase());
15
15
  const optionalText = (max) => z.string().trim().max(max).optional();
16
16
  const fontId = z
@@ -17,7 +17,7 @@ const importSchema = z
17
17
  // The preview's "Keep only titles containing…" text, remembered for the next import.
18
18
  filter: z
19
19
  .string()
20
- .max(importFilterMax, `The filter is too long to remember. Shorten "Keep only titles containing…" on the channel's Existing videos tab to at most ${importFilterMax} characters.`)
20
+ .max(importFilterMax, `The filter is too long to remember. Shorten "Keep only titles containing…" on the channel's Titles to avoid tab to at most ${importFilterMax} characters.`)
21
21
  .optional(),
22
22
  })
23
23
  .strict();
@@ -24,8 +24,8 @@ export function validateLocalProviderChanges(changes, current) {
24
24
  "Install this command-line tool on your computer and sign in to it, then try again.")
25
25
  : provider.readiness.kind === "local"
26
26
  ? (provider.readiness.issue ??
27
- "No speech program was found on this computer. Install espeak-ng, or choose a voice provider with a key in Settings → Providers.")
28
- : "Add this provider's API key in Settings → Providers first.",
27
+ "No speech program was found on this computer. Install the free program eSpeak NG, or choose a voice service with a key in Settings → Keys & services.")
28
+ : "Add this provider's API key in Settings → Keys & services first.",
29
29
  });
30
30
  }
31
31
  }
@@ -14,7 +14,7 @@ export function dailySampleBudget(db, log) {
14
14
  export function budgetSentence(total, budget) {
15
15
  return `Waiting: this sample would take today's sample spending to $${total.toFixed(2)}, over the $${budget.toFixed(2)} a day samples may spend. Press Make sample to make it anyway; the limit starts again at midnight.`;
16
16
  }
17
- // Settings → Template studio shows it beside the budget: what design samples spent since local
17
+ // Settings → Production defaults shows it beside the budget: what design samples spent since local
18
18
  // midnight and what is left of today's budget.
19
19
  export function designBudgetRoom(db, now, timeZone) {
20
20
  const budget = dailySampleBudget(db);
@@ -11,7 +11,7 @@ import { settleStep, stepById } from "./steps-repo.js";
11
11
  // The background half of the runner: each "making" step's provider call, at most two at once,
12
12
  // settled into its step (and made the design's current version when it lands).
13
13
  export const maxConcurrentDesignCalls = 2;
14
- export const noProviderMessage = "No image provider is set up. Add one in Settings → Providers, then press the button again.";
14
+ export const noProviderMessage = "No image service is set up. Add one in Settings → Keys & services, then press the button again.";
15
15
  export function createDesignCalls(deps) {
16
16
  let running = 0;
17
17
  const waiting = [];
@@ -119,6 +119,6 @@ function failedSentence(error, reason) {
119
119
  if (kind === "unsupported")
120
120
  return `The picture couldn't be made: ${reason}${reason.includes("Made with") ? "" : " Choose another image model under Made with … · change."}`;
121
121
  if (kind === "auth" || kind === "missing_key")
122
- return `The picture couldn't be made: ${reason}${/Settings/.test(reason) ? "" : " Check the provider's key in Settings → Providers, then press Try again."}`;
122
+ return `The picture couldn't be made: ${reason}${/Settings/.test(reason) ? "" : " Check the provider's key in Settings → Keys & services, then press Try again."}`;
123
123
  return `The picture couldn't be made: ${reason} Press Try again, or choose another image model under Made with … · change.`;
124
124
  }
@@ -22,16 +22,16 @@ export function designCheckContext(deps) {
22
22
  statuses = await deps.providers();
23
23
  }
24
24
  catch (error) {
25
- return `Slopify couldn't check the image provider: ${errorText(error)} Open Settings → Providers, then press Mark ready again.`;
25
+ return `Slopify couldn't check the image service: ${errorText(error)} Open Settings → Keys & services, then press Approve for schedules again.`;
26
26
  }
27
27
  const status = statuses.find((one) => one.id === provider);
28
28
  if (status === undefined)
29
- return `The image provider “${provider}” isn't one this Slopify knows. Choose another image model under Picture.`;
29
+ return `The image service “${provider}” isn't one this Slopify knows. Choose another image model under Picture.`;
30
30
  const readiness = status.readiness;
31
31
  if (readiness.kind === "keyed" && !readiness.hasKey)
32
- return `${status.displayName} has no API key, so the picture can't be made. Add the key in Settings → Providers, then press Mark ready again.`;
32
+ return `${status.displayName} has no API key, so the picture can't be made. Add the key in Settings → Keys & services, then press Approve for schedules again.`;
33
33
  if (readiness.kind === "cli" && (!readiness.installed || readiness.issue !== undefined))
34
- return `${status.displayName} can't be used yet: ${readiness.issue ?? "it isn't installed on this computer."} Fix it in Settings → Providers, then press Mark ready again.`;
34
+ return `${status.displayName} can't be used yet: ${readiness.issue ?? "it isn't installed on this computer."} Fix it in Settings → Keys & services, then press Approve for schedules again.`;
35
35
  return null;
36
36
  },
37
37
  };
@@ -26,7 +26,7 @@ export function designFields(doc) {
26
26
  const slotSentences = {
27
27
  unclosed: "has a {{ without its }}",
28
28
  empty: "has an empty {{}}",
29
- nested: "has a { or } inside a {{field}}",
29
+ nested: "has a { or } inside a {{keyword}}",
30
30
  };
31
31
  export function fieldProblems(doc) {
32
32
  const problems = [];
@@ -36,7 +36,7 @@ export function fieldProblems(doc) {
36
36
  kind: "slot",
37
37
  where: body.where,
38
38
  error: error.kind,
39
- sentence: `The ${body.where.replace(/^the /, "")} ${slotSentences[error.kind]}. Fix it so every field reads {{Name}}.`,
39
+ sentence: `The ${body.where.replace(/^the /, "")} ${slotSentences[error.kind]}. Fix it so every keyword reads {{Name}}.`,
40
40
  });
41
41
  const names = designFields(doc);
42
42
  for (const name of names)
@@ -51,7 +51,7 @@ export function fieldProblems(doc) {
51
51
  problems.push({
52
52
  kind: "date-field",
53
53
  name: dateField,
54
- sentence: `The schedule reads each row's date from {{${dateField}}}, but no text uses it. Put {{${dateField}}} in a text block, or choose another date field under Schedule.`,
54
+ sentence: `The schedule reads each row's date from {{${dateField}}}, but no text uses it. Put {{${dateField}}} in a text block, or choose another date keyword under Schedule.`,
55
55
  });
56
56
  return problems;
57
57
  }
@@ -64,7 +64,7 @@ export function missingSentence(missing) {
64
64
  const named = list.length === 1
65
65
  ? `${list[0] ?? ""} has`
66
66
  : `${list.slice(0, -1).join(", ")} and ${list.at(-1) ?? ""} have`;
67
- return `${named} no value. Fill ${list.length === 1 ? "it" : "them"} in under Fields, then press Make.`;
67
+ return `${named} no value. Fill ${list.length === 1 ? "it" : "them"} in under Keywords, then press Make.`;
68
68
  }
69
69
  // Every text with its fields filled. Values are inserted verbatim, never read as fields.
70
70
  export function fillDocument(doc, values) {
@@ -33,8 +33,10 @@ export async function contextChecks(doc, templateId, ctx) {
33
33
  checks.push({
34
34
  id: "provider",
35
35
  ok: problem === null,
36
- sentence: problem ?? "The image provider is ready.",
37
- fix: problem === null ? null : { label: "Open Providers", href: "/settings?section=providers" },
36
+ sentence: problem ?? "The image service is ready.",
37
+ fix: problem === null
38
+ ? null
39
+ : { label: "Open Keys & services", href: "/settings?section=providers" },
38
40
  });
39
41
  }
40
42
  else {
@@ -56,7 +58,7 @@ export async function contextChecks(doc, templateId, ctx) {
56
58
  ok: used,
57
59
  sentence: used
58
60
  ? `Each row's date is read from {{${schedule.dateField}}}.`
59
- : `The schedule reads each date from {{${schedule.dateField}}}, but the template doesn't use that field. Choose a field it uses under Schedule.`,
61
+ : `The schedule reads each date from {{${schedule.dateField}}}, but the template doesn't use that keyword. Choose a keyword it uses under Schedule.`,
60
62
  fix: used ? null : at("schedule"),
61
63
  });
62
64
  }
@@ -107,12 +109,12 @@ export async function contextChecks(doc, templateId, ctx) {
107
109
  });
108
110
  return checks;
109
111
  }
110
- // Mark ready asks for the version's own sample, made, as a project template's studio does: the
112
+ // Approve for schedules asks for the version's own sample, made, as Edit template does: the
111
113
  // person has seen what a schedule will make before it makes it.
112
114
  export function sampleCheck(db, templateId, version) {
113
115
  const row = sampleRow(db, templateId);
114
116
  const made = row?.state === "done" && row.version === version;
115
- const how = "Make its sample, look it over beside the settings, then press Mark ready.";
117
+ const how = "Make its sample, look it over beside the settings, then press Approve for schedules.";
116
118
  return {
117
119
  id: "sample",
118
120
  ok: made,
@@ -9,7 +9,7 @@ export function prepare(db, designId, format) {
9
9
  return {
10
10
  ok: false,
11
11
  reason: "not-found",
12
- message: "This design no longer exists. Open Library → Designs to choose another.",
12
+ message: "This design no longer exists. Go back to Designs to choose another.",
13
13
  };
14
14
  const parsed = designDocumentSchema.safeParse(design.document);
15
15
  if (!parsed.success)
@@ -13,7 +13,7 @@ const sha256 = z.string().regex(/^[a-f0-9]{64}$/);
13
13
  export const designValuesSchema = z
14
14
  .record(z.string().min(1).max(100), z.string().max(2000))
15
15
  .refine((values) => Object.keys(values).length <= 100, {
16
- message: "A design has at most 100 fields.",
16
+ message: "A design has at most 100 keywords.",
17
17
  });
18
18
  export const createDesignSchema = z
19
19
  .object({
@@ -14,7 +14,7 @@ const noProvider = {
14
14
  const notFound = {
15
15
  ok: false,
16
16
  reason: "not-found",
17
- message: "This design no longer exists. Open Library → Designs to choose another.",
17
+ message: "This design no longer exists. Go back to Designs to choose another.",
18
18
  };
19
19
  export function createDesignRunner(deps) {
20
20
  const calls = createDesignCalls(deps);
@@ -1,4 +1,5 @@
1
1
  import { transact } from "../../kernel/db/tx.js";
2
+ import { plansSwitch } from "../template-studio/budget.js";
2
3
  import { budgetSentence } from "./budget.js";
3
4
  import { missingValues } from "./fields.js";
4
5
  import { costChanged, costChangedMessage } from "./quote.js";
@@ -56,7 +57,7 @@ export function createDesignSamples(deps) {
56
57
  const spent = sampleSpentToday(db, now(), deps.timeZone()) + sampleReserved(db, templateId);
57
58
  const budget = deps.budget();
58
59
  return quote.rows.some((one) => one.onPlan === true) && !deps.plans()
59
- ? "Waiting: this sample's picture runs on your CLI plan, and samples may not use CLI plans. Turn on Samples may use my CLI plans in Settings → Template studio, or press Make sample to make it anyway."
60
+ ? `Waiting: this sample's picture runs on your Claude Code or Codex plan, and samples may not use it. Turn on ${plansSwitch} in Settings → Production defaults, or press Make sample to make it anyway.`
60
61
  : quote.unknown > 0
61
62
  ? "Waiting: the price of this sample's picture isn't known, so Slopify didn't make it. Press Make sample to make it anyway."
62
63
  : quote.high > holdSlack(acknowledgedHigh)
@@ -16,7 +16,7 @@ export const reusedMessage = "This request id was already used for a different c
16
16
  const notFound = {
17
17
  ok: false,
18
18
  reason: "not-found",
19
- message: "This design no longer exists. Open Library → Designs to choose another.",
19
+ message: "This design no longer exists. Go back to Designs to choose another.",
20
20
  };
21
21
  export function mutationHash(input) {
22
22
  return createHash("sha256").update(JSON.stringify(input)).digest("hex");
@@ -21,14 +21,14 @@ export function documentChecks(doc, templateId) {
21
21
  {
22
22
  id: "fields",
23
23
  ok: problems.length === 0,
24
- sentence: problems[0]?.sentence ?? "Every field is written as {{Name}}.",
24
+ sentence: problems[0]?.sentence ?? "Every keyword is written as {{Name}}.",
25
25
  fix: problems.length === 0 ? null : at("text"),
26
26
  },
27
27
  {
28
28
  id: "samples",
29
29
  ok: missing.length === 0,
30
30
  sentence: missing.length === 0
31
- ? "Every field has a sample value."
31
+ ? "Every keyword has a sample value."
32
32
  : `${missing.map((name) => `{{${name}}}`).join(", ")} ${missing.length === 1 ? "has" : "have"} no sample value. Fill ${missing.length === 1 ? "it" : "them"} in under Text → Sample values.`,
33
33
  fix: missing.length === 0 ? null : at("text"),
34
34
  },
@@ -27,7 +27,7 @@ export const makeFromTemplateSchema = z
27
27
  const notFound = {
28
28
  ok: false,
29
29
  reason: "not-found",
30
- message: "This design template no longer exists. Open Library → Designs → Templates to choose another.",
30
+ message: "This design template no longer exists. Open Templates → Poster templates to choose another.",
31
31
  };
32
32
  // What one piece made from this document costs: its artwork, or nothing when the template
33
33
  // keeps its own picture.
@@ -53,7 +53,7 @@ export function makeFromTemplate(deps, templateId, raw, origin = {}) {
53
53
  return {
54
54
  ok: false,
55
55
  reason: "invalid",
56
- message: `The piece can't be made: ${parsed.error.issues[0]?.message ?? "a field is missing"}.`,
56
+ message: `The poster can't be made: ${parsed.error.issues[0]?.message ?? "a keyword is missing"}.`,
57
57
  };
58
58
  const input = parsed.data;
59
59
  const template = getTemplate(deps.service.db, templateId);
@@ -78,7 +78,7 @@ export function makeFromTemplate(deps, templateId, raw, origin = {}) {
78
78
  return {
79
79
  ok: false,
80
80
  reason: "cost-changed",
81
- message: costChangedMessage(quote, "this piece"),
81
+ message: costChangedMessage(quote, "this poster"),
82
82
  current: quote,
83
83
  };
84
84
  const own = doc.picture.source === "picture" ? doc.picture.sha256 : undefined;
@@ -109,7 +109,7 @@ export function listTemplates(db) {
109
109
  const notFound = {
110
110
  ok: false,
111
111
  reason: "not-found",
112
- message: "This design template no longer exists. Open Library → Designs → Templates to choose another.",
112
+ message: "This design template no longer exists. Open Templates → Poster templates to choose another.",
113
113
  };
114
114
  function invalid(error) {
115
115
  return {
@@ -187,7 +187,7 @@ export function saveTemplate(deps, templateId, raw) {
187
187
  export async function markTemplateReady(deps, templateId, raw, more = async () => []) {
188
188
  const parsed = markReadySchema.safeParse(raw);
189
189
  if (!parsed.success)
190
- return { ok: false, reason: "invalid", message: "Say which version to mark ready." };
190
+ return { ok: false, reason: "invalid", message: "Say which version to approve for schedules." };
191
191
  if (getTemplate(deps.db, templateId) === undefined)
192
192
  return notFound;
193
193
  const revision = templateRevision(deps.db, templateId, parsed.data.version);
@@ -210,7 +210,7 @@ export async function markTemplateReady(deps, templateId, raw, more = async () =
210
210
  return {
211
211
  ok: false,
212
212
  reason: "not-ready",
213
- message: `Version ${String(parsed.data.version)} can't be marked ready yet: ${failed.map((check) => check.sentence).join(" ")}`,
213
+ message: `Version ${String(parsed.data.version)} can't be approved for schedules yet: ${failed.map((check) => check.sentence).join(" ")}`,
214
214
  current: failed,
215
215
  };
216
216
  const template = getTemplate(deps.db, templateId);
@@ -33,7 +33,7 @@ export function readDocumentAssets() {
33
33
  };
34
34
  }
35
35
  catch (error) {
36
- throw new Error(`Slopify couldn't read the fonts and page texture it makes documents with (${errorText(error)}). Its installation is incomplete: reinstall or update Slopify, then use Try again on Document.`, { cause: error });
36
+ throw new Error(`Slopify couldn't read the fonts and page texture it makes documents with (${errorText(error)}). Its installation is incomplete: reinstall or update Slopify, then use Try again on PDF.`, { cause: error });
37
37
  }
38
38
  }
39
39
  export function registerFonts(doc, fonts) {