@gentbajko/slopify 3.25.8 → 4.0.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 (604) hide show
  1. package/dist/adapters/account-limits.js +30 -0
  2. package/dist/adapters/alignment/cache.js +3 -5
  3. package/dist/adapters/alignment/ctc.js +1 -2
  4. package/dist/adapters/alignment/lock.js +4 -3
  5. package/dist/adapters/alignment/multilingual.js +0 -3
  6. package/dist/adapters/alignment/prefetch.js +2 -1
  7. package/dist/adapters/alignment/worker.js +2 -1
  8. package/dist/adapters/explain.js +0 -3
  9. package/dist/adapters/ffmpeg.js +19 -1
  10. package/dist/adapters/host-cli/index.js +19 -3
  11. package/dist/adapters/http-failure.js +53 -0
  12. package/dist/adapters/image/codex.js +36 -4
  13. package/dist/adapters/image/fal.js +17 -50
  14. package/dist/adapters/image/google.js +31 -55
  15. package/dist/adapters/image/openai.js +20 -47
  16. package/dist/adapters/image/reference.js +36 -0
  17. package/dist/adapters/image/replicate.js +18 -47
  18. package/dist/adapters/interest/search-interest.js +113 -0
  19. package/dist/adapters/key-probes.js +53 -2
  20. package/dist/adapters/llm/antigravity.js +4 -0
  21. package/dist/adapters/llm/assistant-cli.js +388 -0
  22. package/dist/adapters/llm/child-session.js +83 -0
  23. package/dist/adapters/llm/claude-code-models.js +51 -98
  24. package/dist/adapters/llm/claude-code.js +13 -2
  25. package/dist/adapters/llm/codex-limits.js +27 -68
  26. package/dist/adapters/llm/codex-server-models.js +61 -109
  27. package/dist/adapters/llm/codex.js +12 -1
  28. package/dist/adapters/llm/openrouter.js +10 -47
  29. package/dist/adapters/llm/run-cli.js +6 -22
  30. package/dist/adapters/llm/sse-lines.js +5 -1
  31. package/dist/adapters/model-access.js +37 -0
  32. package/dist/adapters/tts/cartesia.js +15 -46
  33. package/dist/adapters/tts/elevenlabs.js +20 -52
  34. package/dist/adapters/tts/gemini.js +19 -40
  35. package/dist/adapters/tts/inworld-async.js +4 -1
  36. package/dist/adapters/tts/inworld.js +3 -0
  37. package/dist/adapters/tts/openai.js +20 -50
  38. package/dist/assets/models-lifecycle.yaml +6 -0
  39. package/dist/assistant-wiring.js +60 -0
  40. package/dist/autopilot-timers.js +59 -0
  41. package/dist/catalog/lifecycle.js +52 -0
  42. package/dist/catalog/propose.js +204 -0
  43. package/dist/catalog/registry.js +3 -1
  44. package/dist/catalog/schema.js +2 -0
  45. package/dist/catalog/store.js +39 -0
  46. package/dist/edge/autostart/native.js +3 -5
  47. package/dist/edge/autostart/prompt.js +2 -1
  48. package/dist/edge/autostart/service.js +3 -4
  49. package/dist/edge/cli-args.js +3 -4
  50. package/dist/edge/cli.js +2 -1
  51. package/dist/edge/docker-install/apply.js +11 -11
  52. package/dist/edge/docker-install/run.js +2 -1
  53. package/dist/edge/docker-install/state.js +4 -3
  54. package/dist/edge/docker-install/tree.js +2 -4
  55. package/dist/edge/docker-install/volume.js +3 -2
  56. package/dist/edge/docker.js +5 -4
  57. package/dist/edge/events/hub.js +5 -5
  58. package/dist/edge/http/actions.js +4 -25
  59. package/dist/edge/http/actor.js +5 -3
  60. package/dist/edge/http/app.js +85 -10
  61. package/dist/edge/http/assistant.js +127 -0
  62. package/dist/edge/http/audio-preview.js +4 -11
  63. package/dist/edge/http/auditions.js +1 -1
  64. package/dist/edge/http/autostart.js +5 -5
  65. package/dist/edge/http/backups.js +1 -3
  66. package/dist/edge/http/channel-memory.js +7 -20
  67. package/dist/edge/http/channels.js +13 -26
  68. package/dist/edge/http/checkpoints.js +33 -17
  69. package/dist/edge/http/design-templates.js +167 -0
  70. package/dist/edge/http/designs.js +178 -0
  71. package/dist/edge/http/diagnostics.js +5 -1
  72. package/dist/edge/http/document-themes.js +2 -11
  73. package/dist/edge/http/draft-files.js +1 -3
  74. package/dist/edge/http/draft-problem.js +4 -9
  75. package/dist/edge/http/drafts.js +3 -3
  76. package/dist/edge/http/entries.js +6 -9
  77. package/dist/edge/http/extension-auth.js +27 -0
  78. package/dist/edge/http/external-reviews.js +12 -11
  79. package/dist/edge/http/files.js +5 -11
  80. package/dist/edge/http/folder-location.js +1 -4
  81. package/dist/edge/http/fonts.js +0 -7
  82. package/dist/edge/http/home.js +38 -13
  83. package/dist/edge/http/host-cli.js +18 -8
  84. package/dist/edge/http/ids.js +8 -0
  85. package/dist/edge/http/keywords.js +35 -0
  86. package/dist/edge/http/model-checks.js +132 -0
  87. package/dist/edge/http/narration-peaks.js +5 -13
  88. package/dist/edge/http/onboarding.js +1 -14
  89. package/dist/edge/http/open-folder.js +5 -10
  90. package/dist/edge/http/outputs.js +4 -12
  91. package/dist/edge/http/patch-notes.js +4 -10
  92. package/dist/edge/http/planning.js +2 -6
  93. package/dist/edge/http/problem.js +2 -4
  94. package/dist/edge/http/project-create.js +1 -0
  95. package/dist/edge/http/project-templates.js +29 -17
  96. package/dist/edge/http/projects.js +2 -12
  97. package/dist/edge/http/prompts.js +7 -14
  98. package/dist/edge/http/pronunciations.js +1 -2
  99. package/dist/edge/http/providers.js +39 -24
  100. package/dist/edge/http/refusal.js +29 -0
  101. package/dist/edge/http/remote-access.js +40 -0
  102. package/dist/edge/http/remote-guard.js +71 -0
  103. package/dist/edge/http/request-guard.js +91 -0
  104. package/dist/edge/http/reviews.js +11 -24
  105. package/dist/edge/http/revision-files.js +5 -10
  106. package/dist/edge/http/revisions.js +10 -15
  107. package/dist/edge/http/run-cost.js +1 -2
  108. package/dist/edge/http/same-page.js +80 -0
  109. package/dist/edge/http/schedules.js +59 -32
  110. package/dist/edge/http/settings.js +22 -13
  111. package/dist/edge/http/social.js +282 -0
  112. package/dist/edge/http/staging.js +2 -4
  113. package/dist/edge/http/storage-files.js +5 -7
  114. package/dist/edge/http/storage.js +23 -23
  115. package/dist/edge/http/studio.js +23 -50
  116. package/dist/edge/http/style-preview.js +1 -4
  117. package/dist/edge/http/subtitles.js +3 -17
  118. package/dist/edge/http/template-studio.js +433 -0
  119. package/dist/edge/http/trash.js +10 -11
  120. package/dist/edge/http/tutorial.js +1 -2
  121. package/dist/edge/http/tutorials.js +5 -11
  122. package/dist/edge/http/update.js +7 -13
  123. package/dist/edge/http/waveform.js +3 -6
  124. package/dist/edge/http/youtube-edits.js +2 -3
  125. package/dist/edge/mcp/assistant.js +279 -0
  126. package/dist/edge/mcp/attribution.js +8 -2
  127. package/dist/edge/mcp/call-route.js +11 -2
  128. package/dist/edge/mcp/excluded-routes.js +25 -0
  129. package/dist/edge/mcp/http.js +11 -2
  130. package/dist/edge/mcp/instructions.js +5 -3
  131. package/dist/edge/mcp/library-tools.js +1 -0
  132. package/dist/edge/mcp/route-tool.js +35 -15
  133. package/dist/edge/mcp/server.js +13 -8
  134. package/dist/edge/mcp/tools-channels.js +2 -0
  135. package/dist/edge/mcp/tools-designs.js +165 -0
  136. package/dist/edge/mcp/tools-library.js +14 -2
  137. package/dist/edge/mcp/tools-planning.js +23 -4
  138. package/dist/edge/mcp/tools-projects.js +28 -1
  139. package/dist/edge/mcp/tools-runs.js +14 -1
  140. package/dist/edge/mcp/tools-schedules.js +28 -4
  141. package/dist/edge/mcp/tools-setup.js +61 -3
  142. package/dist/edge/mcp/tools-social.js +46 -0
  143. package/dist/edge/mcp/tools-storage.js +8 -0
  144. package/dist/edge/mcp/tools-template-studio.js +166 -0
  145. package/dist/edge/mcp/tools.js +6 -0
  146. package/dist/edge/remote-access/cli.js +71 -0
  147. package/dist/edge/remote-access/model.js +32 -0
  148. package/dist/edge/remote-access/rules.js +207 -0
  149. package/dist/edge/remote-access/service.js +203 -0
  150. package/dist/edge/remote-access/store.js +44 -0
  151. package/dist/edge/remote-access/views.js +43 -0
  152. package/dist/extension/slopify-studio-chrome.zip +0 -0
  153. package/dist/extension/slopify-studio-firefox.zip +0 -0
  154. package/dist/host-cli/install.js +2 -2
  155. package/dist/host-cli/open-folder.js +2 -2
  156. package/dist/host-cli/paths.js +2 -4
  157. package/dist/host-cli/server.js +4 -4
  158. package/dist/host-cli/service.js +3 -3
  159. package/dist/host-cli/status.js +7 -7
  160. package/dist/kernel/db/migrations/0063-model-checks.sql +20 -0
  161. package/dist/kernel/db/migrations/0064-template-studio.sql +46 -0
  162. package/dist/kernel/db/migrations/0065-promise-and-assistant-reviews.sql +58 -0
  163. package/dist/kernel/db/migrations/0066-assistant.sql +44 -0
  164. package/dist/kernel/db/migrations/0067-autopilot.sql +6 -0
  165. package/dist/kernel/db/migrations/0068-topic-scores.sql +17 -0
  166. package/dist/kernel/db/migrations/0070-designs.sql +53 -0
  167. package/dist/kernel/db/migrations/0071-design-usage.sql +33 -0
  168. package/dist/kernel/db/migrations/0072-design-templates.sql +33 -0
  169. package/dist/kernel/db/migrations/0073-schedule-design-kind.sql +5 -0
  170. package/dist/kernel/db/migrations/0074-template-bakeoffs.sql +44 -0
  171. package/dist/kernel/db/migrations/0075-social-posts.sql +25 -0
  172. package/dist/kernel/db/migrations/0076-channel-social.sql +5 -0
  173. package/dist/kernel/db/migrations/0077-post-prompts.sql +17 -0
  174. package/dist/kernel/db/tx.js +2 -4
  175. package/dist/kernel/errors.js +27 -1
  176. package/dist/kernel/json-answer.js +15 -0
  177. package/dist/kernel/lock.js +4 -9
  178. package/dist/kernel/paths.js +8 -0
  179. package/dist/kernel/ports/assistant-cli.js +4 -0
  180. package/dist/kernel/ports/host-cli.js +3 -0
  181. package/dist/kernel/ports/interest.js +4 -0
  182. package/dist/kernel/ports/languages.js +0 -3
  183. package/dist/kernel/ports/model.js +2 -0
  184. package/dist/kernel/runner/attempt.js +3 -1
  185. package/dist/kernel/runner/audition.js +3 -1
  186. package/dist/kernel/runner/checkpoint-authority.js +2 -1
  187. package/dist/kernel/runner/index.js +8 -10
  188. package/dist/kernel/runner/model-access.js +40 -0
  189. package/dist/kernel/runner/model-check.js +153 -0
  190. package/dist/kernel/runner/providers.js +15 -10
  191. package/dist/kernel/runner/standalone.js +9 -5
  192. package/dist/main.js +334 -19
  193. package/dist/model-checks.js +166 -0
  194. package/dist/patch-notes/3.25.9.md +5 -0
  195. package/dist/patch-notes/4.0.0.md +51 -0
  196. package/dist/patch-notes/index.json +12 -0
  197. package/dist/slices/admission/heads.js +2 -1
  198. package/dist/slices/admission/repo.js +2 -1
  199. package/dist/slices/admission/rules.js +43 -8
  200. package/dist/slices/admission/schema.js +8 -0
  201. package/dist/slices/admission/stage-writes.js +0 -3
  202. package/dist/slices/admission/start.js +48 -31
  203. package/dist/slices/admission/substitute.js +7 -4
  204. package/dist/slices/agents/attention-posts.js +31 -0
  205. package/dist/slices/agents/attention-templates.js +33 -0
  206. package/dist/slices/agents/attention.js +10 -1
  207. package/dist/slices/agents/connection.js +2 -6
  208. package/dist/slices/agents/model.js +6 -3
  209. package/dist/slices/agents/repo.js +15 -7
  210. package/dist/slices/article/continuation.js +0 -63
  211. package/dist/slices/article/plain.js +3 -2
  212. package/dist/slices/article/segments.js +2 -62
  213. package/dist/slices/assistant/access.js +1 -0
  214. package/dist/slices/assistant/asks.js +80 -0
  215. package/dist/slices/assistant/cost.js +54 -0
  216. package/dist/slices/assistant/gate.js +222 -0
  217. package/dist/slices/assistant/model.js +79 -0
  218. package/dist/slices/assistant/names.js +23 -0
  219. package/dist/slices/assistant/prompt.js +74 -0
  220. package/dist/slices/assistant/repo.js +74 -0
  221. package/dist/slices/assistant/tokens.js +18 -0
  222. package/dist/slices/assistant/turn.js +342 -0
  223. package/dist/slices/autopilot/buffer.js +76 -0
  224. package/dist/slices/autopilot/model.js +14 -0
  225. package/dist/slices/autopilot/notify.js +27 -0
  226. package/dist/slices/autopilot/repo.js +34 -0
  227. package/dist/slices/autopilot/signals.js +194 -0
  228. package/dist/slices/autopilot/warnings.js +85 -0
  229. package/dist/slices/autopilot/weekly.js +100 -0
  230. package/dist/slices/backups/files.js +2 -6
  231. package/dist/slices/backups/repo.js +3 -15
  232. package/dist/slices/backups/service.js +4 -3
  233. package/dist/slices/channels/cast-images.js +2 -1
  234. package/dist/slices/channels/model.js +0 -1
  235. package/dist/slices/channels/rebrand.js +0 -3
  236. package/dist/slices/channels/repo.js +4 -0
  237. package/dist/slices/channels/schema.js +6 -0
  238. package/dist/slices/channels/service.js +10 -0
  239. package/dist/slices/channels/videos.js +2 -14
  240. package/dist/slices/checkpoints/index.js +0 -1
  241. package/dist/slices/control/providers.js +0 -26
  242. package/dist/slices/designs/artwork.js +66 -0
  243. package/dist/slices/designs/budget.js +23 -0
  244. package/dist/slices/designs/calls.js +124 -0
  245. package/dist/slices/designs/check-context.js +38 -0
  246. package/dist/slices/designs/check-text.js +43 -0
  247. package/dist/slices/designs/fields.js +88 -0
  248. package/dist/slices/designs/frames.js +34 -0
  249. package/dist/slices/designs/gate.js +128 -0
  250. package/dist/slices/designs/layout.js +293 -0
  251. package/dist/slices/designs/model.js +35 -0
  252. package/dist/slices/designs/pdf-job.js +23 -0
  253. package/dist/slices/designs/pdf-pages.js +30 -0
  254. package/dist/slices/designs/pdf-runner.js +68 -0
  255. package/dist/slices/designs/pdf-worker.js +24 -0
  256. package/dist/slices/designs/picture-size.js +79 -0
  257. package/dist/slices/designs/pictures.js +55 -0
  258. package/dist/slices/designs/quote.js +24 -0
  259. package/dist/slices/designs/render-args.js +36 -0
  260. package/dist/slices/designs/render-input.js +59 -0
  261. package/dist/slices/designs/render.js +215 -0
  262. package/dist/slices/designs/repo.js +101 -0
  263. package/dist/slices/designs/requests.js +80 -0
  264. package/dist/slices/designs/runner.js +239 -0
  265. package/dist/slices/designs/sample-repo.js +91 -0
  266. package/dist/slices/designs/sample.js +222 -0
  267. package/dist/slices/designs/schema.js +87 -0
  268. package/dist/slices/designs/sections.js +44 -0
  269. package/dist/slices/designs/service.js +233 -0
  270. package/dist/slices/designs/steps-repo.js +102 -0
  271. package/dist/slices/designs/studio-kind.js +39 -0
  272. package/dist/slices/designs/template-checks.js +44 -0
  273. package/dist/slices/designs/template-make.js +118 -0
  274. package/dist/slices/designs/templates.js +218 -0
  275. package/dist/slices/document/fonts.js +2 -1
  276. package/dist/slices/episodes/summarize.js +5 -1
  277. package/dist/slices/estimate/index.js +45 -7
  278. package/dist/slices/fonts/catalog.js +3 -2
  279. package/dist/slices/fonts/discovery.js +3 -2
  280. package/dist/slices/fonts/files.js +2 -6
  281. package/dist/slices/fonts/index.js +0 -1
  282. package/dist/slices/fonts/sfnt.js +45 -10
  283. package/dist/slices/images/appearance.js +6 -15
  284. package/dist/slices/images/scenes.js +100 -27
  285. package/dist/slices/keywords/digest.js +84 -0
  286. package/dist/slices/keywords/facts.js +92 -0
  287. package/dist/slices/keywords/fill.js +80 -0
  288. package/dist/slices/keywords/model.js +75 -0
  289. package/dist/slices/keywords/rules.js +187 -0
  290. package/dist/slices/keywords/schema.js +28 -0
  291. package/dist/slices/keywords/specs.js +260 -0
  292. package/dist/slices/keywords/values.js +74 -0
  293. package/dist/slices/library/lint.js +29 -12
  294. package/dist/slices/library/model.js +2 -0
  295. package/dist/slices/library/photorealistic.js +4 -12
  296. package/dist/slices/library/rename.js +9 -7
  297. package/dist/slices/library/save.js +14 -10
  298. package/dist/slices/library/slots.js +37 -11
  299. package/dist/slices/library/transfer.js +2 -1
  300. package/dist/slices/library/used-by.js +25 -10
  301. package/dist/slices/loudness/loudnorm.js +51 -11
  302. package/dist/slices/model-checks/cost.js +61 -0
  303. package/dist/slices/model-checks/listing.js +124 -0
  304. package/dist/slices/model-checks/model.js +20 -0
  305. package/dist/slices/model-checks/record.js +32 -0
  306. package/dist/slices/model-checks/repo.js +120 -0
  307. package/dist/slices/model-checks/rules.js +112 -0
  308. package/dist/slices/model-checks/run.js +143 -0
  309. package/dist/slices/model-checks/service.js +198 -0
  310. package/dist/slices/model-upkeep/switch.js +70 -25
  311. package/dist/slices/model-upkeep/usage.js +26 -11
  312. package/dist/slices/narration/breaths.js +2 -1
  313. package/dist/slices/narration/clean-pieces.js +2 -1
  314. package/dist/slices/narration/concat.js +2 -1
  315. package/dist/slices/narration/peaks.js +3 -4
  316. package/dist/slices/narration/pronunciation-chunks.js +4 -3
  317. package/dist/slices/narration/pronunciation.js +3 -2
  318. package/dist/slices/narration/steering.js +4 -3
  319. package/dist/slices/notifications/notifier.js +4 -3
  320. package/dist/slices/notifications/rules.js +5 -2
  321. package/dist/slices/notifications/settings.js +4 -12
  322. package/dist/slices/notifications/subject.js +2 -1
  323. package/dist/slices/onboarding/copy.js +2 -1
  324. package/dist/slices/onboarding/first-run.js +3 -2
  325. package/dist/slices/onboarding/full-video.js +3 -2
  326. package/dist/slices/onboarding/install.js +4 -3
  327. package/dist/slices/onboarding/model.js +0 -16
  328. package/dist/slices/onboarding/packs.js +22 -22
  329. package/dist/slices/onboarding/quick-short.js +2 -1
  330. package/dist/slices/onboarding/sample.js +0 -1
  331. package/dist/slices/onboarding/state.js +2 -11
  332. package/dist/slices/onboarding/template.js +2 -1
  333. package/dist/slices/patch-notes/library.js +3 -2
  334. package/dist/slices/play-drafts/convert.js +4 -0
  335. package/dist/slices/play-drafts/review-inputs.js +8 -2
  336. package/dist/slices/play-drafts/review.js +5 -4
  337. package/dist/slices/play-drafts/schema.js +4 -0
  338. package/dist/slices/project-templates/from-project.js +9 -2
  339. package/dist/slices/project-templates/repo.js +6 -1
  340. package/dist/slices/project-templates/setup.js +8 -0
  341. package/dist/slices/project-templates/stand-ins.js +22 -0
  342. package/dist/slices/rebuild/activity.js +2 -0
  343. package/dist/slices/rebuild/admission-repo.js +3 -2
  344. package/dist/slices/rebuild/narration-history.js +4 -5
  345. package/dist/slices/rebuild/narration-retry.js +2 -1
  346. package/dist/slices/rebuild/preview-details.js +27 -9
  347. package/dist/slices/rebuild/preview-plan.js +3 -8
  348. package/dist/slices/rebuild/recipe-build.js +8 -3
  349. package/dist/slices/rebuild/recipe-describe.js +3 -3
  350. package/dist/slices/rebuild/recipe-digest.js +58 -0
  351. package/dist/slices/rebuild/recipe-input-schema.js +10 -0
  352. package/dist/slices/rebuild/recipe-keywords.js +71 -0
  353. package/dist/slices/rebuild/recipe-legacy.js +5 -1
  354. package/dist/slices/rebuild/recipe-model.js +4 -0
  355. package/dist/slices/rebuild/recipe-narration-text.js +2 -1
  356. package/dist/slices/rebuild/recipe-preparation.js +3 -3
  357. package/dist/slices/rebuild/recipe-reference.js +8 -4
  358. package/dist/slices/rebuild/recipe-reviews.js +27 -7
  359. package/dist/slices/rebuild/recipe-save.js +6 -3
  360. package/dist/slices/rebuild/recipe-scenes.js +40 -11
  361. package/dist/slices/rebuild/recipe-shorts.js +8 -8
  362. package/dist/slices/rebuild/recipe-social.js +46 -0
  363. package/dist/slices/rebuild/recipe-text.js +64 -20
  364. package/dist/slices/rebuild/recipe-validation.js +98 -28
  365. package/dist/slices/rebuild/recipe-visual.js +55 -16
  366. package/dist/slices/rebuild/recipe-work.js +15 -3
  367. package/dist/slices/rebuild/recipe-youtube.js +4 -3
  368. package/dist/slices/rebuild/recovery-selection.js +3 -1
  369. package/dist/slices/rebuild/recovery.js +3 -2
  370. package/dist/slices/rebuild/repo.js +4 -3
  371. package/dist/slices/rebuild/review-promise.js +25 -0
  372. package/dist/slices/rebuild/review-redo.js +2 -1
  373. package/dist/slices/rebuild/runtime-admission.js +4 -3
  374. package/dist/slices/rebuild/runtime-agent-review.js +11 -5
  375. package/dist/slices/rebuild/runtime-animate.js +9 -8
  376. package/dist/slices/rebuild/runtime-article.js +3 -2
  377. package/dist/slices/rebuild/runtime-checkpoints.js +5 -4
  378. package/dist/slices/rebuild/runtime-document.js +8 -11
  379. package/dist/slices/rebuild/runtime-export-bed.js +6 -11
  380. package/dist/slices/rebuild/runtime-export-edit.js +6 -15
  381. package/dist/slices/rebuild/runtime-export-inputs.js +7 -7
  382. package/dist/slices/rebuild/runtime-export-short.js +8 -4
  383. package/dist/slices/rebuild/runtime-export.js +34 -12
  384. package/dist/slices/rebuild/runtime-figure-card.js +2 -1
  385. package/dist/slices/rebuild/runtime-image.js +4 -9
  386. package/dist/slices/rebuild/runtime-instructions.js +2 -0
  387. package/dist/slices/rebuild/runtime-keywords.js +59 -0
  388. package/dist/slices/rebuild/runtime-lines.js +2 -11
  389. package/dist/slices/rebuild/runtime-local.js +14 -20
  390. package/dist/slices/rebuild/runtime-narration-publication.js +2 -5
  391. package/dist/slices/rebuild/runtime-narration-reuse.js +9 -13
  392. package/dist/slices/rebuild/runtime-narration-text.js +10 -15
  393. package/dist/slices/rebuild/runtime-pictures.js +2 -4
  394. package/dist/slices/rebuild/runtime-piece-label.js +10 -6
  395. package/dist/slices/rebuild/runtime-plan.js +31 -5
  396. package/dist/slices/rebuild/runtime-provider.js +60 -19
  397. package/dist/slices/rebuild/runtime-publication.js +10 -9
  398. package/dist/slices/rebuild/runtime-review.js +33 -28
  399. package/dist/slices/rebuild/runtime-run.js +4 -1
  400. package/dist/slices/rebuild/runtime-shorts.js +34 -50
  401. package/dist/slices/rebuild/runtime-store.js +3 -8
  402. package/dist/slices/rebuild/runtime-subtitles.js +16 -14
  403. package/dist/slices/rebuild/runtime-voices.js +8 -15
  404. package/dist/slices/rebuild/runtime-youtube.js +11 -29
  405. package/dist/slices/rebuild/transition-repo.js +6 -5
  406. package/dist/slices/rebuild/work-records.js +6 -0
  407. package/dist/slices/reruns/cascade.js +1 -2
  408. package/dist/slices/reruns/index.js +6 -9
  409. package/dist/slices/reviews/external-model.js +3 -0
  410. package/dist/slices/reviews/external-repo.js +2 -1
  411. package/dist/slices/reviews/external-rows.js +2 -3
  412. package/dist/slices/reviews/model.js +26 -8
  413. package/dist/slices/reviews/repo.js +2 -1
  414. package/dist/slices/reviews/rules.js +1 -0
  415. package/dist/slices/reviews/view.js +5 -2
  416. package/dist/slices/revisions/downloads.js +4 -5
  417. package/dist/slices/revisions/mutation-assets.js +7 -4
  418. package/dist/slices/revisions/mutation-cues.js +2 -5
  419. package/dist/slices/revisions/mutations.js +0 -1
  420. package/dist/slices/revisions/repo.js +3 -1
  421. package/dist/slices/revisions/schema.js +1 -0
  422. package/dist/slices/revisions/view.js +20 -0
  423. package/dist/slices/run-cost/limits.js +5 -2
  424. package/dist/slices/run-cost/meter.js +12 -5
  425. package/dist/slices/run-cost/panel.js +8 -74
  426. package/dist/slices/run-cost/plan-readings.js +127 -0
  427. package/dist/slices/run-cost/plan-use.js +169 -0
  428. package/dist/slices/run-cost/pricing.js +3 -3
  429. package/dist/slices/run-cost/week.js +32 -4
  430. package/dist/slices/schedules/agenda.js +11 -2
  431. package/dist/slices/schedules/by-date.js +42 -0
  432. package/dist/slices/schedules/calendar.js +5 -0
  433. package/dist/slices/schedules/design-agenda.js +36 -0
  434. package/dist/slices/schedules/design-check.js +41 -0
  435. package/dist/slices/schedules/design-run.js +126 -0
  436. package/dist/slices/schedules/next-run.js +62 -0
  437. package/dist/slices/schedules/prepare.js +18 -2
  438. package/dist/slices/schedules/repo.js +24 -16
  439. package/dist/slices/schedules/rescore.js +35 -0
  440. package/dist/slices/schedules/scheduler.js +13 -3
  441. package/dist/slices/schedules/schema.js +54 -3
  442. package/dist/slices/schedules/score.js +107 -0
  443. package/dist/slices/schedules/service.js +57 -24
  444. package/dist/slices/schedules/topic-list.js +15 -0
  445. package/dist/slices/schedules/topic-scores.js +104 -0
  446. package/dist/slices/schedules/topics.js +48 -36
  447. package/dist/slices/settings/cli-paths.js +4 -6
  448. package/dist/slices/settings/first-run.js +2 -11
  449. package/dist/slices/settings/model-reach.js +3 -17
  450. package/dist/slices/settings/model.js +10 -0
  451. package/dist/slices/settings/playback.js +33 -22
  452. package/dist/slices/settings/probe-read.js +16 -0
  453. package/dist/slices/settings/repo.js +33 -0
  454. package/dist/slices/settings/tutorial.js +2 -2
  455. package/dist/slices/settings/voices.js +4 -12
  456. package/dist/slices/shorts/pick.js +2 -15
  457. package/dist/slices/shorts/prompts.js +2 -13
  458. package/dist/slices/shorts/render.js +3 -0
  459. package/dist/slices/social/channel.js +44 -0
  460. package/dist/slices/social/defaults.js +17 -0
  461. package/dist/slices/social/due.js +36 -0
  462. package/dist/slices/social/extension.js +18 -0
  463. package/dist/slices/social/fit.js +115 -0
  464. package/dist/slices/social/limits.js +186 -0
  465. package/dist/slices/social/links.js +21 -0
  466. package/dist/slices/social/media.js +131 -0
  467. package/dist/slices/social/model.js +75 -0
  468. package/dist/slices/social/notice.js +19 -0
  469. package/dist/slices/social/pack.js +176 -0
  470. package/dist/slices/social/post-prompt.js +55 -0
  471. package/dist/slices/social/queue.js +31 -0
  472. package/dist/slices/social/repo.js +133 -0
  473. package/dist/slices/social/reshape.js +97 -0
  474. package/dist/slices/social/rules.js +5 -0
  475. package/dist/slices/social/schema.js +70 -0
  476. package/dist/slices/social/serve.js +51 -0
  477. package/dist/slices/social/service.js +128 -0
  478. package/dist/slices/social/settings.js +21 -0
  479. package/dist/slices/social/sources.js +114 -0
  480. package/dist/slices/social/state.js +110 -0
  481. package/dist/slices/social/text.js +149 -0
  482. package/dist/slices/storage/arrange.js +3 -1
  483. package/dist/slices/storage/assets.js +8 -10
  484. package/dist/slices/storage/backup-designs.js +74 -0
  485. package/dist/slices/storage/backup-export.js +8 -3
  486. package/dist/slices/storage/backup-format.js +10 -0
  487. package/dist/slices/storage/backup-import.js +20 -2
  488. package/dist/slices/storage/delete-project.js +6 -5
  489. package/dist/slices/storage/files-location.js +6 -8
  490. package/dist/slices/storage/layout.js +13 -2
  491. package/dist/slices/storage/model.js +3 -0
  492. package/dist/slices/storage/portable.js +16 -11
  493. package/dist/slices/storage/schema.js +2 -0
  494. package/dist/slices/storage/staging-refs.js +2 -1
  495. package/dist/slices/storage/staging.js +6 -9
  496. package/dist/slices/storage/trim.js +10 -3
  497. package/dist/slices/studio/calendar.js +4 -1
  498. package/dist/slices/studio/pick.js +2 -11
  499. package/dist/slices/studio/plan.js +6 -15
  500. package/dist/slices/studio/queue.js +3 -14
  501. package/dist/slices/studio/releases.js +26 -0
  502. package/dist/slices/studio/settings.js +14 -51
  503. package/dist/slices/studio/videos.js +12 -0
  504. package/dist/slices/style-preview/render.js +34 -13
  505. package/dist/slices/style-preview/schema.js +13 -0
  506. package/dist/slices/style-preview/service.js +2 -1
  507. package/dist/slices/style-preview/settings.js +33 -5
  508. package/dist/slices/telemetry/collector-client.js +2 -4
  509. package/dist/slices/telemetry/flush.js +2 -4
  510. package/dist/slices/telemetry/model.js +0 -7
  511. package/dist/slices/telemetry/record.js +10 -3
  512. package/dist/slices/template-studio/anatomy-view.js +118 -0
  513. package/dist/slices/template-studio/anatomy.js +73 -0
  514. package/dist/slices/template-studio/bakeoff-make.js +225 -0
  515. package/dist/slices/template-studio/bakeoff-repo.js +83 -0
  516. package/dist/slices/template-studio/bakeoff.js +333 -0
  517. package/dist/slices/template-studio/budget.js +103 -0
  518. package/dist/slices/template-studio/checks.js +273 -0
  519. package/dist/slices/template-studio/document-sample.js +56 -0
  520. package/dist/slices/template-studio/edit.js +15 -0
  521. package/dist/slices/template-studio/for-all.js +161 -0
  522. package/dist/slices/template-studio/free-video.js +31 -0
  523. package/dist/slices/template-studio/free.js +176 -0
  524. package/dist/slices/template-studio/gate.js +40 -0
  525. package/dist/slices/template-studio/hidden.js +20 -0
  526. package/dist/slices/template-studio/jobs.js +79 -0
  527. package/dist/slices/template-studio/loudness.js +111 -0
  528. package/dist/slices/template-studio/make.js +40 -0
  529. package/dist/slices/template-studio/model.js +54 -0
  530. package/dist/slices/template-studio/pack-import.js +253 -0
  531. package/dist/slices/template-studio/pack-samples.js +60 -0
  532. package/dist/slices/template-studio/pack.js +205 -0
  533. package/dist/slices/template-studio/paid-view.js +66 -0
  534. package/dist/slices/template-studio/paid.js +348 -0
  535. package/dist/slices/template-studio/quote.js +148 -0
  536. package/dist/slices/template-studio/ready.js +104 -0
  537. package/dist/slices/template-studio/repo.js +134 -0
  538. package/dist/slices/template-studio/reviews-path.js +58 -0
  539. package/dist/slices/template-studio/runner.js +84 -0
  540. package/dist/slices/template-studio/save.js +236 -0
  541. package/dist/slices/template-studio/say-it-as.js +248 -0
  542. package/dist/slices/template-studio/schedule-gate.js +34 -0
  543. package/dist/slices/template-studio/schedule-price.js +30 -0
  544. package/dist/slices/template-studio/schedule-sample.js +87 -0
  545. package/dist/slices/template-studio/schema.js +218 -0
  546. package/dist/slices/template-studio/scope.js +124 -0
  547. package/dist/slices/template-studio/sections.js +176 -0
  548. package/dist/slices/template-studio/service.js +173 -0
  549. package/dist/slices/template-studio/status.js +174 -0
  550. package/dist/slices/template-studio/topic.js +223 -0
  551. package/dist/slices/trash/designs.js +76 -0
  552. package/dist/slices/trash/model.js +13 -4
  553. package/dist/slices/trash/service.js +52 -2
  554. package/dist/slices/tutorials/library.js +3 -2
  555. package/dist/slices/video/ass-text.js +17 -0
  556. package/dist/slices/video/audio-export.js +2 -64
  557. package/dist/slices/video/encoding.js +160 -0
  558. package/dist/slices/video/ffmpeg.js +23 -21
  559. package/dist/slices/video/figure-card.js +1 -15
  560. package/dist/slices/video/slideshow.js +43 -18
  561. package/dist/slices/voices/palette.js +0 -3
  562. package/dist/slices/youtube/answer.js +2 -15
  563. package/dist/slices/youtube/edits-repo.js +2 -11
  564. package/dist/tutorials/Agents-MCP.md +2 -1
  565. package/dist/tutorials/Assistant.md +42 -0
  566. package/dist/tutorials/Channels.md +2 -0
  567. package/dist/tutorials/Home-and-Projects.md +19 -0
  568. package/dist/tutorials/Install.md +1 -0
  569. package/dist/tutorials/Keywords-Slopify-Fills.md +87 -0
  570. package/dist/tutorials/Models.md +33 -1
  571. package/dist/tutorials/Notifications.md +6 -0
  572. package/dist/tutorials/Play-Images.md +4 -0
  573. package/dist/tutorials/Posting-to-Social-Sites.md +121 -0
  574. package/dist/tutorials/Prompts.md +10 -6
  575. package/dist/tutorials/Remote-Access.md +63 -0
  576. package/dist/tutorials/Reviews-and-Checkpoints.md +13 -2
  577. package/dist/tutorials/Schedules.md +21 -1
  578. package/dist/tutorials/Settings-Reference.md +50 -8
  579. package/dist/tutorials/Template-Studio.md +125 -0
  580. package/dist/tutorials/Templates.md +3 -0
  581. package/dist/tutorials/_Sidebar.md +5 -0
  582. package/dist/updater/work-in-progress.js +42 -12
  583. package/dist/web/assets/index-CkAI_2E7.css +1 -0
  584. package/dist/web/assets/index-DcD0cdfo.js +334 -0
  585. package/dist/web/assets/{pdf-T7MCwjIv.js → pdf-TODRwF31.js} +1 -1
  586. package/dist/web/index.html +2 -2
  587. package/package.json +5 -1
  588. package/dist/slices/article/provided-entries.js +0 -66
  589. package/dist/slices/article/run.js +0 -145
  590. package/dist/slices/images/run.js +0 -234
  591. package/dist/slices/narration/legacy-plan.js +0 -58
  592. package/dist/slices/narration/run.js +0 -270
  593. package/dist/slices/rebuild/runtime-actions.js +0 -90
  594. package/dist/slices/research/run.js +0 -224
  595. package/dist/slices/subtitles/prepare.js +0 -194
  596. package/dist/slices/subtitles/transcript.js +0 -34
  597. package/dist/slices/thumbnail/run.js +0 -219
  598. package/dist/slices/video/audio-inputs.js +0 -33
  599. package/dist/slices/video/reuse-audio.js +0 -62
  600. package/dist/slices/video/run.js +0 -78
  601. package/dist/slices/video/write-export.js +0 -158
  602. package/dist/slices/video/write-subtitles.js +0 -59
  603. package/dist/web/assets/index-BadsoORk.css +0 -1
  604. package/dist/web/assets/index-D8LqNsCl.js +0 -326
