@gentbajko/slopify 3.25.9 → 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 (718) hide show
  1. package/dist/adapters/account-limits.js +30 -0
  2. package/dist/adapters/alignment/audio.js +1 -1
  3. package/dist/adapters/alignment/cache.js +4 -6
  4. package/dist/adapters/alignment/ctc.js +1 -2
  5. package/dist/adapters/alignment/lock.js +4 -3
  6. package/dist/adapters/alignment/multilingual.js +2 -4
  7. package/dist/adapters/alignment/prefetch.js +2 -1
  8. package/dist/adapters/alignment/runner.js +1 -1
  9. package/dist/adapters/alignment/worker.js +3 -2
  10. package/dist/adapters/explain.js +10 -12
  11. package/dist/adapters/ffmpeg.js +19 -1
  12. package/dist/adapters/host-cli/index.js +21 -4
  13. package/dist/adapters/host-cli/transport.js +2 -1
  14. package/dist/adapters/http-failure.js +53 -0
  15. package/dist/adapters/image/bytes.js +2 -1
  16. package/dist/adapters/image/codex.js +42 -9
  17. package/dist/adapters/image/fal.js +21 -53
  18. package/dist/adapters/image/google.js +33 -56
  19. package/dist/adapters/image/models.js +2 -2
  20. package/dist/adapters/image/openai.js +22 -48
  21. package/dist/adapters/image/reference.js +36 -0
  22. package/dist/adapters/image/replicate.js +21 -49
  23. package/dist/adapters/image/video.js +2 -1
  24. package/dist/adapters/interest/search-interest.js +113 -0
  25. package/dist/adapters/key-probes.js +54 -3
  26. package/dist/adapters/llm/antigravity-models.js +1 -1
  27. package/dist/adapters/llm/antigravity.js +11 -6
  28. package/dist/adapters/llm/assistant-cli.js +388 -0
  29. package/dist/adapters/llm/child-session.js +83 -0
  30. package/dist/adapters/llm/claude-code-models.js +52 -99
  31. package/dist/adapters/llm/claude-code.js +16 -4
  32. package/dist/adapters/llm/codex-limits.js +27 -68
  33. package/dist/adapters/llm/codex-models.js +1 -1
  34. package/dist/adapters/llm/codex-server-models.js +61 -109
  35. package/dist/adapters/llm/codex.js +12 -1
  36. package/dist/adapters/llm/document-workspace.js +3 -2
  37. package/dist/adapters/llm/image-workspace.js +3 -2
  38. package/dist/adapters/llm/openrouter.js +12 -48
  39. package/dist/adapters/llm/run-cli.js +9 -24
  40. package/dist/adapters/llm/sse-lines.js +5 -1
  41. package/dist/adapters/model-access.js +37 -0
  42. package/dist/adapters/tts/cartesia.js +15 -46
  43. package/dist/adapters/tts/elevenlabs.js +20 -52
  44. package/dist/adapters/tts/gemini.js +21 -41
  45. package/dist/adapters/tts/inworld-async.js +6 -2
  46. package/dist/adapters/tts/inworld.js +6 -2
  47. package/dist/adapters/tts/openai.js +20 -50
  48. package/dist/adapters/tts/system.js +2 -1
  49. package/dist/assets/models-lifecycle.yaml +6 -0
  50. package/dist/assistant-wiring.js +60 -0
  51. package/dist/autopilot-timers.js +59 -0
  52. package/dist/catalog/lifecycle.js +52 -0
  53. package/dist/catalog/propose.js +204 -0
  54. package/dist/catalog/registry.js +23 -14
  55. package/dist/catalog/schema.js +4 -1
  56. package/dist/catalog/store.js +39 -0
  57. package/dist/catalog/validate.js +5 -2
  58. package/dist/edge/autostart/native.js +3 -5
  59. package/dist/edge/autostart/prompt.js +2 -1
  60. package/dist/edge/autostart/service.js +3 -4
  61. package/dist/edge/cli-args.js +3 -4
  62. package/dist/edge/cli.js +2 -1
  63. package/dist/edge/docker-install/apply.js +11 -11
  64. package/dist/edge/docker-install/run.js +2 -1
  65. package/dist/edge/docker-install/state.js +4 -3
  66. package/dist/edge/docker-install/tree.js +2 -4
  67. package/dist/edge/docker-install/volume.js +3 -2
  68. package/dist/edge/docker.js +5 -4
  69. package/dist/edge/events/hub.js +5 -5
  70. package/dist/edge/http/actions.js +11 -32
  71. package/dist/edge/http/actor.js +5 -3
  72. package/dist/edge/http/app.js +86 -11
  73. package/dist/edge/http/assistant.js +129 -0
  74. package/dist/edge/http/audio-preview.js +4 -11
  75. package/dist/edge/http/auditions.js +1 -1
  76. package/dist/edge/http/autostart.js +5 -5
  77. package/dist/edge/http/backups.js +2 -4
  78. package/dist/edge/http/channel-memory.js +9 -22
  79. package/dist/edge/http/channels.js +14 -27
  80. package/dist/edge/http/checkpoints.js +39 -21
  81. package/dist/edge/http/design-templates.js +167 -0
  82. package/dist/edge/http/designs.js +178 -0
  83. package/dist/edge/http/diagnostics.js +5 -1
  84. package/dist/edge/http/document-themes.js +5 -14
  85. package/dist/edge/http/draft-files.js +1 -3
  86. package/dist/edge/http/draft-problem.js +5 -10
  87. package/dist/edge/http/drafts.js +3 -3
  88. package/dist/edge/http/entries.js +6 -9
  89. package/dist/edge/http/extension-auth.js +27 -0
  90. package/dist/edge/http/external-reviews.js +12 -11
  91. package/dist/edge/http/files.js +5 -11
  92. package/dist/edge/http/folder-location.js +1 -4
  93. package/dist/edge/http/fonts.js +0 -7
  94. package/dist/edge/http/home.js +47 -14
  95. package/dist/edge/http/host-cli.js +18 -8
  96. package/dist/edge/http/ids.js +8 -0
  97. package/dist/edge/http/keywords.js +35 -0
  98. package/dist/edge/http/model-checks.js +132 -0
  99. package/dist/edge/http/narration-peaks.js +5 -13
  100. package/dist/edge/http/onboarding.js +30 -26
  101. package/dist/edge/http/open-folder.js +5 -10
  102. package/dist/edge/http/outputs.js +7 -14
  103. package/dist/edge/http/patch-notes.js +6 -12
  104. package/dist/edge/http/planning.js +3 -7
  105. package/dist/edge/http/problem.js +2 -4
  106. package/dist/edge/http/project-create.js +2 -1
  107. package/dist/edge/http/project-templates.js +31 -19
  108. package/dist/edge/http/projects.js +24 -16
  109. package/dist/edge/http/prompts.js +9 -16
  110. package/dist/edge/http/pronunciations.js +1 -2
  111. package/dist/edge/http/providers.js +48 -34
  112. package/dist/edge/http/refusal.js +29 -0
  113. package/dist/edge/http/remote-access.js +40 -0
  114. package/dist/edge/http/remote-guard.js +71 -0
  115. package/dist/edge/http/request-guard.js +91 -0
  116. package/dist/edge/http/reviews.js +12 -25
  117. package/dist/edge/http/revision-files.js +6 -11
  118. package/dist/edge/http/revisions.js +14 -19
  119. package/dist/edge/http/run-cost.js +1 -2
  120. package/dist/edge/http/same-page.js +80 -0
  121. package/dist/edge/http/schedules.js +69 -40
  122. package/dist/edge/http/settings.js +27 -14
  123. package/dist/edge/http/social.js +283 -0
  124. package/dist/edge/http/staging.js +3 -5
  125. package/dist/edge/http/storage-files.js +5 -7
  126. package/dist/edge/http/storage.js +33 -25
  127. package/dist/edge/http/studio.js +94 -69
  128. package/dist/edge/http/style-preview.js +3 -6
  129. package/dist/edge/http/subtitles.js +3 -17
  130. package/dist/edge/http/template-studio.js +436 -0
  131. package/dist/edge/http/trash.js +11 -12
  132. package/dist/edge/http/tutorial.js +1 -2
  133. package/dist/edge/http/tutorials.js +5 -11
  134. package/dist/edge/http/update.js +7 -13
  135. package/dist/edge/http/waveform.js +3 -6
  136. package/dist/edge/http/youtube-edits.js +3 -4
  137. package/dist/edge/mcp/assistant.js +279 -0
  138. package/dist/edge/mcp/attribution.js +8 -2
  139. package/dist/edge/mcp/call-route.js +11 -2
  140. package/dist/edge/mcp/excluded-routes.js +29 -3
  141. package/dist/edge/mcp/http.js +11 -2
  142. package/dist/edge/mcp/instructions.js +6 -4
  143. package/dist/edge/mcp/library-tools.js +9 -5
  144. package/dist/edge/mcp/remake-tools.js +7 -4
  145. package/dist/edge/mcp/route-tool.js +35 -15
  146. package/dist/edge/mcp/server.js +13 -8
  147. package/dist/edge/mcp/tools-channels.js +11 -9
  148. package/dist/edge/mcp/tools-designs.js +165 -0
  149. package/dist/edge/mcp/tools-library.js +19 -7
  150. package/dist/edge/mcp/tools-planning.js +40 -21
  151. package/dist/edge/mcp/tools-projects.js +38 -11
  152. package/dist/edge/mcp/tools-publishing.js +9 -9
  153. package/dist/edge/mcp/tools-runs.js +23 -10
  154. package/dist/edge/mcp/tools-schedules.js +32 -8
  155. package/dist/edge/mcp/tools-setup.js +75 -11
  156. package/dist/edge/mcp/tools-social.js +47 -0
  157. package/dist/edge/mcp/tools-storage.js +11 -3
  158. package/dist/edge/mcp/tools-template-studio.js +166 -0
  159. package/dist/edge/mcp/tools.js +6 -0
  160. package/dist/edge/remote-access/cli.js +71 -0
  161. package/dist/edge/remote-access/model.js +32 -0
  162. package/dist/edge/remote-access/rules.js +207 -0
  163. package/dist/edge/remote-access/service.js +203 -0
  164. package/dist/edge/remote-access/store.js +44 -0
  165. package/dist/edge/remote-access/views.js +43 -0
  166. package/dist/extension/slopify-studio-chrome.zip +0 -0
  167. package/dist/extension/slopify-studio-firefox.zip +0 -0
  168. package/dist/host-cli/install.js +2 -2
  169. package/dist/host-cli/open-folder.js +2 -2
  170. package/dist/host-cli/paths.js +2 -4
  171. package/dist/host-cli/server.js +4 -4
  172. package/dist/host-cli/service.js +3 -3
  173. package/dist/host-cli/status.js +7 -7
  174. package/dist/kernel/db/migrations/0063-model-checks.sql +20 -0
  175. package/dist/kernel/db/migrations/0064-template-studio.sql +46 -0
  176. package/dist/kernel/db/migrations/0065-promise-and-assistant-reviews.sql +58 -0
  177. package/dist/kernel/db/migrations/0066-assistant.sql +44 -0
  178. package/dist/kernel/db/migrations/0067-autopilot.sql +6 -0
  179. package/dist/kernel/db/migrations/0068-topic-scores.sql +17 -0
  180. package/dist/kernel/db/migrations/0070-designs.sql +53 -0
  181. package/dist/kernel/db/migrations/0071-design-usage.sql +33 -0
  182. package/dist/kernel/db/migrations/0072-design-templates.sql +33 -0
  183. package/dist/kernel/db/migrations/0073-schedule-design-kind.sql +5 -0
  184. package/dist/kernel/db/migrations/0074-template-bakeoffs.sql +44 -0
  185. package/dist/kernel/db/migrations/0075-social-posts.sql +25 -0
  186. package/dist/kernel/db/migrations/0076-channel-social.sql +5 -0
  187. package/dist/kernel/db/migrations/0077-post-prompts.sql +17 -0
  188. package/dist/kernel/db/migrations/0078-uploaded-from-links.sql +7 -0
  189. package/dist/kernel/db/tx.js +2 -4
  190. package/dist/kernel/errors.js +27 -1
  191. package/dist/kernel/json-answer.js +15 -0
  192. package/dist/kernel/lock.js +4 -9
  193. package/dist/kernel/paths.js +8 -0
  194. package/dist/kernel/pipeline.js +12 -0
  195. package/dist/kernel/ports/assistant-cli.js +4 -0
  196. package/dist/kernel/ports/host-cli.js +3 -0
  197. package/dist/kernel/ports/interest.js +4 -0
  198. package/dist/kernel/ports/languages.js +0 -3
  199. package/dist/kernel/ports/model.js +2 -0
  200. package/dist/kernel/ports/settings-places.js +31 -0
  201. package/dist/kernel/ports/system-speech.js +31 -3
  202. package/dist/kernel/runner/attempt.js +14 -4
  203. package/dist/kernel/runner/audition.js +3 -1
  204. package/dist/kernel/runner/checkpoint-authority.js +2 -1
  205. package/dist/kernel/runner/index.js +8 -10
  206. package/dist/kernel/runner/model-access.js +40 -0
  207. package/dist/kernel/runner/model-check.js +153 -0
  208. package/dist/kernel/runner/providers.js +18 -12
  209. package/dist/kernel/runner/standalone.js +11 -7
  210. package/dist/main.js +338 -21
  211. package/dist/model-catalog.js +20 -0
  212. package/dist/model-checks.js +166 -0
  213. package/dist/patch-notes/4.0.0.md +51 -0
  214. package/dist/patch-notes/4.1.0.md +42 -0
  215. package/dist/patch-notes/index.json +12 -0
  216. package/dist/slices/admission/heads.js +2 -1
  217. package/dist/slices/admission/listing-facts.js +95 -0
  218. package/dist/slices/admission/repo.js +11 -7
  219. package/dist/slices/admission/rules.js +50 -15
  220. package/dist/slices/admission/schema.js +8 -0
  221. package/dist/slices/admission/stage-writes.js +0 -3
  222. package/dist/slices/admission/start.js +48 -31
  223. package/dist/slices/admission/state-words.js +34 -5
  224. package/dist/slices/admission/substitute.js +7 -4
  225. package/dist/slices/agents/attention-posts.js +31 -0
  226. package/dist/slices/agents/attention-templates.js +33 -0
  227. package/dist/slices/agents/attention.js +10 -1
  228. package/dist/slices/agents/connection.js +2 -6
  229. package/dist/slices/agents/model.js +6 -3
  230. package/dist/slices/agents/repo.js +15 -7
  231. package/dist/slices/article/continuation.js +0 -63
  232. package/dist/slices/article/plain.js +3 -2
  233. package/dist/slices/article/segments.js +2 -62
  234. package/dist/slices/assistant/access.js +1 -0
  235. package/dist/slices/assistant/asks.js +80 -0
  236. package/dist/slices/assistant/cost.js +54 -0
  237. package/dist/slices/assistant/gate.js +222 -0
  238. package/dist/slices/assistant/model.js +86 -0
  239. package/dist/slices/assistant/names.js +23 -0
  240. package/dist/slices/assistant/prompt.js +74 -0
  241. package/dist/slices/assistant/repo.js +74 -0
  242. package/dist/slices/assistant/tokens.js +18 -0
  243. package/dist/slices/assistant/turn.js +344 -0
  244. package/dist/slices/autopilot/buffer.js +76 -0
  245. package/dist/slices/autopilot/model.js +14 -0
  246. package/dist/slices/autopilot/notify.js +27 -0
  247. package/dist/slices/autopilot/repo.js +34 -0
  248. package/dist/slices/autopilot/signals.js +194 -0
  249. package/dist/slices/autopilot/warnings.js +85 -0
  250. package/dist/slices/autopilot/weekly.js +100 -0
  251. package/dist/slices/backups/files.js +2 -6
  252. package/dist/slices/backups/repo.js +3 -15
  253. package/dist/slices/backups/service.js +8 -7
  254. package/dist/slices/channels/cast-images.js +3 -2
  255. package/dist/slices/channels/model.js +0 -1
  256. package/dist/slices/channels/rebrand.js +0 -3
  257. package/dist/slices/channels/repo.js +4 -0
  258. package/dist/slices/channels/schema.js +7 -1
  259. package/dist/slices/channels/service.js +10 -0
  260. package/dist/slices/channels/videos.js +3 -15
  261. package/dist/slices/checkpoints/index.js +0 -1
  262. package/dist/slices/control/providers.js +2 -28
  263. package/dist/slices/designs/artwork.js +66 -0
  264. package/dist/slices/designs/budget.js +23 -0
  265. package/dist/slices/designs/calls.js +124 -0
  266. package/dist/slices/designs/check-context.js +38 -0
  267. package/dist/slices/designs/check-text.js +43 -0
  268. package/dist/slices/designs/fields.js +88 -0
  269. package/dist/slices/designs/frames.js +34 -0
  270. package/dist/slices/designs/gate.js +130 -0
  271. package/dist/slices/designs/layout.js +293 -0
  272. package/dist/slices/designs/model.js +35 -0
  273. package/dist/slices/designs/pdf-job.js +23 -0
  274. package/dist/slices/designs/pdf-pages.js +30 -0
  275. package/dist/slices/designs/pdf-runner.js +68 -0
  276. package/dist/slices/designs/pdf-worker.js +24 -0
  277. package/dist/slices/designs/picture-size.js +79 -0
  278. package/dist/slices/designs/pictures.js +55 -0
  279. package/dist/slices/designs/quote.js +24 -0
  280. package/dist/slices/designs/render-args.js +36 -0
  281. package/dist/slices/designs/render-input.js +59 -0
  282. package/dist/slices/designs/render.js +215 -0
  283. package/dist/slices/designs/repo.js +101 -0
  284. package/dist/slices/designs/requests.js +80 -0
  285. package/dist/slices/designs/runner.js +239 -0
  286. package/dist/slices/designs/sample-repo.js +91 -0
  287. package/dist/slices/designs/sample.js +223 -0
  288. package/dist/slices/designs/schema.js +87 -0
  289. package/dist/slices/designs/sections.js +44 -0
  290. package/dist/slices/designs/service.js +233 -0
  291. package/dist/slices/designs/steps-repo.js +102 -0
  292. package/dist/slices/designs/studio-kind.js +39 -0
  293. package/dist/slices/designs/template-checks.js +44 -0
  294. package/dist/slices/designs/template-make.js +118 -0
  295. package/dist/slices/designs/templates.js +218 -0
  296. package/dist/slices/document/fonts.js +2 -1
  297. package/dist/slices/document/model.js +7 -4
  298. package/dist/slices/document/render.js +2 -1
  299. package/dist/slices/document/theme-schema.js +1 -1
  300. package/dist/slices/document/theme.js +49 -3
  301. package/dist/slices/episodes/summarize.js +6 -2
  302. package/dist/slices/estimate/index.js +72 -28
  303. package/dist/slices/estimate/requests.js +4 -4
  304. package/dist/slices/fixes/rules.js +3 -1
  305. package/dist/slices/fonts/catalog.js +5 -3
  306. package/dist/slices/fonts/coverage.js +2 -1
  307. package/dist/slices/fonts/discovery.js +3 -2
  308. package/dist/slices/fonts/files.js +2 -6
  309. package/dist/slices/fonts/index.js +0 -1
  310. package/dist/slices/fonts/sfnt.js +45 -10
  311. package/dist/slices/images/appearance.js +8 -16
  312. package/dist/slices/images/scenes.js +102 -28
  313. package/dist/slices/keywords/digest.js +85 -0
  314. package/dist/slices/keywords/facts.js +92 -0
  315. package/dist/slices/keywords/fill.js +80 -0
  316. package/dist/slices/keywords/model.js +75 -0
  317. package/dist/slices/keywords/rules.js +187 -0
  318. package/dist/slices/keywords/schema.js +28 -0
  319. package/dist/slices/keywords/specs.js +260 -0
  320. package/dist/slices/keywords/values.js +74 -0
  321. package/dist/slices/library/lint.js +29 -12
  322. package/dist/slices/library/model.js +2 -0
  323. package/dist/slices/library/photorealistic.js +4 -12
  324. package/dist/slices/library/rename.js +9 -7
  325. package/dist/slices/library/save.js +14 -10
  326. package/dist/slices/library/slots.js +37 -11
  327. package/dist/slices/library/transfer.js +5 -4
  328. package/dist/slices/library/used-by.js +25 -10
  329. package/dist/slices/loudness/loudnorm.js +53 -12
  330. package/dist/slices/loudness/model.js +3 -2
  331. package/dist/slices/loudness/sentence-level.js +2 -1
  332. package/dist/slices/model-checks/cost.js +61 -0
  333. package/dist/slices/model-checks/listing.js +124 -0
  334. package/dist/slices/model-checks/model.js +20 -0
  335. package/dist/slices/model-checks/record.js +32 -0
  336. package/dist/slices/model-checks/repo.js +120 -0
  337. package/dist/slices/model-checks/rules.js +125 -0
  338. package/dist/slices/model-checks/run.js +143 -0
  339. package/dist/slices/model-checks/service.js +198 -0
  340. package/dist/slices/model-upkeep/model.js +1 -1
  341. package/dist/slices/model-upkeep/switch.js +72 -27
  342. package/dist/slices/model-upkeep/usage.js +39 -13
  343. package/dist/slices/narration/breaths.js +3 -1
  344. package/dist/slices/narration/clean-pieces.js +3 -1
  345. package/dist/slices/narration/concat.js +2 -1
  346. package/dist/slices/narration/pauses-model.js +3 -2
  347. package/dist/slices/narration/peaks.js +3 -4
  348. package/dist/slices/narration/pronunciation-chunks.js +4 -3
  349. package/dist/slices/narration/pronunciation.js +6 -4
  350. package/dist/slices/narration/steering.js +4 -3
  351. package/dist/slices/notifications/notifier.js +4 -3
  352. package/dist/slices/notifications/rules.js +5 -2
  353. package/dist/slices/notifications/settings.js +4 -12
  354. package/dist/slices/notifications/subject.js +4 -3
  355. package/dist/slices/onboarding/copy.js +21 -3
  356. package/dist/slices/onboarding/first-run.js +16 -40
  357. package/dist/slices/onboarding/full-video.js +3 -2
  358. package/dist/slices/onboarding/install.js +6 -4
  359. package/dist/slices/onboarding/model.js +26 -37
  360. package/dist/slices/onboarding/packs.js +22 -22
  361. package/dist/slices/onboarding/preferred-model.js +14 -0
  362. package/dist/slices/onboarding/quick-short.js +105 -65
  363. package/dist/slices/onboarding/sample.js +4 -2
  364. package/dist/slices/onboarding/state.js +9 -11
  365. package/dist/slices/onboarding/template.js +8 -6
  366. package/dist/slices/outputs/add.js +3 -2
  367. package/dist/slices/outputs/service.js +2 -2
  368. package/dist/slices/patch-notes/library.js +4 -3
  369. package/dist/slices/play-drafts/convert.js +4 -0
  370. package/dist/slices/play-drafts/readiness.js +3 -3
  371. package/dist/slices/play-drafts/review-inputs.js +9 -3
  372. package/dist/slices/play-drafts/review.js +5 -4
  373. package/dist/slices/play-drafts/schema.js +4 -0
  374. package/dist/slices/play-drafts/service.js +17 -7
  375. package/dist/slices/project-templates/from-project.js +9 -2
  376. package/dist/slices/project-templates/next-chapter.js +2 -1
  377. package/dist/slices/project-templates/provider-defaults.js +22 -0
  378. package/dist/slices/project-templates/repo.js +6 -1
  379. package/dist/slices/project-templates/setup-sections.js +26 -0
  380. package/dist/slices/project-templates/setup.js +14 -1
  381. package/dist/slices/project-templates/stand-ins.js +22 -0
  382. package/dist/slices/rebuild/activity.js +2 -0
  383. package/dist/slices/rebuild/admission-repo.js +3 -2
  384. package/dist/slices/rebuild/narration-history.js +4 -5
  385. package/dist/slices/rebuild/narration-retry.js +2 -1
  386. package/dist/slices/rebuild/preview-details.js +32 -14
  387. package/dist/slices/rebuild/preview-plan.js +3 -8
  388. package/dist/slices/rebuild/recipe-build.js +8 -3
  389. package/dist/slices/rebuild/recipe-describe.js +3 -3
  390. package/dist/slices/rebuild/recipe-digest.js +58 -0
  391. package/dist/slices/rebuild/recipe-input-schema.js +10 -0
  392. package/dist/slices/rebuild/recipe-keywords.js +71 -0
  393. package/dist/slices/rebuild/recipe-legacy.js +5 -1
  394. package/dist/slices/rebuild/recipe-model.js +4 -0
  395. package/dist/slices/rebuild/recipe-narration-text.js +2 -1
  396. package/dist/slices/rebuild/recipe-preparation.js +4 -4
  397. package/dist/slices/rebuild/recipe-reference.js +8 -4
  398. package/dist/slices/rebuild/recipe-reviews.js +27 -7
  399. package/dist/slices/rebuild/recipe-save.js +8 -5
  400. package/dist/slices/rebuild/recipe-scenes.js +40 -11
  401. package/dist/slices/rebuild/recipe-shorts.js +8 -8
  402. package/dist/slices/rebuild/recipe-social.js +46 -0
  403. package/dist/slices/rebuild/recipe-text.js +64 -20
  404. package/dist/slices/rebuild/recipe-validation.js +102 -31
  405. package/dist/slices/rebuild/recipe-visual.js +55 -16
  406. package/dist/slices/rebuild/recipe-voices.js +3 -2
  407. package/dist/slices/rebuild/recipe-work.js +19 -7
  408. package/dist/slices/rebuild/recipe-youtube.js +4 -3
  409. package/dist/slices/rebuild/recovery-selection.js +3 -1
  410. package/dist/slices/rebuild/recovery.js +9 -8
  411. package/dist/slices/rebuild/remake-outdated.js +5 -5
  412. package/dist/slices/rebuild/repo.js +4 -3
  413. package/dist/slices/rebuild/review-promise.js +25 -0
  414. package/dist/slices/rebuild/review-redo.js +3 -2
  415. package/dist/slices/rebuild/runtime-admission.js +4 -3
  416. package/dist/slices/rebuild/runtime-agent-review.js +11 -5
  417. package/dist/slices/rebuild/runtime-animate.js +10 -9
  418. package/dist/slices/rebuild/runtime-article.js +7 -5
  419. package/dist/slices/rebuild/runtime-checkpoints.js +5 -4
  420. package/dist/slices/rebuild/runtime-document.js +11 -13
  421. package/dist/slices/rebuild/runtime-export-bed.js +8 -12
  422. package/dist/slices/rebuild/runtime-export-edit.js +8 -16
  423. package/dist/slices/rebuild/runtime-export-inputs.js +12 -11
  424. package/dist/slices/rebuild/runtime-export-short.js +9 -5
  425. package/dist/slices/rebuild/runtime-export.js +38 -15
  426. package/dist/slices/rebuild/runtime-figure-card.js +2 -1
  427. package/dist/slices/rebuild/runtime-image.js +8 -12
  428. package/dist/slices/rebuild/runtime-instructions.js +2 -0
  429. package/dist/slices/rebuild/runtime-keywords.js +59 -0
  430. package/dist/slices/rebuild/runtime-lines.js +2 -11
  431. package/dist/slices/rebuild/runtime-local.js +17 -22
  432. package/dist/slices/rebuild/runtime-narration-publication.js +2 -5
  433. package/dist/slices/rebuild/runtime-narration-reuse.js +9 -13
  434. package/dist/slices/rebuild/runtime-narration-text.js +13 -17
  435. package/dist/slices/rebuild/runtime-pictures.js +2 -4
  436. package/dist/slices/rebuild/runtime-piece-label.js +10 -6
  437. package/dist/slices/rebuild/runtime-plan.js +31 -5
  438. package/dist/slices/rebuild/runtime-provider.js +67 -25
  439. package/dist/slices/rebuild/runtime-publication.js +10 -9
  440. package/dist/slices/rebuild/runtime-review.js +39 -33
  441. package/dist/slices/rebuild/runtime-run.js +4 -1
  442. package/dist/slices/rebuild/runtime-shorts.js +31 -50
  443. package/dist/slices/rebuild/runtime-store.js +20 -10
  444. package/dist/slices/rebuild/runtime-subtitles.js +28 -25
  445. package/dist/slices/rebuild/runtime-voices.js +10 -16
  446. package/dist/slices/rebuild/runtime-youtube.js +14 -31
  447. package/dist/slices/rebuild/service-readiness.js +11 -3
  448. package/dist/slices/rebuild/transition-repo.js +6 -5
  449. package/dist/slices/rebuild/work-records.js +6 -0
  450. package/dist/slices/reruns/cascade.js +1 -2
  451. package/dist/slices/reruns/index.js +6 -9
  452. package/dist/slices/research/synthesis.js +3 -2
  453. package/dist/slices/reviews/external-model.js +4 -1
  454. package/dist/slices/reviews/external-repo.js +2 -1
  455. package/dist/slices/reviews/external-rows.js +2 -3
  456. package/dist/slices/reviews/holds.js +5 -4
  457. package/dist/slices/reviews/model.js +32 -11
  458. package/dist/slices/reviews/repo.js +2 -1
  459. package/dist/slices/reviews/rules.js +4 -3
  460. package/dist/slices/reviews/verdict.js +2 -1
  461. package/dist/slices/reviews/view.js +5 -2
  462. package/dist/slices/revisions/downloads.js +4 -5
  463. package/dist/slices/revisions/image-plan.js +1 -1
  464. package/dist/slices/revisions/mutation-assets.js +9 -5
  465. package/dist/slices/revisions/mutation-cues.js +2 -5
  466. package/dist/slices/revisions/mutations.js +3 -3
  467. package/dist/slices/revisions/repo.js +3 -1
  468. package/dist/slices/revisions/schema.js +1 -0
  469. package/dist/slices/revisions/view.js +20 -0
  470. package/dist/slices/run-cost/limits.js +5 -2
  471. package/dist/slices/run-cost/meter.js +12 -5
  472. package/dist/slices/run-cost/panel.js +8 -74
  473. package/dist/slices/run-cost/plan-readings.js +127 -0
  474. package/dist/slices/run-cost/plan-use.js +169 -0
  475. package/dist/slices/run-cost/pricing.js +3 -3
  476. package/dist/slices/run-cost/week.js +32 -4
  477. package/dist/slices/schedules/agenda.js +18 -7
  478. package/dist/slices/schedules/by-date.js +42 -0
  479. package/dist/slices/schedules/calendar.js +5 -0
  480. package/dist/slices/schedules/design-agenda.js +36 -0
  481. package/dist/slices/schedules/design-check.js +41 -0
  482. package/dist/slices/schedules/design-run.js +126 -0
  483. package/dist/slices/schedules/next-run.js +62 -0
  484. package/dist/slices/schedules/prepare.js +18 -2
  485. package/dist/slices/schedules/repo.js +30 -21
  486. package/dist/slices/schedules/rescore.js +35 -0
  487. package/dist/slices/schedules/scheduler.js +13 -3
  488. package/dist/slices/schedules/schema.js +57 -3
  489. package/dist/slices/schedules/score.js +107 -0
  490. package/dist/slices/schedules/service.js +57 -24
  491. package/dist/slices/schedules/topic-list.js +15 -0
  492. package/dist/slices/schedules/topic-scores.js +104 -0
  493. package/dist/slices/schedules/topics.js +53 -41
  494. package/dist/slices/settings/cli-paths.js +4 -6
  495. package/dist/slices/settings/first-run.js +8 -43
  496. package/dist/slices/settings/health.js +30 -15
  497. package/dist/slices/settings/key-test.js +1 -1
  498. package/dist/slices/settings/model-reach.js +3 -17
  499. package/dist/slices/settings/model.js +14 -2
  500. package/dist/slices/settings/playback.js +34 -23
  501. package/dist/slices/settings/probe-read.js +16 -0
  502. package/dist/slices/settings/repo.js +33 -0
  503. package/dist/slices/settings/tutorial.js +2 -2
  504. package/dist/slices/settings/voices.js +4 -12
  505. package/dist/slices/shorts/keep.js +2 -1
  506. package/dist/slices/shorts/pick.js +6 -18
  507. package/dist/slices/shorts/prompts.js +4 -14
  508. package/dist/slices/shorts/render.js +6 -2
  509. package/dist/slices/social/channel.js +44 -0
  510. package/dist/slices/social/defaults.js +17 -0
  511. package/dist/slices/social/due.js +36 -0
  512. package/dist/slices/social/extension.js +18 -0
  513. package/dist/slices/social/fit.js +115 -0
  514. package/dist/slices/social/limits.js +186 -0
  515. package/dist/slices/social/links.js +21 -0
  516. package/dist/slices/social/media.js +132 -0
  517. package/dist/slices/social/model.js +75 -0
  518. package/dist/slices/social/notice.js +19 -0
  519. package/dist/slices/social/pack.js +176 -0
  520. package/dist/slices/social/post-prompt.js +56 -0
  521. package/dist/slices/social/queue.js +31 -0
  522. package/dist/slices/social/repo.js +133 -0
  523. package/dist/slices/social/reshape.js +97 -0
  524. package/dist/slices/social/rules.js +5 -0
  525. package/dist/slices/social/schema.js +70 -0
  526. package/dist/slices/social/serve.js +51 -0
  527. package/dist/slices/social/service.js +129 -0
  528. package/dist/slices/social/settings.js +21 -0
  529. package/dist/slices/social/sources.js +114 -0
  530. package/dist/slices/social/state.js +110 -0
  531. package/dist/slices/social/text.js +149 -0
  532. package/dist/slices/storage/arrange.js +3 -1
  533. package/dist/slices/storage/assets.js +8 -10
  534. package/dist/slices/storage/backup-designs.js +74 -0
  535. package/dist/slices/storage/backup-export.js +8 -3
  536. package/dist/slices/storage/backup-format.js +10 -0
  537. package/dist/slices/storage/backup-import.js +22 -4
  538. package/dist/slices/storage/delete-project.js +6 -5
  539. package/dist/slices/storage/files-location.js +6 -8
  540. package/dist/slices/storage/layout.js +13 -2
  541. package/dist/slices/storage/model.js +3 -0
  542. package/dist/slices/storage/portable.js +17 -12
  543. package/dist/slices/storage/schema.js +2 -0
  544. package/dist/slices/storage/staging-refs.js +2 -1
  545. package/dist/slices/storage/staging.js +6 -9
  546. package/dist/slices/storage/trim.js +26 -9
  547. package/dist/slices/studio/calendar.js +7 -2
  548. package/dist/slices/studio/disclosure.js +4 -4
  549. package/dist/slices/studio/model.js +1 -1
  550. package/dist/slices/studio/pack.js +9 -8
  551. package/dist/slices/studio/pick.js +2 -11
  552. package/dist/slices/studio/plan.js +7 -16
  553. package/dist/slices/studio/queue.js +3 -14
  554. package/dist/slices/studio/releases.js +26 -0
  555. package/dist/slices/studio/settings.js +20 -57
  556. package/dist/slices/studio/videos.js +35 -4
  557. package/dist/slices/style-preview/render.js +37 -16
  558. package/dist/slices/style-preview/schema.js +14 -1
  559. package/dist/slices/style-preview/service.js +2 -1
  560. package/dist/slices/style-preview/settings.js +33 -5
  561. package/dist/slices/subtitles/captions.js +1 -1
  562. package/dist/slices/telemetry/collector-client.js +2 -4
  563. package/dist/slices/telemetry/flush.js +2 -4
  564. package/dist/slices/telemetry/model.js +0 -7
  565. package/dist/slices/telemetry/record.js +10 -3
  566. package/dist/slices/template-studio/anatomy-view.js +161 -0
  567. package/dist/slices/template-studio/anatomy.js +73 -0
  568. package/dist/slices/template-studio/bakeoff-make.js +225 -0
  569. package/dist/slices/template-studio/bakeoff-repo.js +83 -0
  570. package/dist/slices/template-studio/bakeoff.js +333 -0
  571. package/dist/slices/template-studio/budget.js +106 -0
  572. package/dist/slices/template-studio/cards.js +78 -0
  573. package/dist/slices/template-studio/checks.js +322 -0
  574. package/dist/slices/template-studio/document-sample.js +56 -0
  575. package/dist/slices/template-studio/edit.js +15 -0
  576. package/dist/slices/template-studio/for-all.js +161 -0
  577. package/dist/slices/template-studio/free-video.js +31 -0
  578. package/dist/slices/template-studio/free.js +176 -0
  579. package/dist/slices/template-studio/gate.js +40 -0
  580. package/dist/slices/template-studio/hidden.js +20 -0
  581. package/dist/slices/template-studio/jobs.js +79 -0
  582. package/dist/slices/template-studio/loudness.js +111 -0
  583. package/dist/slices/template-studio/make.js +40 -0
  584. package/dist/slices/template-studio/model.js +58 -0
  585. package/dist/slices/template-studio/pack-import.js +253 -0
  586. package/dist/slices/template-studio/pack-samples.js +60 -0
  587. package/dist/slices/template-studio/pack.js +207 -0
  588. package/dist/slices/template-studio/paid-view.js +66 -0
  589. package/dist/slices/template-studio/paid.js +355 -0
  590. package/dist/slices/template-studio/quote.js +155 -0
  591. package/dist/slices/template-studio/ready.js +104 -0
  592. package/dist/slices/template-studio/repo.js +134 -0
  593. package/dist/slices/template-studio/reviews-path.js +58 -0
  594. package/dist/slices/template-studio/runner.js +84 -0
  595. package/dist/slices/template-studio/save.js +236 -0
  596. package/dist/slices/template-studio/say-it-as.js +248 -0
  597. package/dist/slices/template-studio/schedule-gate.js +34 -0
  598. package/dist/slices/template-studio/schedule-price.js +32 -0
  599. package/dist/slices/template-studio/schedule-sample.js +91 -0
  600. package/dist/slices/template-studio/schema.js +243 -0
  601. package/dist/slices/template-studio/scope.js +124 -0
  602. package/dist/slices/template-studio/sections.js +176 -0
  603. package/dist/slices/template-studio/service.js +175 -0
  604. package/dist/slices/template-studio/status.js +191 -0
  605. package/dist/slices/template-studio/topic.js +223 -0
  606. package/dist/slices/thumbnail/by-llm.js +1 -1
  607. package/dist/slices/trash/designs.js +76 -0
  608. package/dist/slices/trash/model.js +13 -4
  609. package/dist/slices/trash/service.js +52 -2
  610. package/dist/slices/tutorials/library.js +3 -2
  611. package/dist/slices/uploads/repo.js +37 -1
  612. package/dist/slices/uploads/rules.js +3 -2
  613. package/dist/slices/video/ass-text.js +17 -0
  614. package/dist/slices/video/audio-export.js +2 -64
  615. package/dist/slices/video/edit-list.js +2 -2
  616. package/dist/slices/video/edit-settings.js +1 -1
  617. package/dist/slices/video/encoding.js +169 -0
  618. package/dist/slices/video/ffmpeg.js +23 -21
  619. package/dist/slices/video/figure-card.js +1 -15
  620. package/dist/slices/video/plan.js +2 -1
  621. package/dist/slices/video/slideshow.js +43 -18
  622. package/dist/slices/voices/attribution.js +3 -2
  623. package/dist/slices/voices/model.js +9 -8
  624. package/dist/slices/voices/palette.js +0 -3
  625. package/dist/slices/voices/script.js +2 -1
  626. package/dist/slices/youtube/answer.js +4 -16
  627. package/dist/slices/youtube/edits-repo.js +3 -12
  628. package/dist/tutorials/AI-CLIs.md +25 -25
  629. package/dist/tutorials/Agents-MCP.md +34 -33
  630. package/dist/tutorials/Assistant.md +42 -0
  631. package/dist/tutorials/Backups.md +7 -7
  632. package/dist/tutorials/Calendar.md +17 -25
  633. package/dist/tutorials/Cast-Library.md +19 -19
  634. package/dist/tutorials/Channels.md +42 -35
  635. package/dist/tutorials/Costs-and-Run-Cost.md +38 -40
  636. package/dist/tutorials/Create-Article.md +114 -0
  637. package/dist/tutorials/Create-Extras.md +112 -0
  638. package/dist/tutorials/Create-Images.md +165 -0
  639. package/dist/tutorials/Create-Narration.md +178 -0
  640. package/dist/tutorials/Create-Overview.md +244 -0
  641. package/dist/tutorials/Create-Video-and-Style.md +144 -0
  642. package/dist/tutorials/Docker.md +3 -3
  643. package/dist/tutorials/Document-Themes.md +32 -32
  644. package/dist/tutorials/Editing-a-Project.md +94 -94
  645. package/dist/tutorials/FAQ.md +18 -18
  646. package/dist/tutorials/First-Launch-and-Welcome.md +50 -64
  647. package/dist/tutorials/Home-and-Projects.md +115 -71
  648. package/dist/tutorials/Home.md +33 -33
  649. package/dist/tutorials/Install.md +14 -13
  650. package/dist/tutorials/Intros-and-Outros.md +33 -34
  651. package/dist/tutorials/Keyboard-Shortcuts-and-Command-Palette.md +62 -58
  652. package/dist/tutorials/Keywords-Slopify-Fills.md +87 -0
  653. package/dist/tutorials/Models.md +53 -21
  654. package/dist/tutorials/Multiple-Voices.md +41 -40
  655. package/dist/tutorials/Narration-Aliases-and-Glossary.md +14 -14
  656. package/dist/tutorials/Notifications.md +27 -17
  657. package/dist/tutorials/Other-Languages.md +15 -15
  658. package/dist/tutorials/PDF-Documents.md +38 -29
  659. package/dist/tutorials/Posting-to-Social-Sites.md +121 -0
  660. package/dist/tutorials/Project-Page.md +118 -120
  661. package/dist/tutorials/Prompts-and-Templates.md +84 -0
  662. package/dist/tutorials/Prompts.md +48 -44
  663. package/dist/tutorials/Providers-and-Keys.md +105 -98
  664. package/dist/tutorials/Publishing-to-YouTube.md +31 -31
  665. package/dist/tutorials/Recovery-and-Retries.md +31 -31
  666. package/dist/tutorials/Remote-Access.md +63 -0
  667. package/dist/tutorials/Reviews-and-Checkpoints.md +98 -90
  668. package/dist/tutorials/Schedules.md +46 -27
  669. package/dist/tutorials/Settings-Reference.md +194 -174
  670. package/dist/tutorials/Shorts.md +34 -33
  671. package/dist/tutorials/Start-at-Login.md +3 -3
  672. package/dist/tutorials/Studio-Extension.md +22 -24
  673. package/dist/tutorials/Template-Studio.md +128 -0
  674. package/dist/tutorials/Templates.md +51 -77
  675. package/dist/tutorials/Trash.md +4 -4
  676. package/dist/tutorials/Troubleshooting.md +44 -44
  677. package/dist/tutorials/Uninstalling-and-Moving.md +5 -5
  678. package/dist/tutorials/Updating-and-Patch-Notes.md +18 -18
  679. package/dist/tutorials/Video-Editing.md +34 -34
  680. package/dist/tutorials/Where-Your-Files-Live.md +10 -10
  681. package/dist/tutorials/YouTube-Description.md +26 -26
  682. package/dist/tutorials/Your-First-Short.md +35 -35
  683. package/dist/tutorials/_Sidebar.md +19 -14
  684. package/dist/updater/work-in-progress.js +42 -12
  685. package/dist/web/assets/index-BNftKQrA.css +1 -0
  686. package/dist/web/assets/index-Cpa58woP.js +459 -0
  687. package/dist/web/assets/pdf-BKh8f7R6.js +56 -0
  688. package/dist/web/assets/pdf.worker.min-BmVo14Nb.mjs +28 -0
  689. package/dist/web/assets/pdf.worker.min-D7i8o3wR.js +1 -0
  690. package/dist/web/index.html +2 -2
  691. package/package.json +6 -2
  692. package/dist/slices/article/provided-entries.js +0 -66
  693. package/dist/slices/article/run.js +0 -145
  694. package/dist/slices/images/run.js +0 -234
  695. package/dist/slices/narration/legacy-plan.js +0 -58
  696. package/dist/slices/narration/run.js +0 -270
  697. package/dist/slices/rebuild/runtime-actions.js +0 -90
  698. package/dist/slices/research/run.js +0 -224
  699. package/dist/slices/subtitles/prepare.js +0 -194
  700. package/dist/slices/subtitles/transcript.js +0 -34
  701. package/dist/slices/thumbnail/run.js +0 -219
  702. package/dist/slices/video/audio-inputs.js +0 -33
  703. package/dist/slices/video/reuse-audio.js +0 -62
  704. package/dist/slices/video/run.js +0 -78
  705. package/dist/slices/video/write-export.js +0 -158
  706. package/dist/slices/video/write-subtitles.js +0 -59
  707. package/dist/tutorials/Library-Overview.md +0 -76
  708. package/dist/tutorials/Play-Images.md +0 -161
  709. package/dist/tutorials/Play-Narration.md +0 -167
  710. package/dist/tutorials/Play-Outputs.md +0 -109
  711. package/dist/tutorials/Play-Overview.md +0 -227
  712. package/dist/tutorials/Play-Title-and-Article.md +0 -136
  713. package/dist/tutorials/Play-Video-and-Style.md +0 -143
  714. package/dist/web/assets/index-BadsoORk.css +0 -1
  715. package/dist/web/assets/index-D8LqNsCl.js +0 -326
  716. package/dist/web/assets/pdf-T7MCwjIv.js +0 -54
  717. package/dist/web/assets/pdf.worker.min-Dswkl-cV.mjs +0 -29
  718. package/dist/web/assets/pdf.worker.min-zC6nfbxG.js +0 -1
