@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
@@ -1,5 +1,5 @@
1
1
  import { admittedUnfinished } from "../admission/admitted-work.js";
2
- import { oneProject } from "../admission/repo.js";
2
+ import { liveProject, oneProject } from "../admission/repo.js";
3
3
  export function markUploaded(db, projectId, uploaded, at) {
4
4
  if (db.prepare("SELECT 1 FROM projects WHERE id=?").get(projectId) === undefined)
5
5
  return { ok: false, reason: "not-found" };
@@ -35,6 +35,42 @@ export function uploadedAndSettled(db, projectId) {
35
35
  .all(...one.params)
36
36
  .map((row) => String(row.project_id)));
37
37
  }
38
+ // A finished project an edit left with outdated files reads done too, not "Stopped": every step
39
+ // still to run made its files before the edit (a selected output of the current version, now
40
+ // outdated), and nothing runs it until the person chooses to make those files again, which shows
41
+ // the cost first. Renaming a project with a PDF is one: its PDF prints the title. A step that
42
+ // never made a file, a failed or canceled step, a step a run admitted (`admittedUnfinished`, as
43
+ // a restart holds it), a pause or a queued run keep it unsettled, so a run a restart stopped
44
+ // still reads Stopped. So does a version saved to make a section again (Make … again, a review's
45
+ // redo) whose run was refused, say for a missing key: the person asked for that work, and Continue
46
+ // the run starts it. One project's alone when `projectId` is given.
47
+ export function editedAndSettled(db, projectId) {
48
+ const one = oneProject("h", projectId);
49
+ const unfinished = "s.project_id=h.project_id AND s.state NOT IN ('done','provided','skipped')";
50
+ return new Set(db
51
+ .prepare(`SELECT h.project_id FROM project_heads h
52
+ WHERE ${liveProject("h", "project_id")}
53
+ AND EXISTS(SELECT 1 FROM stages s WHERE ${unfinished})
54
+ AND NOT EXISTS(SELECT 1 FROM stages s WHERE ${unfinished}
55
+ AND (s.state!='pending' OR NOT EXISTS(SELECT 1 FROM revision_outputs o
56
+ WHERE o.project_id=h.project_id AND o.revision_id=h.revision_id AND o.selected=1
57
+ AND o.state='outdated' AND json_extract(o.descriptor,'$.stageKind')=s.kind)))
58
+ AND NOT EXISTS(SELECT 1 FROM revision_work w WHERE w.project_id=h.project_id
59
+ AND w.state IN ('pending','running')
60
+ AND (w.dispatch_state!='held' OR ${admittedUnfinished("w")}))
61
+ AND NOT EXISTS(SELECT 1 FROM project_recovery_requests r WHERE r.project_id=h.project_id
62
+ AND r.intent_revision_id=h.revision_id)
63
+ AND NOT EXISTS(SELECT 1 FROM project_controls c WHERE c.project_id=h.project_id AND c.paused=1)
64
+ AND NOT EXISTS(SELECT 1 FROM project_queue q WHERE q.project_id=h.project_id AND q.state='queued')${one.sql}`)
65
+ .all(...one.params)
66
+ .map((row) => String(row.project_id)));
67
+ }
68
+ // Every project that reads done though a step is left: uploaded with nothing left to run
69
+ // (`uploadedAndSettled`), or finished and left only with files an edit made outdated
70
+ // (`editedAndSettled`).
71
+ export function settledProjects(db, projectId) {
72
+ return new Set([...uploadedAndSettled(db, projectId), ...editedAndSettled(db, projectId)]);
73
+ }
38
74
  // "Keep as is": a waiting run the person leaves as it is, until the project's next edit. Set on
39
75
  // the current head revision; the listing counts it only while that revision is still the head.