@@ -0,0 +1,160 @@
1
+ import { constants, setPriority } from "node:os";
2
+ import { errorText } from "../../kernel/errors.js";
3
+ import { lookEncoding } from "./look.js";
4
+ // Settings → Production defaults → Rendering: how this computer encodes a video's picture.
5
+ // They change how a video is made, never what it is, so no recipe fingerprint carries them and
6
+ // a render reads them once as it starts (`renderMachine`).
7
+ export const videoEncoders = ["cpu", "nvidia"];
8
+ export const clipEncodeSpeeds = ["thorough", "fast"];
9
+ export const renderPriorities = ["normal", "background"];
10
+ // Today's render: x264 at its default preset, at normal priority.
11
+ export const defaultEncode = { kind: "x264", clipSpeed: "thorough" };
12
+ // Clips that are joined by copying get the final encode's settings. Clips that will be decoded
13
+ // again under burned-in captions are kept closer to the source, so the second encode is not a
14
+ // visible second generation.
15
+ const intermediateCrf = "16";
16
+ // measured on two minutes of a real project (changelog.d/2026-10-13-render-settings.md): 20
17
+ // matches CRF 16's quality for the clips under captions; 28.5 makes the finished video about the
18
+ // size CRF 23 does, where NVENC scores about 2.5 VMAF below x264 (no NVENC setting tried closed
19
+ // that gap at that size).
20
+ export const nvencQuality = { intermediate: 20, final: 28.5 };
21
+ // `final` is a picture someone watches (a clip joined by copying, a burned-in part, the join);
22
+ // `intermediate` is a clip decoded again under burned-in captions.
23
+ export function codecArgs(encode, role, look) {
24
+ if (encode.kind === "nvenc")
25
+ return [
26
+ "-c:v",
27
+ "h264_nvenc",
28
+ // The slowest, best preset, constant quality with no bitrate cap. No `-multipass`: measured,
29
+ // a second full-resolution pass gave no better picture for its size on these videos.
30
+ "-preset",
31
+ "p7",
32
+ "-tune",
33
+ "hq",
34
+ "-rc",
35
+ "vbr",
36
+ "-cq",
37
+ String(nvencQuality[role]),
38
+ "-b:v",
39
+ "0",
40
+ // x264's own keyframe interval, so a clip opens on its one keyframe as x264's do and the
41
+ // clips still join by copying.
42
+ "-g",
43
+ "250",
44
+ "-pix_fmt",
45
+ "yuv420p",
46
+ ];
47
+ return [
48
+ "-c:v",
49
+ "libx264",
50
+ "-pix_fmt",
51
+ "yuv420p",
52
+ ...lookEncoding(look),
53
+ ...(role === "intermediate"
54
+ ? [...(encode.clipSpeed === "fast" ? ["-preset", "veryfast"] : []), "-crf", intermediateCrf]
55
+ : []),
56
+ ];
57
+ }
58
+ export const noNvenc = "No NVIDIA encoder found on this computer.";
59
+ // The renders' own settings on a tiny picture: a driver or card that refuses any of them fails
60
+ // here rather than in a render. NVENC refuses anything under about 145 pixels across.
61
+ const testArgs = [
62
+ "-hide_banner",
63
+ "-nostdin",
64
+ "-loglevel",
65
+ "error",
66
+ "-f",
67
+ "lavfi",
68
+ "-i",
69
+ "color=c=black:s=256x256:r=30:d=0.1",
70
+ "-frames:v",
71
+ "1",
72
+ ...codecArgs({ kind: "nvenc", bin: "", clipSpeed: "thorough" }, "final", undefined),
73
+ "-f",
74
+ "null",
75
+ "-",
76
+ ];
77
+ // The filters a render runs in the NVENC ffmpeg besides the encoder.
78
+ const neededFilters = ["zoompan", "xfade", "ass"];
79
+ // Which ffmpeg can encode on an NVIDIA card, asked once and remembered until Slopify restarts.
80
+ // The bundled ffmpeg first, then the computer's own: unlike everything else, NVENC is the
81
+ // computer's (its driver and its ffmpeg), and only used when chosen in Settings.
82
+ export function createNvencProbe(deps) {
83
+ let answer;
84
+ return () => {
85
+ answer ??= probe(deps).catch((error) => ({
86
+ available: false,
87
+ reason: `${noNvenc} (${errorText(error)})`,
88
+ }));
89
+ return answer;
90
+ };
91
+ }
92
+ async function probe(deps) {
93
+ let failed;
94
+ for (const bin of new Set(deps.candidates)) {
95
+ const encoders = await deps.exec(bin, ["-hide_banner", "-encoders"]).catch(() => "");
96
+ if (!/^\s*V\S*\s+h264_nvenc\s/m.test(encoders))
97
+ continue;
98
+ const filters = await deps.exec(bin, ["-hide_banner", "-filters"]).catch(() => "");
99
+ const missing = neededFilters.filter((name) => !new RegExp(`^\\s*\\S+\\s+${name}\\s`, "m").test(filters));
100
+ if (missing.length > 0) {
101
+ failed = `the ffmpeg at ${bin} has NVENC but not the ${missing.join(", ")} filter a video needs`;
102
+ continue;
103
+ }
104
+ try {
105
+ await deps.exec(bin, testArgs);
106
+ deps.log.write("info", "video.encoder", { detail: `NVENC works through ${bin}` });
107
+ return { available: true, bin };
108
+ }
109
+ catch (error) {
110
+ failed = `the ffmpeg at ${bin} lists NVENC but its test encode failed: ${errorText(error)}`;
111
+ }
112
+ }
113
+ if (failed !== undefined) {
114
+ deps.log.write("info", "video.encoder", { detail: failed });
115
+ return { available: false, reason: `${noNvenc} (${failed})` };
116
+ }
117
+ return { available: false, reason: noNvenc };
118
+ }
119
+ // The render's machine settings, read as it starts. A card chosen but not working is a notice,
120
+ // never a failed render: the processor encodes it instead.
121
+ export async function renderMachine(deps) {
122
+ const { settings } = deps;
123
+ const cpu = { kind: "x264", clipSpeed: settings.clipEncodeSpeed };
124
+ if (settings.videoEncoder !== "nvidia")
125
+ return { machine: { encode: cpu, priority: settings.renderPriority } };
126
+ const status = await deps.nvenc();
127
+ if (status.available)
128
+ return {
129
+ machine: {
130
+ encode: { kind: "nvenc", bin: status.bin, clipSpeed: settings.clipEncodeSpeed },
131
+ priority: settings.renderPriority,
132
+ },
133
+ };
134
+ deps.log.write("warn", "video.encoder", {
135
+ projectId: deps.projectId,
136
+ detail: `NVIDIA encoder chosen but unavailable; encoding on the processor. ${status.reason}`,
137
+ });
138
+ return {
139
+ machine: { encode: cpu, priority: settings.renderPriority },
140
+ notice: nvencFallbackNotice(status.reason),
141
+ };
142
+ }
143
+ export function nvencFallbackNotice(reason) {
144
+ return `This video was encoded on the processor because the NVIDIA graphics card encoder didn't work (${reason.replace(/\.$/, "")}). Check the NVIDIA driver, or choose Processor (x264) in Settings → Production defaults → Video encoder.`;
145
+ }
146
+ // Background renders run below normal priority: nice 10 on Linux and macOS, the below-normal
147
+ // class on Windows. Threads ffmpeg starts afterwards inherit it. Advisory: a system that
148
+ // refuses is logged and the render carries on.
149
+ export function setRenderPriority(pid, priority, log, set = setPriority) {
150
+ if (priority !== "background")
151
+ return;
152
+ try {
153
+ set(pid, constants.priority.PRIORITY_BELOW_NORMAL);
154
+ }
155
+ catch (error) {
156
+ log.write("warn", "video.priority", {
157
+ detail: `could not lower the render's priority: ${errorText(error)}`,
158
+ });
159
+ }
160
+ }
@@ -1,7 +1,9 @@
1
1
  import { spawn } from "node:child_process";