@@ -10,17 +10,21 @@ import { plainText } from "../article/plain.js";
10
10
  import { segmentMessages } from "../article/segments.js";
11
11
  import { splitEndMatter } from "../article/split.js";
12
12
  import { withEarlierEpisodes } from "../episodes/related.js";
13
+ import { withoutSlopifyScene } from "../images/scenes.js";
13
14
  import { parsePronunciationGlossary, pronunciationRulesOf, } from "../narration/pronunciation.js";
14
15
  import { withSharedGlossary } from "../narration/shared-glossary.js";
15
16
  import { researchDocuments } from "../research/documents.js";
16
17
  import { plannerMessages, subAgentMessages } from "../research/planner.js";
17
18
  import { synthesisMessages } from "../research/synthesis.js";
19
+ import { sampleArticleInstruction, sampleNarrationText } from "../template-studio/scope.js";
18
20
  import { thumbnailMessages } from "../thumbnail/by-llm.js";
19
21
  import { adaptationMessages } from "../voices/adaptation.js";
20
22
  import { attributionMessages } from "../voices/attribution.js";
21
23
  import { usesVoices } from "../voices/model.js";
22
24
  import { scriptMessages } from "../voices/script.js";
23
25
  import { describedText, describeFuture, describing } from "./recipe-describe.js";