40
76
  export function setAside(db, projectId, aside, at) {
@@ -1,5 +1,6 @@
1
- // An uploaded, settled project (`uploadedAndSettled`) reads done where its run would read paused
2
- // or pending: the Projects list, a project's page and the calendar.
1
+ // A settled project (`settledProjects`: uploaded with nothing left to run, or finished and left
2
+ // only with files an edit made outdated) reads done where its run would read paused or pending:
3
+ // the Projects list, a project's page and the calendar.
3
4
  export function settledStatus(status, settled) {
4
5
  return settled && (status === "paused" || status === "pending") ? "done" : status;
5
6
  }
@@ -116,9 +116,9 @@ const editListSchema = z
116
116
  export function readEditList(value) {
117
117
  const version = typeof value === "object" && value !== null && "version" in value ? value.version : undefined;
118
118
  if (version !== editListVersion)
119
- throw new Error(`This video's edit list is version ${String(version)}, and this Slopify reads version ${editListVersion}. Update Slopify, or use More → Render the video again in the Video section to plan it again.`);
119
+ throw new Error(`This video's edit list is version ${String(version)}, and this Slopify reads version ${editListVersion}. Update Slopify, or press Make the video again in the project's Video section to plan it again.`);
120
120
  const parsed = editListSchema.safeParse(value);
121
121
  if (!parsed.success)
122
- throw new Error(`This video's edit list is damaged (${parsed.error.issues[0]?.path.join(".") || "the list"}: ${parsed.error.issues[0]?.message ?? "not readable"}). Use More → Render the video again in the Video section to plan it again.`);
122
+ throw new Error(`This video's edit list is damaged (${parsed.error.issues[0]?.path.join(".") || "the list"}: ${parsed.error.issues[0]?.message ?? "not readable"}). Press Make the video again in the project's Video section to plan it again.`);
123
123
  return parsed.data;
124
124
  }
@@ -177,7 +177,7 @@ export function videoEditRows(edit) {
177
177
  ],
178
178
  ["Vignette", lookLevelLabels[edit.vignette]],
179
179
  ["Film grain", lookLevelLabels[edit.grain]],
180
- ["Colour grade", colorGradeLabels[edit.grade]],
180
+ ["Color grade", colorGradeLabels[edit.grade]],
181
181
  ["Atmosphere", atmosphereLabels[edit.atmosphere]],
182
182
  ["Chapter cards", edit.chapterCards ? "On" : "Off"],
183
183
  [
@@ -55,7 +55,15 @@ export function codecArgs(encode, role, look) {
55
55
  : []),
56
56
  ];
57
57
  }
58
- export const noNvenc = "No NVIDIA encoder found on this computer.";
58
+ // Said to a person who may have no NVIDIA card at all: stock ffmpeg builds list the NVIDIA
59
+ // encoder everywhere, so a failed test usually means there is no card, not a broken one. What
60
+ // the test printed goes under Details (`detail`), never into these sentences.
61
+ export const noNvenc = "No NVIDIA graphics card was found on this computer.";
62
+ export const failedNvenc = "No working NVIDIA graphics card was found on this computer.";
63
+ // The reason with the test's output, for the log and a run's notice.
64
+ export function nvencWhy(status) {
65
+ return status.detail === undefined ? status.reason : `${status.reason} (${status.detail})`;
66
+ }
59
67
  // The renders' own settings on a tiny picture: a driver or card that refuses any of them fails
60
68
  // here rather than in a render. NVENC refuses anything under about 145 pixels across.
61
69
  const testArgs = [
@@ -84,7 +92,8 @@ export function createNvencProbe(deps) {
84
92
  return () => {
85
93
  answer ??= probe(deps).catch((error) => ({
86
94
  available: false,
87
- reason: `${noNvenc} (${errorText(error)})`,
95
+ reason: noNvenc,
96
+ detail: errorText(error),
88
97
  }));
89
98
  return answer;
90
99
  };
@@ -112,7 +121,7 @@ async function probe(deps) {
112
121
  }
113
122
  if (failed !== undefined) {
114
123
  deps.log.write("info", "video.encoder", { detail: failed });
115
- return { available: false, reason: `${noNvenc} (${failed})` };
124
+ return { available: false, reason: failedNvenc, detail: failed };
116
125
  }
117
126
  return { available: false, reason: noNvenc };
118
127
  }
@@ -133,11 +142,11 @@ export async function renderMachine(deps) {
133
142
  };
134
143
  deps.log.write("warn", "video.encoder", {
135
144
  projectId: deps.projectId,
136
- detail: `NVIDIA encoder chosen but unavailable; encoding on the processor. ${status.reason}`,
145
+ detail: `NVIDIA encoder chosen but unavailable; encoding on the processor. ${nvencWhy(status)}`,
137
146
  });
138
147
  return {
139
148
  machine: { encode: cpu, priority: settings.renderPriority },
140
- notice: nvencFallbackNotice(status.reason),
149
+ notice: nvencFallbackNotice(nvencWhy(status)),
141
150
  };
142
151
  }