2
2
  import { isAbsolute, resolve as resolvePath } from "node:path";
3
+ import { errorText, internalError } from "../../kernel/errors.js";
3
4
  import { bedChains, bedInputs } from "./ambient-mix.js";
4
- import { gradeFilter, lookChain, lookEncoding, placed } from "./look.js";
5
+ import { codecArgs, defaultEncode, setRenderPriority, } from "./encoding.js";
6
+ import { gradeFilter, lookChain, placed } from "./look.js";
5
7
  import { decimal, zoomRange } from "./plan.js";
6
8
  import { segments, transitionHalves } from "./transitions.js";
7
9
  // Hand-rolled per the standards: the filtergraph is the load-bearing part of this slice
@@ -44,7 +46,7 @@ export function slideshowClips(edit) {
44
46
  named.set(key, name);
45
47
  const shot = edit.shots[segment.kind === "shot" ? segment.shot : segment.to];
46
48
  if (shot === undefined)
47
- throw new Error("Slopify hit an internal error (the video's edit list lost a shot). Use More → Render the video again in the Video section; if it happens again, use Download diagnostics in Settings and report it.");
49
+ throw new Error(internalError("the video's edit list lost a shot", "Use More → Render the video again in the Video section"));
48
50
  clips.push({ name, segment, shot, frames: segment.count });
49
51
  }