26
+ import { keywordDigest } from "./recipe-digest.js";
27
+ import { articleMarkdownOf, chaptersProducer, filledPrompt, } from "./recipe-keywords.js";
24
28
  import { recipe, selectedReference, } from "./recipe-model.js";
25
29
  export function renderedPrompt(context, key) {
26
30
  const raw = context.content.promptTemplates[key];
@@ -28,6 +32,10 @@ export function renderedPrompt(context, key) {
28
32
  ? (context.config.rendered[key] ?? "")
29
33
  : render(raw, context.config.values);
30
34
  }
35
+ // The prompt rendered with the person's values, then with the keywords Slopify fills.
36
+ export function promptOf(context, key) {
37
+ return filledPrompt(context, key, renderedPrompt(context, key));
38
+ }
31
39
  export function llmInput(context, messages, webSearch = false, documents) {
32
40
  const choice = context.config.llm;
33
41
  const model = context.catalogue?.llm.find((row) => row.provider === choice?.provider &&
@@ -66,7 +74,7 @@ export function selectedText(context, key, field) {
66
74
  export function textRecipes(context) {
67
75
  const { config, content, resolved } = context;
68
76
  const recipes = [];
69
- const brief = { articlePrompt: renderedPrompt(context, "article"), values: config.values };
77
+ const brief = { articlePrompt: promptOf(context, "article").text, values: config.values };
70
78
  let research;
71
79
  if (config.sources.research === "generate") {
72
80
  const planner = recipe(context, "research:planner", "research", llmInput(context, plannerMessages(brief)));
@@ -113,7 +121,9 @@ export function textRecipes(context) {
113
121
  // The related earlier episodes go under the prompt only when the run carries them, so a
114
122
  // project made without them keeps its fingerprint. Either is written in the project's
115
123
  // language (`withLanguage`; English adds nothing).
116
- const writtenPrompt = withEarlierEpisodes(brief.articlePrompt, config.earlierEpisodes);
124
+ // A template studio sample asks for its outline and first chapter only (`config.sample`);
125
+ // nothing is added for any other project, so its fingerprint stays as it was.
126
+ const writtenPrompt = withSampleScope(withEarlierEpisodes(brief.articlePrompt, config.earlierEpisodes), config.sample);
117
127
  const messages = withLanguage(voices !== undefined && writesScript
118
128
  ? scriptMessages(voices.format, voices.speakers, writtenPrompt, articleNotes)
119
129
  : articleMessages({
@@ -157,6 +167,18 @@ export function textRecipes(context) {
157
167
  const pictureText = content.picturesArticle === undefined
158
168
  ? articleText
159
169
  : plainText(splitEndMatter(content.picturesArticle).body);
170
+ // What the keywords in the later prompts are filled from: the article's chapters and, when a
171
+ // prompt uses one, the keyword digest written from it.
172
+ const digest = keywordDigest(context, { pictureText, article });
173
+ if (digest !== undefined)
174
+ recipes.push(digest.recipe);
175
+ const keywords = {
176
+ producers: [
177
+ chaptersProducer(articleMarkdown, article),
178
+ ...(digest === undefined ? [] : [digest.producer]),
179
+ ],
180
+ };
181
+ const keyed = { ...context, keywords };
160
182
  let script;
161
183
  if (voices !== undefined && scriptCheck !== undefined) {
162
184
  if (writesScript)
@@ -189,7 +211,10 @@ export function textRecipes(context) {
189
211
  };
190
212
  }
191
213
  }
192
- let narrationText = articleText;
214
+ // A sample says only the narration's opening (`template-studio/scope.ts`).
215
+ let narrationText = config.sample === undefined || articleText === null
216
+ ? articleText
217
+ : sampleNarrationText(articleText);
193
218
  const descriptions = [];
194
219
  const cards = [];
195
220
  if (describing(context) && voices === undefined) {
@@ -201,7 +226,11 @@ export function textRecipes(context) {
201
226
  const described = describedText(context, endMatter.body, [article.key]);
202
227
  descriptions.push(...described.recipes);
203
228
  cards.push(...described.cards);
204
- narrationText = described.spoken;
229
+ // A sample says only the opening of the described narration too.
230
+ narrationText =
231
+ config.sample === undefined || described.spoken === null
232
+ ? described.spoken
233
+ : sampleNarrationText(described.spoken);
205
234
  }
206
235
  }
207
236
  const glossary = glossaryOf(context, endMatter);
@@ -210,11 +239,20 @@ export function textRecipes(context) {
210
239
  const choice = config[category];
211
240
  if (choice === undefined || config.sources.audio !== "generate")
212
241
  continue;
213
- const prompt = renderedPrompt(context, category);
242
+ const filled = promptOf(keyed, category);
243
+ const prompt = filled.text;
244
+ const waiting = filled.waits.length > 0;
214
245
  const key = `entry:${category}:text`;
215
246
  const input = choice.mode === "text"
216
- ? { kind: "local", version: 1, operation: "entry-text", values: prompt }
217
- : pictureText === null
247
+ ? waiting
248
+ ? {
249
+ kind: "deferred",
250
+ version: 1,
251
+ operation: key,
252
+ template: [prompt, ...filled.waits.map((one) => one.fingerprint)],
253
+ }
254
+ : { kind: "local", version: 1, operation: "entry-text", values: prompt }
255
+ : pictureText === null || waiting
218
256
  ? {
219
257
  kind: "deferred",
220
258
  version: 1,
@@ -222,36 +260,43 @@ export function textRecipes(context) {
222
260
  template: [
223
261
  llmInputFingerprint(context, withLanguage(segmentMessages(prompt, config, ""), config.language)),
224
262
  article.fingerprint,
263
+ ...filled.waits.map((one) => one.fingerprint),
225
264
  ],
226
265
  }
227
266
  : llmInput(context, withLanguage(segmentMessages(prompt, config, pictureText), config.language));
228
- const value = recipe(context, key, "article", input, choice.mode === "llm" ? [article.key] : []);
267
+ const value = recipe(context, key, "article", input, choice.mode === "llm" ? [...new Set([article.key, ...filled.dependsOn])] : filled.dependsOn);
229
268
  recipes.push(value);
230
269
  entries[category] = {
231
270
  recipe: value,
232
- text: choice.mode === "text" ? prompt : matchingText(context, value, "text"),
271
+ text: choice.mode === "text" ? (waiting ? null : prompt) : matchingText(context, value, "text"),
233
272
  };
234
273
  }
235
274
  if (config.sources.thumbnail === "prompt_by_llm") {
236
- const prompt = renderedPrompt(context, "thumbnailPrompt");
275
+ const filled = promptOf(keyed, "thumbnailPrompt");
237
276
  const messages = thumbnailMessages({
238
- instruction: prompt,
277
+ // The AI writes the picture itself, so a `Slopify_` scene keyword has nothing to fill.
278
+ instruction: withoutSlopifyScene(filled.text),
239
279
  title: subjectOf(config),
240
280
  values: config.values,
241
281
  format: config.format,
242
282
  article: pictureText ?? "",
243
283
  });
244
- recipes.push(recipe(context, "thumbnail:prompt", "thumbnail", pictureText === null
284
+ recipes.push(recipe(context, "thumbnail:prompt", "thumbnail", pictureText === null || filled.waits.length > 0
245
285
  ? {
246
286
  kind: "deferred",
247
287
  version: 1,
248
288
  operation: "thumbnail-prompt",
249
- template: [llmInputFingerprint(context, messages), article.fingerprint],
289
+ template: [
290
+ llmInputFingerprint(context, messages),
291
+ article.fingerprint,
292
+ ...filled.waits.map((one) => one.fingerprint),
293
+ ],
250
294
  }
251
- : llmInput(context, messages), [article.key]));
295
+ : llmInput(context, messages), [...new Set([article.key, ...filled.dependsOn])]));
252
296
  }
253
297
  return {
254
298
  recipes,
299
+ keywords,
255
300
  articleText,
256
301
  pictureText,
257
302
  narrationText,
@@ -276,12 +321,6 @@ export function matchingText(context, value, field) {
276
321
  return null;
277
322
  return selectedText(context, value.key, field);
278
323
  }
279
- function articleMarkdownOf(context) {
280
- const { config, content, resolved } = context;
281
- return config.sources.article !== "generate" || content.articleEdited === true
282
- ? (content.articleMarkdown ?? config.provided.article ?? "")
283
- : resolved.articleMarkdown;
284
- }
285
324
  function glossaryOf(context, endMatter) {
286
325
  if (!usesPronunciationGlossary(context.config))
287
326
  return { ok: true, entries: [] };
@@ -301,3 +340,8 @@ function withShared(own, config) {
301
340
  return own;
302
341
  return { ...own, entries: withSharedGlossary(own.entries, config.sharedGlossary) };
303
342
  }
343
+ function withSampleScope(prompt, sample) {
344
+ return sample === undefined || sample.fullScript
345
+ ? prompt
346
+ : `${prompt.trimEnd()}\n\n${sampleArticleInstruction}`;
347
+ }
@@ -1,10 +1,16 @@
1
+ import { editSettings } from "../../kernel/ports/settings-places.js";
1
2
  import { narrationPreparationFields, shortsFields, usesNarrationPreparation, usesReference, usesShorts, usesYoutubeDescription, videoEditFields, youtubeDescriptionFields, } from "../admission/rules.js";
2
3
  import { detectSlots, filledKeywords, render } from "../admission/substitute.js";
3
4
  import { imagesPerVideoMax } from "../images/scale.js";
5
+ import { keywordUsesOf, placedProblems, textEntryProblems, usesFacts, } from "../keywords/rules.js";
4
6
  import { loudnessFields } from "../loudness/model.js";
5
7
  import { pauseFields } from "../narration/pauses-model.js";
6
- import { reviewFields } from "../reviews/rules.js";
8
+ import { reviewPromptKey } from "../reviews/model.js";
9
+ import { activeReviewStages, reviewFields } from "../reviews/rules.js";
10
+ import { socialPlatforms } from "../social/model.js";
11
+ import { usesPostPrompt } from "../social/rules.js";
7
12
  import { usesAnimation } from "../video/edit-settings.js";
13
+ import { usesScriptPrompt } from "../voices/model.js";
8
14
  export function validateRecipeInputs(config, content) {
9
15
  const fields = [
10
16
  ...narrationPreparationFields(config),
@@ -20,14 +26,18 @@ export function validateRecipeInputs(config, content) {
20
26
  config.sources.thumbnail === "prompt_by_llm" ||
21
27
  usesNarrationPreparation(config) ||
22
28
  usesYoutubeDescription(config) ||
29
+ // A post written by a post prompt.
30
+ usesPostPrompt(config) ||
23
31
  usesShorts(config) ||
24
32
  (config.sources.audio === "generate" &&
25
- (config.intro?.mode === "llm" || config.outro?.mode === "llm"));
33
+ (config.intro?.mode === "llm" || config.outro?.mode === "llm")) ||
34
+ // The keyword digest and the scene keywords are written by the text model too.
35
+ Object.keys(keywordUsesOf(keywordBodies(config, content))).some((cost) => cost === "digest" || cost === "scenes");
26
36
  if (llm && (!config.llm?.provider.trim() || !config.llm.model.trim()))
27
- fields.push({ field: "llm", message: "Pick a text provider and model." });
37
+ fields.push({ field: "llm", message: "Choose a Service and a Model under Written by." });
28
38
  if (config.sources.audio === "generate") {
29
39
  if (!config.audio?.provider.trim() || !config.audio.model.trim())
30
- fields.push({ field: "audio", message: "Pick a narration provider and model." });
40
+ fields.push({ field: "audio", message: "Choose a Service and a Model under Read by." });
31
41
  else if (!config.audio.voice.trim())
32
42
  fields.push({ field: "audio.voice", message: "Pick a narration voice." });
33
43
  }
@@ -39,7 +49,7 @@ export function validateRecipeInputs(config, content) {
39
49
  usesShorts(config) ||
40
50
  usesAnimation(config)) &&
41
51
  (!config.images?.provider.trim() || !config.images.model.trim()))
42
- fields.push({ field: "images", message: "Pick an image provider and model." });
52
+ fields.push({ field: "images", message: "Choose a Service and a Model under Images by." });
43
53
  // 60, or more for a project that scales its images with the narration (`images/scale.ts`).
44
54
  const imagesMax = imagesPerVideoMax(config);
45
55
  if (content.imageOrder.length > imagesMax)
@@ -58,10 +68,60 @@ export function validateRecipeInputs(config, content) {
58
68
  field: `chunking.${chunking?.mode}`,
59
69
  message: "Use a whole number from 1 to 1,000,000.",
60
70
  });
71
+ if (usesReference(config) &&
72
+ config.reference?.source === "provide" &&
73
+ content.provided.reference === undefined)
74
+ fields.push({
75
+ field: "content.provided.reference",
76
+ message: "Upload the establishing image, choose From a prompt instead, or set Establishing image to Off.",
77
+ });
78
+ if (config.sources.images !== "off")
79
+ for (const key of content.imageOrder) {
80
+ const image = content.imageDefinitions[key];
81
+ if (image?.source === "provide" && image.assetId === null)
82
+ fields.push({
83
+ field: `content.imageDefinitions.${key}.assetId`,
84
+ message: "Choose an uploaded image.",
85
+ });
86
+ }
87
+ const prompts = checkedPrompts(config, content);
88
+ for (const prompt of prompts) {
89
+ if (!(prompt.raw === null ? prompt.literal : render(prompt.raw, config.values))?.trim())
90
+ fields.push({ field: prompt.field, message: "Enter a prompt or text." });
91
+ const text = prompt.raw ?? prompt.literal ?? "";
92
+ if (config.keywordFacts === undefined && usesFacts([text]))
93
+ fields.push({
94
+ field: prompt.field,
95
+ message: `This prompt uses a keyword Slopify fills from the channel and its videos, but this project has no keyword facts yet. Use Refresh keyword facts in ${editSettings.title}, then save again.`,
96
+ });
97
+ if (prompt.raw === null)
98
+ continue;
99
+ const slots = detectSlots(prompt.raw);
100
+ if (slots.errors.length > 0)
101
+ fields.push({ field: prompt.field, message: "Fix the keyword placeholders in this prompt." });
102
+ for (const key of slots.names)
103
+ if (!filledKeywords.includes(key) &&
104
+ (!Object.hasOwn(config.values, key) || !config.values[key]?.trim()))
105
+ fields.push({ field: `values.${key}`, message: "Enter a value for this keyword." });
106
+ }
107
+ fields.push(...placedProblems(keywordBodies(config, content), config.values));
108
+ for (const category of ["intro", "outro"]) {
109
+ const raw = content.promptTemplates[category] ?? config.rendered[category];
110
+ if (config.sources.audio === "generate" && config[category]?.mode === "text" && raw != null)
111
+ fields.push(...textEntryProblems(raw, category).map((problem) => ({
112
+ field: `rendered.${category}`,
113
+ message: problem.message,
114
+ })));
115
+ }
116
+ return fields;
117
+ }
118
+ // The prompts a version runs, each with the field a refusal marks and where it runs.
119
+ function checkedPrompts(config, content) {
61
120
  const prompts = [];
62
121
  if (usesNarrationPreparation(config))
63
122
  prompts.push({
64
123
  field: "rendered.narration",
124
+ place: "narration",
65
125
  raw: content.promptTemplates.narration ?? null,
66
126
  literal: config.rendered.narration,
67
127
  });
@@ -71,6 +131,7 @@ export function validateRecipeInputs(config, content) {
71
131
  content.promptTemplates.description != null)
72
132
  prompts.push({
73
133
  field: "rendered.description",
134
+ place: "description",
74
135
  raw: content.promptTemplates.description,
75
136
  literal: config.rendered.description,
76
137
  });
@@ -82,6 +143,7 @@ export function validateRecipeInputs(config, content) {
82
143
  if (usesShorts(config) && name?.trim() && content.promptTemplates[key] != null)
83
144
  prompts.push({
84
145
  field: `rendered.${key}`,
146
+ place: key === "shorts" ? "shorts" : "image",
85
147
  raw: content.promptTemplates[key] ?? null,
86
148
  literal: config.rendered[key],
87
149
  });
@@ -89,6 +151,7 @@ export function validateRecipeInputs(config, content) {
89
151
  (config.sources.research === "generate" && config.sources.article !== "provide"))
90
152
  prompts.push({
91
153
  field: "rendered.article",
154
+ place: usesScriptPrompt(config) ? "script" : "article",
92
155
  raw: content.promptTemplates.article ?? null,
93
156
  literal: config.rendered.article,
94
157
  });
@@ -96,12 +159,26 @@ export function validateRecipeInputs(config, content) {
96
159
  if (config.sources.audio === "generate" && config[category] !== undefined)
97
160
  prompts.push({
98
161
  field: `rendered.${category}`,
162
+ place: category,
99
163
  raw: content.promptTemplates[category] ?? null,
100
164
  literal: config.rendered[category],
101
165
  });
166
+ // Each site's post prompt, only when one is picked: none picked uses the built-in one.
167
+ for (const platform of socialPlatforms) {
168
+ const choice = config.social?.[platform];
169
+ const key = `social.${platform}`;
170
+ if (choice?.text === "prompt" && choice.prompt?.trim() && content.promptTemplates[key] != null)
171
+ prompts.push({
172
+ field: `rendered.${key}`,
173
+ place: "post",
174
+ raw: content.promptTemplates[key] ?? null,
175
+ literal: config.rendered[key],
176
+ });
177
+ }
102
178
  if (config.sources.thumbnail === "from_prompt" || config.sources.thumbnail === "prompt_by_llm")
103
179
  prompts.push({
104
180
  field: "rendered.thumbnailPrompt",
181
+ place: "thumbnail",
105
182
  raw: content.promptTemplates.thumbnailPrompt ?? null,
106
183
  literal: config.rendered.thumbnailPrompt,
107
184
  });
@@ -109,43 +186,37 @@ export function validateRecipeInputs(config, content) {
109
186
  if (usesReference(config) && config.reference?.source === "prompt")
110
187
  prompts.push({
111
188
  field: "rendered.referencePrompt",
189
+ place: "image",
112
190
  raw: content.promptTemplates.referencePrompt ?? null,
113
191
  literal: config.rendered.referencePrompt,
114
192
  });
115
- if (usesReference(config) &&
116
- config.reference?.source === "provide" &&
117
- content.provided.reference === undefined)
118
- fields.push({
119
- field: "content.provided.reference",
120
- message: "Upload the establishing image, choose From a prompt instead, or set Establishing image to Off.",
121
- });
122
193
  if (config.sources.images !== "off")
123
194
  for (const key of content.imageOrder) {
124
195
  const image = content.imageDefinitions[key];
125
196
  if (image?.source === "generate")
126
197
  prompts.push({
127
198
  field: `content.imageDefinitions.${key}.prompt`,
199
+ place: "image",
128
200
  raw: image.templateKey == null ? null : (content.promptTemplates[image.templateKey] ?? null),
129
201
  literal: image.prompt ?? undefined,
130
202
  });
131
- else if (image?.source === "provide" && image.assetId === null)
132
- fields.push({
133
- field: `content.imageDefinitions.${key}.assetId`,
134
- message: "Choose an uploaded image.",
135
- });
136
203
  }
137
- for (const prompt of prompts) {
138
- if (!(prompt.raw === null ? prompt.literal : render(prompt.raw, config.values))?.trim())
139
- fields.push({ field: prompt.field, message: "Enter a prompt or text." });
140
- if (prompt.raw === null)
141
- continue;
142
- const slots = detectSlots(prompt.raw);
143
- if (slots.errors.length > 0)
144
- fields.push({ field: prompt.field, message: "Fix the keyword placeholders in this prompt." });
145
- for (const key of slots.names)
146
- if (!filledKeywords.includes(key) &&
147
- (!Object.hasOwn(config.values, key) || !config.values[key]?.trim()))
148
- fields.push({ field: `values.${key}`, message: "Enter a value for this keyword." });
149
- }
150
- return fields;
204
+ return prompts;
205
+ }
206
+ // The bodies a version runs with their places, for the keyword checks: its prompts and its
207
+ // reviews' prompts.
208
+ export function keywordBodies(config, content) {
209
+ return [
210
+ ...checkedPrompts(config, content).map((prompt) => ({
211
+ field: prompt.field,
212
+ place: prompt.place,
213
+ body: prompt.raw ?? prompt.literal ?? "",
214
+ })),
215
+ ...activeReviewStages(config).flatMap((stage) => {
216
+ const raw = content.promptTemplates[reviewPromptKey(stage)];
217
+ return raw == null || !config.reviews?.stages[stage]?.prompt?.trim()
218
+ ? []
219
+ : [{ field: `rendered.${reviewPromptKey(stage)}`, place: "review", body: raw }];
220
+ }),
221
+ ];
151
222
  }
@@ -1,3 +1,4 @@
1
+ import { internalError } from "../../kernel/errors.js";
1
2
  import { thumbnailAspect } from "../../kernel/pipeline.js";
2
3
  import { subjectOf, thumbnailCountOf, thumbnailKey } from "../admission/model.js";
3
4
  import { usesShortMode } from "../admission/short-mode.js";
@@ -7,6 +8,7 @@ import { thumbnailOverrideOf } from "../revisions/model.js";
7
8
  import { usesAmbientBed } from "../video/ambient-bed.js";
8
9
  import { lookWait, withLooks } from "./recipe-appearance.js";
9
10
  import { castFor } from "./recipe-cast.js";
11
+ import { fillPrompt } from "./recipe-keywords.js";
10
12
  import { noMaster } from "./recipe-loudness.js";
11
13
  import { recipe, resourceIdentity, } from "./recipe-model.js";
12
14
  import { castField, imageChoice } from "./recipe-reference.js";
@@ -23,30 +25,36 @@ master = noMaster,
23
25
  // Each image's own scene, for the images whose prompt asks for one (`recipe-scenes.ts`).
24
26
  scenes,
25
27
  // How the subject and characters look, for prompts with `{{Appearance}}`.
26
- appearance) {
28
+ appearance,
29
+ // The keywords Slopify fills in an image prompt (`recipe-keywords.ts`).
30
+ fill = unfilled) {
27
31
  const recipes = [];
28
32
  const imageKeys = config.sources.images === "off" ? [] : content.imageOrder;
29
33
  for (const key of imageKeys) {
30
34
  const image = content.imageDefinitions[key];
31
35
  if (image === undefined)
32
- throw new Error("Slopify hit an internal error (one of the project's images has no saved settings). Try again; if it happens again, use Download diagnostics in Settings and report it.");
36
+ throw new Error(internalError("one of the project's images has no saved settings"));
33
37
  const raw = image.templateKey === undefined || image.templateKey === null
34
38
  ? undefined
35
39
  : content.promptTemplates[image.templateKey];
36
40
  const at = scenes?.keys.indexOf(key) ?? -1;
37
41
  if (scenes !== undefined && at !== -1 && image.source === "generate") {
38
- recipes.push(sceneImage(config, content, key, raw ?? image.prompt ?? "", scenes, at, reference, appearance));
42
+ recipes.push(sceneImage(config, content, key, raw ?? image.prompt ?? "", scenes, at, reference, appearance, fill));
39
43
  continue;
40
44
  }
41
45
  // With Scenes from the article off, a prompt's `{{Scene}}` line is left out.
42
- const written = raw === undefined || raw === null
46
+ const base = raw === undefined || raw === null
43
47
  ? image.prompt === null || image.prompt === undefined
44
48
  ? image.prompt
45
49
  : withoutScene(image.prompt)
46
50
  : render(withoutScene(raw), config.values);
47
- const prompt = written === null || written === undefined || image.source !== "generate"
51
+ const keyed = base === null || base === undefined || image.source !== "generate" ? undefined : fill(base);
52
+ const written = keyed?.text ?? base;
53
+ const prompt = written === null || written === undefined || keyed === undefined
48
54
  ? written
49
- : withLooks(written, appearance);
55
+ : keyed.waits.length > 0
56
+ ? null
57
+ : withLooks(written, appearance);
50
58
  // Waiting for the looks: the same wait an image with a scene has, on the lookup instead.
51
59
  if (prompt === null && written !== null && written !== undefined) {
52
60
  const wait = lookWait(written, appearance);
@@ -62,8 +70,13 @@ appearance) {
62
70
  config.format,
63
71
  ...(config.images?.thinking === undefined ? [] : [config.images.thinking]),
64
72
  ...(reference === undefined ? [] : [reference.input.fingerprint]),
73
+ ...(keyed?.waits ?? []).map((one) => one.fingerprint),
65
74
  ],
66
- }, [...wait.dependsOn, ...(reference === undefined ? [] : [reference.key])]));
75
+ }, [
76
+ ...wait.dependsOn,
77
+ ...(reference === undefined ? [] : [reference.key]),
78
+ ...(keyed?.waits ?? []).map((one) => one.key),
79
+ ]));
67
80
  continue;
68
81
  }
69
82
  recipes.push(recipe({ content }, `image:${key}`, "images", image.source === "provide"
@@ -76,7 +89,10 @@ appearance) {
76
89
  prompt: prompt ?? "",
77
90
  ...(reference === undefined ? {} : { reference: reference.input }),
78
91
  ...castField(castFor(config, prompt)),
79
- }, image.source === "provide" || reference === undefined ? [] : [reference.key], { unresolved: image.source === "provide" ? image.assetId === null : !prompt?.trim() }));
92
+ }, [
93
+ ...(image.source === "provide" || reference === undefined ? [] : [reference.key]),
94
+ ...(keyed?.dependsOn ?? []),
95
+ ], { unresolved: image.source === "provide" ? image.assetId === null : !prompt?.trim() }));
80
96
  }
81
97
  if (config.sources.video !== "off") {
82
98
  const short = usesShortMode(config);
@@ -152,11 +168,12 @@ appearance) {
152
168
  // An image drawn from its own scene. Until the scenes are written it waits as a deferred
153
169
  // request that names the scenes step and the image's place among them, so writing them turns
154
170
  // it into its image request (materialization), and writing them again draws it again.
155
- function sceneImage(config, content, key, body, scenes, at, reference, appearance) {
171
+ function sceneImage(config, content, key, body, scenes, at, reference, appearance, fill) {
156
172
  const scene = scenes.scenes?.[at];
157
- const prompt = scene === undefined
173
+ const keyed = fill(render(body, config.values));
174
+ const prompt = scene === undefined || keyed.waits.length > 0
158
175
  ? undefined
159
- : (withLooks(withScene(render(body, config.values), scene), appearance, scene) ?? undefined);
176
+ : (withLooks(withScene(keyed.text, scene, { legacy: scenes.legacy, ...scenes.details?.[at] }), appearance, scene) ?? undefined);
160
177
  const wait = lookWait(body, appearance);
161
178
  return recipe({ content }, `image:${key}`, "images", prompt === undefined
162
179
  ? {
@@ -172,6 +189,7 @@ function sceneImage(config, content, key, body, scenes, at, reference, appearanc
172
189
  ...(config.images?.thinking === undefined ? [] : [config.images.thinking]),
173
190
  ...(reference === undefined ? [] : [reference.input.fingerprint]),
174
191
  ...wait.template,
192
+ ...keyed.waits.map((one) => one.fingerprint),
175
193
  ],
176
194
  }
177
195
  : {
@@ -182,7 +200,15 @@ function sceneImage(config, content, key, body, scenes, at, reference, appearanc
182
200
  prompt,
183
201
  ...(reference === undefined ? {} : { reference: reference.input }),
184
202
  ...castField(castFor(config, prompt)),
185
- }, [scenes.recipe.key, ...wait.dependsOn, ...(reference === undefined ? [] : [reference.key])]);
203
+ }, [
204
+ scenes.recipe.key,
205
+ ...wait.dependsOn,
206
+ ...(reference === undefined ? [] : [reference.key]),
207
+ ...keyed.dependsOn,
208
+ ]);
209
+ }
210
+ function unfilled(text) {
211
+ return { text, waits: [], dependsOn: [] };
186
212
  }
187
213
  // What the ambient bed adds to the render's fingerprint: its settings and, for the user's own
188
214
  // file, the project asset it plays. Undefined without a bed.
@@ -219,9 +245,17 @@ appearance) {
219
245
  }, [], { unresolved: content.provided.thumbnail === undefined }),
220
246
  ];
221
247
  const promptRecipe = textRecipes.find((value) => value.key === "thumbnail:prompt");
222
- const written = config.sources.thumbnail === "prompt_by_llm" && promptRecipe !== undefined
223
- ? matchingText(context, promptRecipe, "prompt")
224
- : renderedPrompt(context, "thumbnailPrompt");
248
+ // A prompt the AI wrote is sent as it wrote it; a Library prompt has its keywords filled.
249
+ const keyed = config.sources.thumbnail === "prompt_by_llm" && promptRecipe !== undefined
250
+ ? undefined
251
+ : fillPrompt(context, renderedPrompt(context, "thumbnailPrompt"), "thumbnail");
252
+ const written = keyed === undefined
253
+ ? promptRecipe === undefined
254
+ ? null
255
+ : matchingText(context, promptRecipe, "prompt")
256
+ : keyed.waits.length > 0
257
+ ? null
258
+ : keyed.text;
225
259
  const sceneFor = (variant) => scenes?.scenes?.[variant - 1];
226
260
  const withScenes = scenes !== undefined;
227
261
  const variants = Array.from({ length: thumbnailCountOf(config) }, (_, index) => index + 1);
@@ -239,7 +273,10 @@ appearance) {
239
273
  : withScenes
240
274
  ? scene === undefined
241
275
  ? null
242
- : withScene(written, scene)
276
+ : withScene(written, scene, {
277
+ legacy: scenes.legacy,
278
+ ...scenes.details?.[variant - 1],
279
+ })
243
280
  : withoutScene(written);
244
281
  // A thumbnail stands for the subject, so it always has the subject's look.
245
282
  const prompt = sceneFilled === null
@@ -263,6 +300,7 @@ appearance) {
263
300
  ...(variant === 1 ? [] : [["thumbnail-variant", variant]]),
264
301
  ...(withScenes ? [["thumbnail-scene", scenes.recipe.fingerprint]] : []),
265
302
  ...wait.template,
303
+ ...(keyed?.waits ?? []).map((one) => one.fingerprint),
266
304
  ],
267
305
  }