143
152
  export function nvencFallbackNotice(reason) {
@@ -46,7 +46,7 @@ export function slideshowClips(edit) {
46
46
  named.set(key, name);
47
47
  const shot = edit.shots[segment.kind === "shot" ? segment.shot : segment.to];
48
48
  if (shot === undefined)
49
- throw new Error(internalError("the video's edit list lost a shot", "Use More → Render the video again in the Video section"));
49
+ throw new Error(internalError("the video's edit list lost a shot", "Press Make the video again in the project's Video section"));
50
50
  clips.push({ name, segment, shot, frames: segment.count });
51
51
  }
52
52
  return name;
@@ -93,7 +93,7 @@ encode = defaultEncode) {
93
93
  const chains = [];
94
94
  const picture = (shot, from, label) => {
95
95
  if (shot === undefined)
96
- throw new Error(internalError("the video's edit list lost a shot", "Use More → Render the video again in the Video section"));
96
+ throw new Error(internalError("the video's edit list lost a shot", "Press Make the video again in the project's Video section"));
97
97
  const at = inputs.filter((one) => one === "-i").length;
98
98
  const built = pictureChain(edit, shot, from, segment.count, at, label, gradeFilter(edit.look));
99
99
  inputs.push(...built.inputs);
@@ -1,3 +1,4 @@
1
+ import { editSettings } from "../../kernel/ports/settings-places.js";
1
2
  import { chapterCuts, narrationShotFrames } from "./cuts.js";
2
3
  import { editListVersion, } from "./edit-list.js";
3
4
  import { chapterCardSeconds, endScreenSeconds } from "./edit-settings.js";
@@ -41,7 +42,7 @@ const figureGapSeconds = 1;
41
42
  export function planRender(input) {
42
43
  if (input.images.length === 0) {
43
44
  // Admission permits Video only with an image source, so an empty set is a bug upstream.
44
- throw new Error("The video needs at least one image, but there are none. Check the Images section in Edit project, then Try again.");
45
+ throw new Error(`The video needs at least one image, but there are none. Check ${editSettings.images}, then Try again.`);
45
46
  }
46
47
  const frame = frames[input.format];
47
48
  const bed = input.body === undefined ? undefined : input.bed;
@@ -62,7 +62,7 @@ async function renderWith(run, encode) {
62
62
  const font = edit.cardFont;
63
63
  if (cards.length > 0) {
64
64
  if (font === undefined)
65
- throw new Error(internalError("the chapter cards have no font", "Use More → Render the video again in the Video section"));
65
+ throw new Error(internalError("the chapter cards have no font", "Press Make the video again in the project's Video section"));
66
66
  mkdirSync(join(workspace, "fonts"), { mode: 0o700 });
67
67
  copyFileSync(font.path, join(workspace, "fonts", `card${extname(font.path)}`));
68
68
  }
@@ -1,3 +1,4 @@
1
+ import { editSettings } from "../../kernel/ports/settings-places.js";
1
2
  import { parseScript } from "./script.js";
2
3
  // Audiobooks from an existing text: the text model hands each passage to a speaker - the
3
4
  // narrator's prose to the narrator, each quoted line to the character who says it - and
@@ -35,12 +36,12 @@ export function parseAttribution(answer, source, speakers) {
35
36
  if (expected.length > 0 && kept / expected.length < 0.9)
36
37
  return {
37
38
  ok: false,
38
- reason: `The speaker split left out ${String(expected.length - kept)} of the text's ${String(expected.length)} words. The text model must hand every word to a speaker; Try again, or choose another model in Edit project → Providers.`,
39
+ reason: `The speaker split left out ${String(expected.length - kept)} of the text's ${String(expected.length)} words. The AI model must hand every word to a speaker; Try again, or choose another model in ${editSettings.writer}.`,
39
40
  };
40
41
  if (got.length - kept > Math.max(3, expected.length * 0.1))
41
42
  return {
42
43
  ok: false,
43
- reason: `The speaker split added ${String(got.length - kept)} words that are not in the text. The text model must not write anything of its own; Try again, or choose another model in Edit project → Providers.`,
44
+ reason: `The speaker split added ${String(got.length - kept)} words that are not in the text. The AI model must not write anything of its own; Try again, or choose another model in ${editSettings.writer}.`,
44
45
  };
45
46
  return parsed;
46
47
  }
@@ -1,4 +1,5 @@
1
1
  import { z } from "zod";
2
+ import { createRows, editSettings } from "../../kernel/ports/settings-places.js";
2
3
  // Multiple voices: a run whose narration is a script of speaker turns rather than one voice
3
4
  // reading an article. Absent on a run is the Narration format, which is every project saved
4
5
  // before this existed, and nothing about such a run reads anything in this slice.
@@ -136,23 +137,23 @@ export function voicesProblems(settings) {
136
137
  if (speakers.length === 0)
137
138
  problems.push({
138
139
  field: "voices.speakers",
139
- message: "Add at least one speaker under Speakers (Play → Narration, or Edit project → Providers).",
140
+ message: `Add at least one speaker under Speakers (${createRows.narration}, or ${editSettings.narration}).`,
140
141
  });
141
142
  if (settings.source === "attribute" && settings.format !== "audiobook")
142
143
  problems.push({
143
144
  field: "voices.source",
144
- message: "Only audiobooks can hand an existing text's dialogue to speakers. Choose Write a script under Speakers (Play → Narration, or Edit project → Providers).",
145
+ message: `Only audiobooks can hand an existing text's dialogue to speakers. Choose Write a script under Speakers (${createRows.narration}, or ${editSettings.narration}).`,
145
146
  });
146
147
  if ((settings.format === "podcast" || settings.format === "interview") && speakers.length < 2)
147
148
  problems.push({
148
149
  field: "voices.speakers",
149
- message: `A ${voiceFormatLabels[settings.format].toLowerCase()} needs at least two speakers. Add one under Speakers (Play → Narration, or Edit project → Providers).`,
150
+ message: `A ${voiceFormatLabels[settings.format].toLowerCase()} needs at least two speakers. Add one under Speakers (${createRows.narration}, or ${editSettings.narration}).`,
150
151
  });
151
152
  if ((settings.format === "audiobook" || settings.format === "drama") &&
152
153
  !speakers.some((speaker) => speaker.role === "narrator"))
153
154
  problems.push({
154
155
  field: "voices.speakers",
155
- message: `An ${settings.format === "drama" ? "radio drama" : "audiobook"} needs a narrator. Set one speaker's role to Narrator under Speakers (Play → Narration, or Edit project → Providers).`,
156
+ message: `An ${settings.format === "drama" ? "radio drama" : "audiobook"} needs a narrator. Set one speaker's role to Narrator under Speakers (${createRows.narration}, or ${editSettings.narration}).`,
156
157
  });
157
158
  const names = new Set();
158
159
  const ids = new Set();
@@ -181,7 +182,7 @@ export function voicesProblems(settings) {
181
182
  if (speaker.voice.provider.trim() === "" || speaker.voice.model.trim() === "")
182
183
  problems.push({
183
184
  field: `${field}.voice`,
184
- message: `Choose a voice provider and model for ${name || "this speaker"}.`,
185
+ message: `Choose a voice service and model for ${name || "this speaker"}.`,
185
186
  });
186
187
  else if (speaker.voice.voice.trim() === "")
187
188
  problems.push({
@@ -198,20 +199,20 @@ export function voicesProblems(settings) {
198
199
  if (settings.book.title.trim() === "")
199
200
  problems.push({
200
201
  field: "voices.book.title",
201
- message: "Enter the book's title under Speakers (Play → Narration), or remove the book to make a project on its own.",
202
+ message: `Enter the book's title under Speakers (${createRows.narration}, or ${editSettings.narration}), or remove the book to make a project on its own.`,
202
203
  });
203
204
  if (!Number.isInteger(settings.book.chapter) ||
204
205
  settings.book.chapter < 1 ||
205
206
  settings.book.chapter > bookChapterMax)
206
207
  problems.push({
207
208
  field: "voices.book.chapter",
208
- message: `Enter a chapter number from 1 to ${String(bookChapterMax)} under Speakers (Play → Narration).`,
209
+ message: `Enter a chapter number from 1 to ${String(bookChapterMax)} under Speakers (${createRows.narration}, or ${editSettings.narration}).`,
209
210
  });
210
211
  }
211
212
  if (!turnGapSteps.includes(settings.turnGapSeconds))
212
213
  problems.push({
213
214
  field: "voices.turnGapSeconds",
214
- message: "Pick a gap between turns from the list under Speakers (Play → Narration, or Edit project → Providers).",
215
+ message: `Pick a gap between turns from the list under Speakers (${createRows.narration}, or ${editSettings.narration}).`,
215
216
  });
216
217
  return problems;
217
218
  }
@@ -1,3 +1,4 @@
1
+ import { createRows, editSettings } from "../../kernel/ports/settings-places.js";
1
2
  import { voiceFormatLabels } from "./model.js";
2
3
  const heading = /^#{1,6}\s+(.+?)\s*#*\s*$/;
3
4
  const turnLine = /^\*{0,2}([^:*\n]{1,60}?)\*{0,2}\s*:\s*\*{0,2}\s*(.*)$/;
@@ -42,7 +43,7 @@ export function parseScript(markdown, speakers, keepMarkdown = false) {
42
43
  ok: false,
43
44
  reason: turn === null
44
45
  ? `Line ${String(number)} of the script is not a turn. Start every turn with a speaker's name and a colon (${names}), one turn per paragraph.`
45
- : `Line ${String(number)} of the script is spoken by "${(turn[1] ?? "").trim()}", who is not one of the speakers (${names}). Use only these names, or add the speaker under Speakers (Play → Narration, or Edit project → Providers).`,
46
+ : `Line ${String(number)} of the script is spoken by "${(turn[1] ?? "").trim()}", who is not one of the speakers (${names}). Use only these names, or add the speaker under Speakers (${createRows.narration}, or ${editSettings.narration}).`,
46
47
  };
47
48
  }
48
49
  const spoken = [];
@@ -1,5 +1,6 @@
1
1
  import { z } from "zod";
2
2
  import { jsonBetween } from "../../kernel/json-answer.js";
3
+ import { editSettings } from "../../kernel/ports/settings-places.js";
3
4
  import { alternativeTitles, descriptionMaxCharacters, hashtagsMax, minChapterSeconds, minChapters, pinnedCommentMaxCharacters, tagMaxCharacters, tagsMaxCharacters, titleMaxCharacters, } from "./model.js";
4
5
  import { parseTimestamp, youtubeTimestamp } from "./timestamps.js";
5
6
  import { keepsShape, titleShapeRule } from "./titles.js";
@@ -53,7 +54,7 @@ const answerSchema = z.object({
53
54
  pinnedComment: z.string(),
54
55
  titles: z.array(z.string()),
55
56
  });
56
- const fix = "Try again, or choose another model in Edit project → Providers.";
57
+ const fix = `Try again, or choose another model in ${editSettings.writer}.`;
57
58
  // Reads and checks the model's answer. The reason is the sentence the stage shows; the
58
59
  // provider wrapper's `check` asks the model again while attempts remain.
59
60
  export function checkDescriptionAnswer(text, durationSeconds,
@@ -59,7 +59,7 @@ export function readChannelLinksFor(db, channelId) {
59
59
  // the generated text, the chapters fitted to YouTube's rules (`chapters.ts`; the last one's
60
60
  // length only checked when the video's length is given), placeholders filled from the
61
61
  // project's and its channel's links (one with no link stays as typed). For anything that hands
62
- // the description on, such as Prepare upload and the downloads. `chapterNotice` says what the
62
+ // the description on, such as Copy for YouTube Studio and the downloads. `chapterNotice` says what the
63
63
  // fitting changed.
64
64
  export function effectiveDescription(db, projectId, generated) {
65
65
  const edits = readDescriptionEdits(db, projectId);
@@ -2,7 +2,7 @@
2
2
 
3
3
  Slopify can write the text with the Claude Code, Codex or Antigravity command-line tools, and draw the images with Codex, using the sign-in you already have. You need no API key for those steps, and their calls count toward your plan's limits instead of costing per call.
4
4
 
5
- **Where to find it:** Settings → Providers → the **Text** list (Claude Code CLI, Codex CLI, Antigravity CLI) and the **Images** list (Codex CLI).
5
+ **Where to find it:** Settings → Keys & services → the **Writing services** list (Claude Code CLI, Codex CLI, Antigravity CLI) and the **Image services** list (Codex CLI).
6
6
 
7
7
  ## What each tool does
8
8
 
@@ -15,7 +15,7 @@ Slopify can write the text with the Claude Code, Codex or Antigravity command-li
15
15
 
16
16
  Codex text and Codex images share one sign-in, one executable path and one plan allowance.
17
17
 
18
- Codex CLI must be version 0.149.1 or newer. An older Codex shows **Needs attention** and says to update it.
18
+ Codex CLI must be version 0.149.1 or newer. An older Codex shows **Needs fixing** and says to update it.
19
19
 
20
20
  ## Set up a CLI
21
21
 
@@ -24,11 +24,11 @@ Codex CLI must be version 0.149.1 or newer. An older Codex shows **Needs attenti
24
24
  - Claude Code: `claude auth login`
25
25
  - Codex: `codex login`
26
26
  - Antigravity: run `agy` and sign in with your Google account (a browser opens; over SSH it shows a URL and a code).
27
- 3. Open **Settings → Providers** (reload the page if it was already open).
28
- 4. Pick the tool in the **Text** or **Images** list. Its row should say **Ready**, and its setup shows **Installed, version …** and the **Command** Slopify runs.
29
- 5. Choose **Check all** to confirm it is signed in.
27
+ 3. Open **Settings → Keys & services** (reload the page if it was already open).
28
+ 4. Pick the tool in the **Writing services** or **Image services** list. Its row should say **Ready**, and its setup shows **Installed, version …** and the **Command** Slopify runs.
29
+ 5. Press **Check every service** to confirm it is signed in.
30
30
 
31
- On the very first launch, before any key is saved, Slopify looks for all three tools. If it finds any, it says so and picks one for text on new Play drafts, preferring Claude Code, then Codex, then Antigravity, and Codex for images when found. The CLIs can't speak, but the narration needs no key either: without a voice key, the [System voice](Providers-and-Keys#the-system-voice) reads it with your computer's own speech. See [First-Launch-and-Welcome](First-Launch-and-Welcome).
31
+ On the very first launch, before any key is saved, Slopify looks for all three tools. If it finds any, it says so and picks one for text on new Create drafts, preferring Claude Code, then Codex, then Antigravity, and Codex for images when found. The CLIs can't speak, but the narration needs no key either: without a voice key, the [System voice](Providers-and-Keys#the-system-voice) reads it with your computer's own speech. See [First-Launch-and-Welcome](First-Launch-and-Welcome).
32
32
 
33
33
  ## Antigravity CLI: what is different
34
34
 
@@ -41,21 +41,21 @@ It works differently from the Gemini CLI in a few ways:
41
41
  - **No timeout of its own.** `agy` waits as long as the model needs; Slopify's own stall checks end a run that stops making progress.
42
42
  - **Busy messages are retried.** When several calls run at once, Google can answer that its capacity for the model is used for the moment. A reset of 5 minutes or less is retried by itself within seconds; a longer reset is handled as a plan limit and the run waits for it.
43
43
  - **Its sign-in is checked with `agy models`.** Slopify runs the command: if it lists models, the CLI is **Signed in**; if it says "Please sign in", the CLI is signed out and the check gives the command to run (`agy`); anything else shows as unknown. `GEMINI_API_KEY` is not read.
44
- - **Old model ids are gone.** Models come from `agy models`. A project that still names a Gemini CLI model such as `gemini-2.5-pro` stops with "The chosen model is not available…" until you pick a new one in **Edit project → Providers**. A saved path to a program still called `gemini` is refused: clear it or set the Antigravity CLI path in Settings → Providers.
44
+ - **Old model ids are gone.** Models come from `agy models`. A project that still names a Gemini CLI model such as `gemini-2.5-pro` stops with "The chosen model is not available…" until you pick a new one under **Written by** in **Edit settings** → **Article**. A saved path to a program still called `gemini` is refused: clear it or set the Antigravity CLI path in Settings → Keys & services.
45
45
 
46
46
  ## Row states
47
47
 
48
48
  | State | Meaning |
49
49
  |---|---|
50
50
  | **Ready** | Found, new enough and not reporting a sign-in problem. |
51
- | **Needs attention** | Found, but something is wrong (for example an old Codex version). The detail says what. |
51
+ | **Needs fixing** | Found, but something is wrong (for example an old Codex version). The detail says what. |
52
52
  | **Not found** | Not on PATH, or not at the saved path. |
53
53
 
54
54
  ## Set the executable path
55
55
 
56
56
  Slopify finds each tool on PATH, the list of folders your terminal searches. Set a path when the tool is installed somewhere else, or you have more than one copy.
57
57
 
58
- 1. Pick the tool in **Settings → Providers**.
58
+ 1. Pick the tool in **Settings → Keys & services**.
59
59
  2. Choose **Change path**.
60
60
  3. In **Executable path**, type the absolute path to the program file, without quotes or arguments. Leave it blank to go back to finding it on PATH.
61
61
  4. Choose **Save path**. Slopify checks the tool answers before saving; a folder or a missing file is refused with the reason.
@@ -67,7 +67,7 @@ Slopify finds each tool on PATH, the list of folders your terminal searches. Set
67
67
 
68
68
  ## Check the sign-in
69
69
 
70
- **Check all** (Settings → Providers) asks each tool:
70
+ **Check every service** (Settings → Keys & services) asks each tool:
71
71
 
72
72
  - **Installed**: found, with its version.
73
73
  - **Signed in**: Slopify asks each CLI directly. If one is signed out, the line tells you the command to run (`claude auth login`, `codex login` or `agy`). For Antigravity CLI it runs `agy models`: a model list means signed in, "Please sign in" means signed out (run `agy`), and anything else is shown as unknown.
@@ -79,20 +79,20 @@ If a run fails because a tool's sign-in expired, the project offers **Copy sign-
79
79
 
80
80
  ## Pick a model and thinking level
81
81
 
82
- A CLI's model list comes from the tool installed on your computer, not from the model catalogue. On Play:
82
+ A CLI's model list comes from the tool installed on your computer, not from the model catalogue. On Create:
83
83
 
84
- 1. Pick the tool as the text provider (or Codex CLI for images).
84
+ 1. Pick the tool as the **Service** under **Written by** (or Codex CLI under **Images by** for images).
85
85
  2. Pick a **Text model** from the list. The refresh button beside it (**Refresh models**) asks the tool for its current list; **Custom ID** lets you type a model the list does not show.
86
- 3. If the model offers it, pick **Thinking** (text) or **Effort** (images). **Model default** leaves it to the tool. The levels come from the model: for example Low, Medium, High and Xhigh. Higher levels can write or draw more carefully, but take longer and use more of your plan.
86
+ 3. If the model offers it, pick **Thinking** (text) or **Effort** (images). **Model default** leaves it to the tool. The levels come from the model: for example Low, Medium, High and Extra high. Higher levels can write or draw more carefully, but take longer and use more of your plan.
87
87
 
88
- If a model is missing when a run reaches it, the run stops with "The chosen model is not available in the AI command-line tool on your computer." Update the tool, or pick another model in **Edit project** under Providers, then **Continue the run**. A thinking level the model does not support stops the same way.
88
+ If a model is missing when a run reaches it, the run stops with "The chosen model is not available in the AI command-line tool on your computer." Update the tool, or pick another model in **Edit settings** (under **Written by**, or **Images by** for Codex images), then **Continue the run**. A thinking level the model does not support stops the same way.
89
89
 
90
90
  ## Codex images
91
91
 
92
92
  Codex draws each image as its own job.
93
93
 
94
94
  - **Codex default** runs with the tool's own defaults: no model and no effort on the command line.
95
- - Any other model is one of Codex's own models (the same list the Codex text provider shows), run with the **Effort** you pick. With a chosen model or effort, or a reference picture to match, the agent may look at its work and redraw it.
95
+ - Any other model is one of Codex's own models (the same list Codex shows as a writing service), run with the **Effort** you pick. With a chosen model or effort, or a reference picture to match, the agent may look at its work and redraw it.
96
96
  - Each image may take several minutes; the time limit for one image is 30 minutes.
97
97
  - Up to 4 images are drawn at once.
98
98
 
@@ -106,16 +106,16 @@ The CLIs bill your subscription, which gives a share of a 5-hour and a weekly al
106
106
  - **Codex** is asked before and after each call. It reports whole percents, so a short run can show "under 1%".
107
107
  - **Antigravity** reports none.
108
108
 
109
- You see these on a project's **Run cost** tab, in the run's **Cost so far** panel, and on Home under **This week**. See [Costs-and-Run-Cost](Costs-and-Run-Cost).
109
+ You see these in a project's cost breakdown (**See the breakdown** on its cost line) and on Home under **This week**. See [Costs-and-Run-Cost](Costs-and-Run-Cost).
110
110
 
111
111
  When a tool says its allowance is used up, the run does not fail:
112
112
 
113
113
  1. Slopify stores the reset time the tool gave (per tool, not per project).
114
114
  2. Every call to that tool, from any project, waits until 2 minutes after the reset. Without a stated time, it checks again every 30 minutes.
115
- 3. The project's status reads **Waiting for limits**, with a line such as "Waiting for Codex limits (resets at 14:00)." The same words show on Home's Running now, the Projects row and the Calendar.
115
+ 3. The project's status reads **Waiting for limits**, with a line such as "Waiting for Codex limits (resets at 14:00)." The same words show on Home's Running, the Projects row and the Calendar.
116
116
  4. When the reset passes, the run continues by itself.
117
117
 
118
- Other providers are not held up: a project whose narration uses a key carries on while its Codex images wait. The wait survives a restart; a project that was waiting when Slopify stopped resumes on the next start and waits again. A project you paused or canceled meanwhile is left alone.
118
+ Other services are not held up: a project whose narration uses a key carries on while its Codex images wait. The wait survives a restart; a project that was waiting when Slopify stopped resumes on the next start and waits again. A project you paused or canceled meanwhile is left alone.
119
119
 
120
120
  Antigravity's quota is handled the same way ("Slopify waits and tries again later by itself").
121
121
 
@@ -125,14 +125,14 @@ A container cannot use the CLIs installed on your computer directly. On Linux, t
125
125
 
126
126
  1. Install and sign in to the CLIs on your computer first.
127
127
  2. Run the Docker installer (`npx @gentbajko/slopify@latest --docker`). If a CLI is found, it asks once whether to set up the helper. `--accept-host-cli` approves without asking; `--host-cli=off` skips it.
128
- 3. In Settings → Providers the tools show **Managed on host**. Their path cannot be changed there.
128
+ 3. In Settings → Keys & services the tools show **Managed on host**. Their path cannot be changed there.
129
129
 
130
130
  What to know:
131
131
 
132
132
  - The helper runs as you, as the user service `slopify-cli-bridge.service`, and talks to the container over a private socket. No network port is opened and no sign-in is copied into Docker.
133
133
  - After you install, update or remove a CLI, run the Docker installer again so Slopify sees the change.
134
134
  - Run cost and plan-limit shares work the same as without Docker. An older helper may not pass them through, and the cost page then says the share is not known.
135
- - With the helper off, Settings shows the CLIs as unavailable. API-key providers still work.
135
+ - With the helper off, Settings shows the CLIs as unavailable. Services with an API key still work.
136
136
 
137
137
  Check or turn off the helper:
138
138
 
@@ -141,17 +141,17 @@ systemctl --user status slopify-cli-bridge.service
141
141
  systemctl --user disable --now slopify-cli-bridge.service
142
142
  ```
143
143
 
144
- Turning it off leaves Docker, your API providers, your data and your host sign-ins as they are. See [Docker](Docker).
144
+ Turning it off leaves Docker, your keyed services, your data and your host sign-ins as they are. See [Docker](Docker).
145
145
 
146
146
  ## Tips
147
147
 
148
- - A CLI step on Play's estimate reads "$0 on your plan · ~$X via API": what the same tokens would cost with a key.
149
- - If your plan runs out often, split the work: keep text on a CLI and put images on a keyed provider, or the other way round.
150
- - Use **Check all** after updating a CLI; a newer tool can drop a model your template still names.
148
+ - A CLI step on Create's estimate reads "$0 on your plan · ~$X via API": what the same tokens would cost with a key.
149
+ - If your plan runs out often, split the work: keep the writing on a CLI and put images on a keyed service, or the other way round.
150
+ - Use **Check every service** after updating a CLI; a newer tool can drop a model your template still names.
151
151
 
152
152
  ## Related pages
153
153
 
154
- - [Providers-and-Keys](Providers-and-Keys)
154
+ - [Keys and Services](Providers-and-Keys)
155
155
  - [Models](Models)
156
156
  - [Costs-and-Run-Cost](Costs-and-Run-Cost)
157
157
  - [Docker](Docker)