50
52
  return name;
@@ -77,10 +79,6 @@ function clipKey(edit, segment) {
77
79
  ? [shot?.source, shot?.motion, shot?.frames]
78
80
  : [shot?.source, shot?.motion, shot?.frames, segment.from, segment.count, at]);
79
81
  }
80
- // Clips that are joined by copying get the final encode's settings. Clips that will be
81
- // decoded again under burned-in captions are kept closer to the source, so the second
82
- // encode is not a visible second generation.
83
- const intermediateCrf = "16";
84
82
  // A whole shot as one clip.
85
83
  export function clipArgs(frame, shot, output, intermediate = false) {
86
84
  return segmentArgs({ ...frame, shots: [shot] }, { kind: "shot", shot: 0, from: 0, count: shot.frames, start: 0 }, output, intermediate);
@@ -88,12 +86,14 @@ export function clipArgs(frame, shot, output, intermediate = false) {
88
86
  // One clip of the slideshow: part of a shot, or a transition between two, with the Look.
89
87
  export function segmentArgs(edit, segment, output, intermediate = false,
90
88
  // The clip's own chapter-card script, beside it in the working folder (`cards.ts`).
91
- cardsFile = "cards.ass") {
89
+ cardsFile = "cards.ass",
90
+ // Settings → Production defaults → Rendering (`encoding.ts`).
91
+ encode = defaultEncode) {
92
92
  const inputs = [];
93
93
  const chains = [];
94
94
  const picture = (shot, from, label) => {
95
95
  if (shot === undefined)
96
- throw new Error("Slopify hit an internal error (the video's edit list lost a shot). Use More → Render the video again in the Video section; if it happens again, use Download diagnostics in Settings and report it.");
96
+ throw new Error(internalError("the video's edit list lost a shot", "Use More → Render the video again in the 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);
@@ -125,9 +125,7 @@ cardsFile = "cards.ass") {
125
125
  ...(segment.kind === "shot" && segment.from === 0 && look === undefined
126
126
  ? []
127
127
  : ["-frames:v", String(segment.count)]),
128
- ...videoCodec,
129
- ...lookEncoding(edit.look),
130
- ...(intermediate ? ["-crf", intermediateCrf] : []),
128
+ ...codecArgs(encode, intermediate ? "intermediate" : "final", edit.look),
131
129
  "-an",
132
130
  output,
133
131
  ];
@@ -257,7 +255,7 @@ export function audioMixArgs(edit, output, codec = "wav") {
257
255
  }
258
256
  // `sound`, when given, is the finished AAC (`audioMixArgs`), copied in; without it the join
259
257
  // mixes and encodes the edit list's audio itself.
260
- export function joinArgs(edit, output, list, burnSubtitles = false, portraits = [], sound) {
258
+ export function joinArgs(edit, output, list, burnSubtitles = false, portraits = [], sound, encode = defaultEncode) {
261
259
  const mix = sound === undefined ? audioMix(edit, 1) : { inputs: ["-i", sound], chains: [] };
262
260
  const inputs = ["-f", "concat", "-i", list, ...mix.inputs];
263
261
  const chains = burnSubtitles ? burnIn(inputs, portraits) : [];
@@ -270,7 +268,7 @@ export function joinArgs(edit, output, list, burnSubtitles = false, portraits =
270
268
  "-map",
271
269
  burnSubtitles ? "[v]" : "0:v",
272
270
  ...(audio ? ["-map", sound === undefined ? "[a]" : "1:a"] : []),
273
- ...(burnSubtitles ? [...videoCodec, ...lookEncoding(edit.look)] : ["-c:v", "copy"]),
271
+ ...(burnSubtitles ? codecArgs(encode, "final", edit.look) : ["-c:v", "copy"]),
274
272
  ...(audio ? ["-c:a", sound === undefined ? "aac" : "copy"] : ["-an"]),
275
273
  "-movflags",
276
274
  "+faststart",
@@ -280,7 +278,7 @@ export function joinArgs(edit, output, list, burnSubtitles = false, portraits =
280
278
  // One part of a long video's picture with its captions burned in, from frame `start` of the
281
279
  // video (`slideshow.ts` encodes the parts side by side, then joins them by copying). The part's
282
280
  // frames are moved to their place in the video for the captions and back to zero after them.
283
- export function burnPartArgs(edit, list, output, start, portraits = []) {
281
+ export function burnPartArgs(edit, list, output, start, portraits = [], encode = defaultEncode) {
284
282
  const inputs = ["-f", "concat", "-i", list];
285
283
  const shifted = start === 0
286
284
  ? { before: "", after: "" }
@@ -298,8 +296,7 @@ export function burnPartArgs(edit, list, output, start, portraits = []) {
298
296
  chains.join(";"),
299
297
  "-map",
300
298
  "[v]",
301
- ...videoCodec,
302
- ...lookEncoding(edit.look),
299
+ ...codecArgs(encode, "final", edit.look),
303
300
  "-an",
304
301
  output,
305
302
  ];
@@ -333,7 +330,6 @@ const progressArgs = [
333
330
  "-nostats",
334
331
  "-y",
335
332
  ];
336
- const videoCodec = ["-c:v", "libx264", "-pix_fmt", "yuv420p"];
337
333
  const centredX = "iw/2-(iw/zoom/2)";
338
334
  const centredY = "ih/2-(ih/zoom/2)";
339
335
  // zoompan's expressions for a motion, or undefined for one that does not move. `on` is
@@ -403,6 +399,9 @@ export function runFfmpeg(run) {
403
399
  windowsHide: true,
404
400
  ...(run.cwd === undefined ? {} : { cwd: run.cwd }),
405
401
  });
402
+ // Before ffmpeg starts its threads, which inherit the priority.
403
+ if (child.pid !== undefined && run.priority !== undefined)
404
+ setRenderPriority(child.pid, run.priority, run.log);
406
405
  const errors = [];
407
406
  let pending = "";
408
407
  // A throw out of a stream listener is an uncaughtException, not a rejection: it would
@@ -413,7 +412,7 @@ export function runFfmpeg(run) {
413
412
  work();
414
413
  }
415
414
  catch (error) {
416
- run.log.write("warn", what, { detail: messageOf(error) });
415
+ run.log.write("warn", what, { detail: errorText(error) });
417
416
  }
418
417
  };
419
418
  const report = (line) => {
@@ -508,9 +507,6 @@ export async function probeDurationMs(bin, file, signal, log) {
508
507
  });
509
508
  return last;
510
509
  }
511
- function messageOf(error) {
512
- return error instanceof Error ? error.message : String(error);
513
- }
514
510
  function failure(code, signal, errors) {
515
511
  const ended = code === null ? `killed by ${signal ?? "a signal"}` : `exited with code ${code}`;
516
512
  const said = errors.length === 0
@@ -518,3 +514,9 @@ function failure(code, signal, errors) {
518
514
  : `ffmpeg ${ended}: ${errors.join(" / ")}`;
519
515
  return `The audio/video export failed (${said}). Try again; if it fails again, make sure your disk has free space (see Settings → Backup & storage), then use Download diagnostics in Settings and report it.`;
520
516
  }
517
+ // `runFfmpeg` at Settings → Production defaults → Render priority, read as each call starts: for
518
+ // the work outside a video's render that still runs ffmpeg on this computer (a design's drawing,
519
+ // a post's picture, Template Studio's free samples). A priority the caller set itself is kept.
520
+ export function runFfmpegAt(priority, run = runFfmpeg) {
521
+ return (call) => run({ ...call, priority: call.priority ?? priority() });
522
+ }
@@ -1,5 +1,6 @@
1
1
  import { remark } from "remark";
2
2
  import remarkGfm from "remark-gfm";
3
+ import { assText, assColour as colour } from "./ass-text.js";
3
4
  import { runFfmpeg } from "./ffmpeg.js";
4
5
  // The 3.0 design tokens (`packages/web/src/styles/index.css`): the surface graphite, ink,
5
6
  // the second ink, Slopify lime and the line colour, in the bundled Barlow.
@@ -449,21 +450,6 @@ function wrap(textValue, width, most) {
449
450
  function clip(value, room) {
450
451
  return value.length <= room ? value : `${value.slice(0, Math.max(1, room - 1))}…`;
451
452
  }
452
- // Text as libass draws it literally: braces and backslashes escaped rather than read as
453
- // override tags; code keeps its spaces, prose has them collapsed.
454
- function assText(value, keepSpaces = false) {
455
- const escaped = value.replace(/\\/g, "\\\\").replace(/\{/g, "\\{").replace(/\}/g, "\\}");
456
- return keepSpaces
457
- ? escaped.replace(/ {2,}/g, (run) => "\\h".repeat(run.length))
458
- : escaped.replace(/\s+/g, " ");
459
- }
460
- function colour(hex) {
461
- const match = /^#([0-9A-Fa-f]{2})([0-9A-Fa-f]{2})([0-9A-Fa-f]{2})$/.exec(hex);
462
- if (match === null)
463
- return "&HFFFFFF&";
464
- const [, r, g, b] = match;
465
- return `&H${(b ?? "").toUpperCase()}${(g ?? "").toUpperCase()}${(r ?? "").toUpperCase()}&`;
466
- }
467
453
  function text(x, y, align, body) {
468
454
  return `Dialogue: 0,0:00:00.00,0:00:10.00,Card,,0,0,0,,{\\an${String(align)}\\pos(${String(Math.round(x))},${String(Math.round(y))})}${body}`;
469
455
  }
@@ -1,8 +1,10 @@
1
1
  import { copyFileSync, mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
2
2
  import { extname, join } from "node:path";
3
+ import { errorText, internalError } from "../../kernel/errors.js";
3
4
  import { masterFile, masterReport } from "../loudness/loudnorm.js";
4
5
  import { cardsAss } from "./cards.js";
5
6
  import { cacheKey, fileIdentity, keepClip, pruneClips, reuseClip } from "./clip-cache.js";
7
+ import { defaultEncode, nvencFallbackNotice, } from "./encoding.js";
6
8
  import { audioMixArgs, burnPartArgs, concatList, joinArgs, runFfmpeg, segmentArgs, slideshowClips, } from "./ffmpeg.js";
7
9
  import { cardsIn } from "./look.js";
8
10
  import { inPool, renderJobs } from "./pool.js";
@@ -14,9 +16,31 @@ const copyWeight = 0.05;
14
16
  const leastPartFrames = 60 * 30;
15
17
  // The sound the join plays, as the join itself brings every segment to it.
16
18
  const masterFormat = { sampleRate: 44100, channels: 2 };
17
- // What the master measured, when the run asked for one.
19
+ // What the master measured, when the run asked for one. A render on the NVIDIA card that fails
20
+ // for any reason but a cancel is made again on the processor: the card never fails a render.
18
21
  export async function renderSlideshow(run) {
22
+ const encode = run.machine?.encode ?? defaultEncode;
23
+ if (encode.kind !== "nvenc")
24
+ return renderWith(run, encode);
25
+ try {
26
+ return await renderWith(run, encode);
27
+ }
28
+ catch (error) {
29
+ if (run.signal.aborted)
30
+ throw error;
31
+ const detail = errorText(error);
32
+ run.log.write("warn", "video.encoder", {
33
+ detail: `the NVENC render failed; rendering again on the processor: ${detail}`,
34
+ });
35
+ run.notice?.(nvencFallbackNotice(`it stopped the render: ${detail}`));
36
+ return renderWith(run, { kind: "x264", clipSpeed: encode.clipSpeed });
37
+ }
38
+ }
39
+ async function renderWith(run, encode) {
19
40
  const { edit } = run;
41
+ const priority = run.machine?.priority;
42
+ // The ffmpeg that encodes the picture; copying, sound and probing stay with the bundled one.
43
+ const encoder = encode.kind === "nvenc" ? encode.bin : run.bin;
20
44
  const { clips, order } = slideshowClips(edit);
21
45
  const msOf = (frames) => (frames / edit.fps) * 1000;
22
46
  const joinMs = msOf(edit.shots.reduce((sum, shot) => sum + shot.frames, 0));
@@ -38,7 +62,7 @@ export async function renderSlideshow(run) {
38
62
  const font = edit.cardFont;
39
63
  if (cards.length > 0) {
40
64
  if (font === undefined)
41
- throw new Error("Slopify hit an internal error (the chapter cards have no font). Use More → Render the video again in the Video section; if it happens again, use Download diagnostics in Settings and report it.");
65
+ throw new Error(internalError("the chapter cards have no font", "Use More → Render the video again in the Video section"));
42
66
  mkdirSync(join(workspace, "fonts"), { mode: 0o700 });
43
67
  copyFileSync(font.path, join(workspace, "fonts", `card${extname(font.path)}`));
44
68
  }
@@ -81,7 +105,7 @@ export async function renderSlideshow(run) {
81
105
  if (drawn !== undefined)
82
106
  writeFileSync(join(workspace, script), drawn, { mode: 0o600 });
83
107
  const output = join(workspace, clip.name);
84
- const args = segmentArgs(edit, clip.segment, output, run.burnSubtitles, script);
108
+ const args = segmentArgs(edit, clip.segment, output, run.burnSubtitles, script, encode);
85
109
  const weight = msOf(clip.frames) * clipWeight;
86
110
  const cache = run.cache;
87
111
  let key;
@@ -89,7 +113,7 @@ export async function renderSlideshow(run) {
89
113
  // let it go a moment ago) is encoded as if there were none.
90
114
  try {
91
115
  if (cache !== undefined) {
92
- key = cacheKey(run.bin, args.map((value) => drawn === undefined ? value : value.replaceAll(script, "\u0000cards")), output, drawn === undefined || font === undefined ? [] : [drawn, fileIdentity(font.path)]);
116
+ key = cacheKey(encoder, args.map((value) => drawn === undefined ? value : value.replaceAll(script, "\u0000cards")), output, drawn === undefined || font === undefined ? [] : [drawn, fileIdentity(font.path)]);
93
117
  keys.add(key);
94
118
  if (reuseClip(cache, key, output)) {
95
119
  rendered += weight;
@@ -99,15 +123,16 @@ export async function renderSlideshow(run) {
99
123
  }
100
124
  }
101
125
  catch (error) {
102
- run.log.write("warn", "video.cache", { detail: messageOf(error) });
126
+ run.log.write("warn", "video.cache", { detail: errorText(error) });
103
127
  rmSync(output, { force: true });
104
128
  }
105
129
  await runFfmpeg({
106
- bin: run.bin,
130
+ bin: encoder,
107
131
  ...(cards.length > 0 ? { cwd: workspace } : {}),
108
132
  args,
109
133
  signal,
110
134
  log: run.log,
135
+ priority,
111
136
  onProgress: (elapsedMs) => {
112
137
  running.set(clip.name, Math.min(elapsedMs * clipWeight, weight));
113
138
  tell();
@@ -120,7 +145,7 @@ export async function renderSlideshow(run) {
120
145
  keepClip(cache, key, output);
121
146
  }
122
147
  catch (error) {
123
- run.log.write("warn", "video.cache", { detail: messageOf(error) });
148
+ run.log.write("warn", "video.cache", { detail: errorText(error) });
124
149
  }
125
150
  }
126
151
  });
@@ -138,11 +163,12 @@ export async function renderSlideshow(run) {
138
163
  const partList = join(workspace, `${part.name}.ffconcat`);
139
164
  writeFileSync(partList, concatList(part.names), { mode: 0o600 });
140
165
  await runFfmpeg({
141
- bin: run.bin,
166
+ bin: encoder,
142
167
  cwd: run.cwd,
143
- args: burnPartArgs(edit, partList, join(workspace, `${part.name}.mp4`), part.start, run.portraits),
168
+ args: burnPartArgs(edit, partList, join(workspace, `${part.name}.mp4`), part.start, run.portraits, encode),
144
169
  signal,
145
170
  log: run.log,
171
+ priority,
146
172
  onProgress: (elapsedMs) => {
147
173
  done.set(part.name, Math.min(elapsedMs, msOf(part.frames)));
148
174
  burned();
@@ -167,11 +193,12 @@ export async function renderSlideshow(run) {
167
193
  throw new Error("the render was canceled");
168
194
  const burnsHere = run.burnSubtitles && parts.length <= 1;
169
195
  await runFfmpeg({
170
- bin: run.bin,
196
+ bin: burnsHere ? encoder : run.bin,
171
197
  cwd: run.cwd,
172
- args: joinArgs(edit, run.output, played, burnsHere, run.portraits, sound),
198
+ args: joinArgs(edit, run.output, played, burnsHere, run.portraits, sound, encode),
173
199
  signal: run.signal,
174
200
  log: run.log,
201
+ priority,
175
202
  onProgress: (elapsedMs) => report(rendered + elapsedMs * (burnsHere ? 1 : copyWeight)),
176
203
  });
177
204
  if (run.cache !== undefined) {
@@ -179,12 +206,12 @@ export async function renderSlideshow(run) {
179
206
  pruneClips(run.cache, keys);
180
207
  }
181
208
  catch (error) {
182
- run.log.write("warn", "video.cache", { detail: messageOf(error) });
209
+ run.log.write("warn", "video.cache", { detail: errorText(error) });
183
210
  }
184
211
  }
185
212
  // What the finished video measures, after its AAC encode.
186
213
  return run.master !== undefined && edit.audio.length > 0
187
- ? await masterReport(run, run.output, run.master)
214
+ ? await masterReport({ ...run, priority }, run.output, run.master)
188
215
  : undefined;
189
216
  }
190
217
  finally {
@@ -194,13 +221,14 @@ export async function renderSlideshow(run) {
194
221
  // The video's finished sound as AAC in the working folder: the narration, silences and bed
195
222
  // mixed, and with Level the volume mastered first, alone and then as one file.
196
223
  async function renderSound(run, edit, workspace, signal) {
197
- const quiet = { bin: run.bin, signal, log: run.log, onProgress: () => { } };
224
+ const priority = run.machine?.priority;
225
+ const quiet = { bin: run.bin, signal, log: run.log, priority, onProgress: () => { } };
198
226
  let played = edit;
199
227
  if (run.master !== undefined) {
200
228
  const mixed = join(workspace, "mix.wav");
201
229
  await runFfmpeg({ ...quiet, args: audioMixArgs(edit, mixed) });
202
230
  const mastered = join(workspace, "master.wav");
203
- await masterFile({ ...run, signal }, mixed, mastered, run.master, masterFormat);
231
+ await masterFile({ ...run, signal, priority }, mixed, mastered, run.master, masterFormat);
204
232
  played = {
205
233
  ...edit,
206
234
  audio: [
@@ -249,6 +277,3 @@ export function burnParts(order, clips, jobs) {
249
277
  parts.push({ name: `p${String(parts.length + 1)}`, names, start, frames: at - start });
250
278
  return parts;
251
279
  }
252
- function messageOf(error) {
253
- return error instanceof Error ? error.message : String(error);
254
- }
@@ -14,9 +14,6 @@ export function speakerColour(index) {
14
14
  }
15
15
  // ASS writes colours blue first: &HAABBGGRR in a style (alpha 00 is opaque) and &HBBGGRR& in
16
16
  // an inline override.
17
- export function assColour(hex, alpha = 0) {
18
- return `&H${alpha.toString(16).padStart(2, "0")}${bgr(hex)}`.toUpperCase();
19
- }
20
17
  export function assInlineColour(hex) {
21
18
  return `&H${bgr(hex)}&`.toUpperCase();
22
19
  }
@@ -1,4 +1,5 @@
1
1
  import { z } from "zod";
2
+ import { jsonBetween } from "../../kernel/json-answer.js";
2
3
  import { alternativeTitles, descriptionMaxCharacters, hashtagsMax, minChapterSeconds, minChapters, pinnedCommentMaxCharacters, tagMaxCharacters, tagsMaxCharacters, titleMaxCharacters, } from "./model.js";
3
4
  import { parseTimestamp, youtubeTimestamp } from "./timestamps.js";
4
5
  import { keepsShape, titleShapeRule } from "./titles.js";
@@ -60,7 +61,7 @@ export function checkDescriptionAnswer(text, durationSeconds,
60
61
  title = "",
61
62
  // Its pattern, whose fixed wording the other titles keep.
62
63
  shape) {
63
- const parsed = answerSchema.safeParse(jsonOf(text));
64
+ const parsed = answerSchema.safeParse(jsonBetween(text, "{", "}"));
64
65
  if (!parsed.success)
65
66
  return {
66
67
  ok: false,
@@ -239,17 +240,3 @@ function checkTags(raw) {
239
240
  return broke(`the tags come to ${String(length)} characters, over YouTube's ${String(tagsMaxCharacters)}`);
240
241
  return { ok: true, value: tags };
241
242
  }
242
- // The object in the answer, wherever the model put it: bare, in a ```json fence, or after a
243
- // sentence of its own.
244
- function jsonOf(text) {
245
- const start = text.indexOf("{");
246
- const end = text.lastIndexOf("}");
247
- if (start === -1 || end <= start)
248
- return undefined;
249
- try {
250
- return JSON.parse(text.slice(start, end + 1));
251
- }
252
- catch {
253
- return undefined;
254
- }
255
- }
@@ -4,7 +4,7 @@
4
4
  import { z } from "zod";
5
5
  import { defaultChannelId } from "../channels/model.js";
6
6
  import { channelById, projectChannelId } from "../channels/repo.js";
7
- import { readSetting, writeSetting } from "../settings/repo.js";
7
+ import { readJsonSetting, writeSetting } from "../settings/repo.js";
8
8
  import { chapterNotice, fitChapters } from "./chapters.js";
9
9
  import { composeDescription, descriptionFields, resolveFields, shownFields, splitDescription, } from "./edits.js";
10
10
  import { fillPlaceholders, mergeLinks } from "./placeholders.js";
@@ -36,18 +36,9 @@ export function writeDescriptionEdits(db, projectId, edits, updatedAt) {
36
36
  ON CONFLICT(project_id) DO UPDATE SET fields_json=excluded.fields_json,links_json=excluded.links_json,updated_at=excluded.updated_at`).run(projectId, JSON.stringify(edits.fields), JSON.stringify(edits.links), updatedAt);
37
37
  }
38
38
  export function readChannelLinks(db) {
39
- const value = readSetting(db, channelLinksKey);
40
- if (value === undefined)
41
- return [];
42
39
  // A hand-edited row that no longer parses reads as no links rather than breaking the page;
43
40
  // saving the list in Settings writes a good one again.
44
- try {
45
- const parsed = linksSchema.safeParse(JSON.parse(value));
46
- return parsed.success ? parsed.data : [];
47
- }
48
- catch {
49
- return [];
50
- }
41
+ return readJsonSetting(db, channelLinksKey, linksSchema, []);
51
42
  }
52
43
  export function writeChannelLinks(db, links) {
53
44
  writeSetting(db, channelLinksKey, JSON.stringify(links.map((link) => ({ name: link.name.trim(), url: link.url.trim() }))));
@@ -144,7 +144,8 @@ Slopify cannot wake an agent; the agent looks. Its work list is the `needs_atten
144
144
  | A paused run, or a run a restart **Stopped** | Continues it once nothing on the project still needs fixing. |
145
145
  | Topics a schedule holds for approval | Approves the ones that fit and turns down the rest. |
146
146
  | A video marked **Late · upload now** | Reports it. It cannot upload. |
147
- | A finishing touch the Studio extension failed (captions, end screen, pinned comment) | Reports it. Fixing it is the extension's or yours. |
147
+ | A finishing touch the Studio extension failed (captions, end screen, pinned comment), or a post it couldn't fill on another site | Reports it. Fixing it is the extension's or yours. |
148
+ | A post filled in another site's form, waiting for your **Post** (`post-due`) | Reports it. Posting is yours. |
148
149
 
149
150
  Projects that are **Kept as is** are not listed. Each entry says whether Slopify is already retrying or redoing it by itself (the agent leaves those alone and looks again later) and whether the agent can act on it at all.
150
151
 
@@ -0,0 +1,42 @@
1
+ # Assistant
2
+
3
+ The assistant is a chat inside Slopify. Ask it how something works, or ask it to do something on the screen you have open: "Which templates do I have?", "Why did this run stop?", "Remake the thumbnail of this project". It runs on your own **Claude Code** or **Codex** sign-in, so it uses your plan's allowance and is not added to a project's run cost.
4
+
5
+ **Where to find it:** **Assistant** in the left rail, under the search field. On a phone it is the sparkle button in the top bar. It opens beside the page without moving it.
6
+
7
+ ## What it can and can't do
8
+
9
+ - It uses only Slopify's own tools, the same ones an [agent](Agents-MCP) uses, with the same checks and refusals as the buttons in the page. It reads this guide to answer questions about Slopify.
10
+ - It has no web search, can't run commands and can't open files on your computer. None of your Claude Code or Codex settings, hooks, plugins, skills, memory or other MCP servers are loaded for it. If the CLI ever offers it a tool from outside Slopify, Slopify stops the answer at once and says so.
11
+ - It never answers an external review, exports or imports the Library, uploads a file from disk, empties the Trash or deletes old versions. Uploading to YouTube, backups, provider keys and updates stay yours.
12
+ - What it changes shows as **by assistant** in a project's history, in notifications and in **Settings → Connections → Agents (MCP)**.
13
+
14
+ ## It asks first
15
+
16
+ Anything that costs money (starting a run or a remake, retrying a step, generating topics, a cast picture or a voice sample), anything a button would confirm, anything that removes something, and anything that sends to or changes an address outside Slopify (where notifications go, a test notification) waits for you. The chat shows a card with what it wants to do, for which project, and what it costs: the estimate the remake preview or Play's review showed, or "Slopify can't price it here" when there is none.
17
+
18
+ | Press | What happens |
19
+ | --- | --- |
20
+ | **Allow** | The assistant does that one thing, with exactly what it asked for, and tells you what happened. An Allow is used once and only in that chat. |
21
+ | **Don't** | Nothing runs. The assistant sees your answer the next time you write. |
22
+
23
+ An ask you don't answer within 15 minutes expires: ask again, so it can check the cost again. The assistant can't press Allow for you; only a press in the page counts.
24
+
25
+ ## Using the chat
26
+
27
+ - **Send** (or Enter) sends your message; Shift+Enter starts a new line. The answer streams in as it is written, with a line for each tool it used.
28
+ - **Stop** ends the answer at once. Whatever it already did stays done. An answer that takes longer than 10 minutes is stopped for you.
29
+ - **New chat** starts over. Each chat keeps its last 200 messages; the assistant remembers the last 20 of them (up to about 24,000 characters) when it answers.
30
+ - **Runs on** picks the CLI: **Auto** takes Claude Code when it is installed and signed in, otherwise Codex.
31
+ - It knows the page you have open: the project, schedule, channel or template on it, the section, and the rows you selected in a list.
32
+
33
+ ## When it can't start
34
+
35
+ The panel says why and links to the fix:
36
+
37
+ | Message | Fix |
38
+ | --- | --- |
39
+ | Neither Claude Code nor Codex is ready | Install one and sign in, then check it in **Settings → Providers** ([AI CLIs](AI-CLIs)). |
40
+ | Claude Code (or Codex) is signed out | Run `claude auth login` (or `codex login`) in a terminal, then send your message again. |
41
+ | Codex is too old | Update Codex CLI to the version the message names. |
42
+ | The Docker version | The assistant isn't available in Docker: there the CLIs run on your computer through the host helper, which can't hand them Slopify's tools safely. Install Slopify with npm ([Install](Install)) to use it. |
@@ -47,6 +47,8 @@ A channel page has six tabs: **Brand**, **Cast**, **Templates**, **Schedules**,
47
47
 
48
48
  Mark voices in **Settings → Voices** and Image prompts in **Library → Prompts**. See [Publishing to YouTube](Publishing-to-YouTube).
49
49
 
50
+ **Posting.** The channel's accounts on other sites, used only for [posting](Posting-to-Social-Sites). **X subscription** (Free, Premium or Premium+) only changes which warnings a post gets: Free warns about text over 280 characters and videos over 2 min 20 s. **Account names** (such as `@yourname` on X, `in/your-name` on LinkedIn, `yourname.bsky.social` on Bluesky, `u/yourname` on Reddit) let the extension find a published post's link on your profile. They are never a sign-in. Saved with **Save channel**.
51
+
50
52
  **Series brief.** What the channel covers, its style and what makes a topic worth a video. Topic generation reads it for every schedule of this channel that has no brief of its own. It doesn't change how videos are written. Up to 10,000 characters. See [Schedules](Schedules#topics-that-find-themselves).
51
53
 
52
54
  **Language of new projects.** The language this channel's new projects are written and narrated in when Play or the template picks none. It applies even with the brand kit off, since it is not styling. Pick voices that speak it. Not set means English. Projects already made keep their language. See [Other languages](Other-Languages).