268
306
  : {
@@ -279,6 +317,7 @@ appearance) {
279
317
  ...(reference === undefined ? [] : [reference.key]),
280
318
  ...(withScenes ? [scenes.recipe.key] : []),
281
319
  ...wait.dependsOn,
320
+ ...(keyed?.dependsOn ?? []),
282
321
  ]);
283
322
  });
284
323
  }
@@ -1,4 +1,5 @@
1
1
  import { aliasMatches, applyAliases } from "../../kernel/ports/narration-aliases.js";
2
+ import { createRows, editSettings } from "../../kernel/ports/settings-places.js";
2
3
  import { splitText } from "../../kernel/ports/text.js";
3
4
  import { narrationAliasesOf, usesNarrationPreparation } from "../admission/rules.js";
4
5
  import { aliasSpans, withAliasSpans } from "../narration/aliases.js";
@@ -83,7 +84,7 @@ export function voiceBodyRecipes(context, voices, script, glossary) {
83
84
  parts.push(future());
84
85
  }
85
86
  else if (parsed !== undefined && !parsed.ok) {
86
- parts.push(refused(context, "audio:body:turn:1", script.dependsOn, `The script can't be narrated: ${parsed.reason} Fix it in Edit project → Article, then Try again.`));
87
+ parts.push(refused(context, "audio:body:turn:1", script.dependsOn, `The script can't be narrated: ${parsed.reason} Fix it in ${editSettings.article}, then Try again.`));
87
88
  }
88
89
  else if (pending) {
89
90
  parts.push(future());
@@ -251,7 +252,7 @@ function speakerGlossary(context, speaker, glossary) {
251
252
  if (!own.ok)
252
253
  return {
253
254
  ok: false,
254
- reason: `${speaker.name.trim()}'s pronunciations can't be used: ${own.reason.replace("Edit the glossary or turn off Use Pronunciation Glossary.", "Fix them under Speakers (Play → Narration, or Edit project → Providers).")}`,
255
+ reason: `${speaker.name.trim()}'s pronunciations can't be used: ${own.reason.replace("Edit the glossary or turn off Use Pronunciation Glossary.", `Fix them under Speakers (${createRows.narration}, or ${editSettings.narration}).`)}`,
255
256
  };
256
257
  const run = context.config.audio?.usePronunciationGlossary === true && glossary?.ok === true
257
258
  ? glossary.entries