@gentbajko/slopify 3.25.9 → 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 (603) 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/4.0.0.md +51 -0
  195. package/dist/patch-notes/index.json +6 -0
  196. package/dist/slices/admission/heads.js +2 -1
  197. package/dist/slices/admission/repo.js +2 -1
  198. package/dist/slices/admission/rules.js +43 -8
  199. package/dist/slices/admission/schema.js +8 -0
  200. package/dist/slices/admission/stage-writes.js +0 -3
  201. package/dist/slices/admission/start.js +48 -31
  202. package/dist/slices/admission/substitute.js +7 -4
  203. package/dist/slices/agents/attention-posts.js +31 -0
  204. package/dist/slices/agents/attention-templates.js +33 -0
  205. package/dist/slices/agents/attention.js +10 -1
  206. package/dist/slices/agents/connection.js +2 -6
  207. package/dist/slices/agents/model.js +6 -3
  208. package/dist/slices/agents/repo.js +15 -7
  209. package/dist/slices/article/continuation.js +0 -63
  210. package/dist/slices/article/plain.js +3 -2
  211. package/dist/slices/article/segments.js +2 -62
  212. package/dist/slices/assistant/access.js +1 -0
  213. package/dist/slices/assistant/asks.js +80 -0
  214. package/dist/slices/assistant/cost.js +54 -0
  215. package/dist/slices/assistant/gate.js +222 -0
  216. package/dist/slices/assistant/model.js +79 -0
  217. package/dist/slices/assistant/names.js +23 -0
  218. package/dist/slices/assistant/prompt.js +74 -0
  219. package/dist/slices/assistant/repo.js +74 -0
  220. package/dist/slices/assistant/tokens.js +18 -0
  221. package/dist/slices/assistant/turn.js +342 -0
  222. package/dist/slices/autopilot/buffer.js +76 -0
  223. package/dist/slices/autopilot/model.js +14 -0
  224. package/dist/slices/autopilot/notify.js +27 -0
  225. package/dist/slices/autopilot/repo.js +34 -0
  226. package/dist/slices/autopilot/signals.js +194 -0
  227. package/dist/slices/autopilot/warnings.js +85 -0
  228. package/dist/slices/autopilot/weekly.js +100 -0
  229. package/dist/slices/backups/files.js +2 -6
  230. package/dist/slices/backups/repo.js +3 -15
  231. package/dist/slices/backups/service.js +4 -3
  232. package/dist/slices/channels/cast-images.js +2 -1
  233. package/dist/slices/channels/model.js +0 -1
  234. package/dist/slices/channels/rebrand.js +0 -3
  235. package/dist/slices/channels/repo.js +4 -0
  236. package/dist/slices/channels/schema.js +6 -0
  237. package/dist/slices/channels/service.js +10 -0
  238. package/dist/slices/channels/videos.js +2 -14
  239. package/dist/slices/checkpoints/index.js +0 -1
  240. package/dist/slices/control/providers.js +0 -26
  241. package/dist/slices/designs/artwork.js +66 -0
  242. package/dist/slices/designs/budget.js +23 -0
  243. package/dist/slices/designs/calls.js +124 -0
  244. package/dist/slices/designs/check-context.js +38 -0
  245. package/dist/slices/designs/check-text.js +43 -0
  246. package/dist/slices/designs/fields.js +88 -0
  247. package/dist/slices/designs/frames.js +34 -0
  248. package/dist/slices/designs/gate.js +128 -0
  249. package/dist/slices/designs/layout.js +293 -0
  250. package/dist/slices/designs/model.js +35 -0
  251. package/dist/slices/designs/pdf-job.js +23 -0
  252. package/dist/slices/designs/pdf-pages.js +30 -0
  253. package/dist/slices/designs/pdf-runner.js +68 -0
  254. package/dist/slices/designs/pdf-worker.js +24 -0
  255. package/dist/slices/designs/picture-size.js +79 -0
  256. package/dist/slices/designs/pictures.js +55 -0
  257. package/dist/slices/designs/quote.js +24 -0
  258. package/dist/slices/designs/render-args.js +36 -0
  259. package/dist/slices/designs/render-input.js +59 -0
  260. package/dist/slices/designs/render.js +215 -0
  261. package/dist/slices/designs/repo.js +101 -0
  262. package/dist/slices/designs/requests.js +80 -0
  263. package/dist/slices/designs/runner.js +239 -0
  264. package/dist/slices/designs/sample-repo.js +91 -0
  265. package/dist/slices/designs/sample.js +222 -0
  266. package/dist/slices/designs/schema.js +87 -0
  267. package/dist/slices/designs/sections.js +44 -0
  268. package/dist/slices/designs/service.js +233 -0
  269. package/dist/slices/designs/steps-repo.js +102 -0
  270. package/dist/slices/designs/studio-kind.js +39 -0
  271. package/dist/slices/designs/template-checks.js +44 -0
  272. package/dist/slices/designs/template-make.js +118 -0
  273. package/dist/slices/designs/templates.js +218 -0
  274. package/dist/slices/document/fonts.js +2 -1
  275. package/dist/slices/episodes/summarize.js +5 -1
  276. package/dist/slices/estimate/index.js +45 -7
  277. package/dist/slices/fonts/catalog.js +3 -2
  278. package/dist/slices/fonts/discovery.js +3 -2
  279. package/dist/slices/fonts/files.js +2 -6
  280. package/dist/slices/fonts/index.js +0 -1
  281. package/dist/slices/fonts/sfnt.js +45 -10
  282. package/dist/slices/images/appearance.js +6 -15
  283. package/dist/slices/images/scenes.js +100 -27
  284. package/dist/slices/keywords/digest.js +84 -0
  285. package/dist/slices/keywords/facts.js +92 -0
  286. package/dist/slices/keywords/fill.js +80 -0
  287. package/dist/slices/keywords/model.js +75 -0
  288. package/dist/slices/keywords/rules.js +187 -0
  289. package/dist/slices/keywords/schema.js +28 -0
  290. package/dist/slices/keywords/specs.js +260 -0
  291. package/dist/slices/keywords/values.js +74 -0
  292. package/dist/slices/library/lint.js +29 -12
  293. package/dist/slices/library/model.js +2 -0
  294. package/dist/slices/library/photorealistic.js +4 -12
  295. package/dist/slices/library/rename.js +9 -7
  296. package/dist/slices/library/save.js +14 -10
  297. package/dist/slices/library/slots.js +37 -11
  298. package/dist/slices/library/transfer.js +2 -1
  299. package/dist/slices/library/used-by.js +25 -10
  300. package/dist/slices/loudness/loudnorm.js +51 -11
  301. package/dist/slices/model-checks/cost.js +61 -0
  302. package/dist/slices/model-checks/listing.js +124 -0
  303. package/dist/slices/model-checks/model.js +20 -0
  304. package/dist/slices/model-checks/record.js +32 -0
  305. package/dist/slices/model-checks/repo.js +120 -0
  306. package/dist/slices/model-checks/rules.js +112 -0
  307. package/dist/slices/model-checks/run.js +143 -0
  308. package/dist/slices/model-checks/service.js +198 -0
  309. package/dist/slices/model-upkeep/switch.js +70 -25
  310. package/dist/slices/model-upkeep/usage.js +26 -11
  311. package/dist/slices/narration/breaths.js +2 -1
  312. package/dist/slices/narration/clean-pieces.js +2 -1
  313. package/dist/slices/narration/concat.js +2 -1
  314. package/dist/slices/narration/peaks.js +3 -4
  315. package/dist/slices/narration/pronunciation-chunks.js +4 -3
  316. package/dist/slices/narration/pronunciation.js +3 -2
  317. package/dist/slices/narration/steering.js +4 -3
  318. package/dist/slices/notifications/notifier.js +4 -3
  319. package/dist/slices/notifications/rules.js +5 -2
  320. package/dist/slices/notifications/settings.js +4 -12
  321. package/dist/slices/notifications/subject.js +2 -1
  322. package/dist/slices/onboarding/copy.js +2 -1
  323. package/dist/slices/onboarding/first-run.js +3 -2
  324. package/dist/slices/onboarding/full-video.js +3 -2
  325. package/dist/slices/onboarding/install.js +4 -3
  326. package/dist/slices/onboarding/model.js +0 -16
  327. package/dist/slices/onboarding/packs.js +22 -22
  328. package/dist/slices/onboarding/quick-short.js +2 -1
  329. package/dist/slices/onboarding/sample.js +0 -1
  330. package/dist/slices/onboarding/state.js +2 -11
  331. package/dist/slices/onboarding/template.js +2 -1
  332. package/dist/slices/patch-notes/library.js +3 -2
  333. package/dist/slices/play-drafts/convert.js +4 -0
  334. package/dist/slices/play-drafts/review-inputs.js +8 -2
  335. package/dist/slices/play-drafts/review.js +5 -4
  336. package/dist/slices/play-drafts/schema.js +4 -0
  337. package/dist/slices/project-templates/from-project.js +9 -2
  338. package/dist/slices/project-templates/repo.js +6 -1
  339. package/dist/slices/project-templates/setup.js +8 -0
  340. package/dist/slices/project-templates/stand-ins.js +22 -0
  341. package/dist/slices/rebuild/activity.js +2 -0
  342. package/dist/slices/rebuild/admission-repo.js +3 -2
  343. package/dist/slices/rebuild/narration-history.js +4 -5
  344. package/dist/slices/rebuild/narration-retry.js +2 -1
  345. package/dist/slices/rebuild/preview-details.js +27 -9
  346. package/dist/slices/rebuild/preview-plan.js +3 -8
  347. package/dist/slices/rebuild/recipe-build.js +8 -3
  348. package/dist/slices/rebuild/recipe-describe.js +3 -3
  349. package/dist/slices/rebuild/recipe-digest.js +58 -0
  350. package/dist/slices/rebuild/recipe-input-schema.js +10 -0
  351. package/dist/slices/rebuild/recipe-keywords.js +71 -0
  352. package/dist/slices/rebuild/recipe-legacy.js +5 -1
  353. package/dist/slices/rebuild/recipe-model.js +4 -0
  354. package/dist/slices/rebuild/recipe-narration-text.js +2 -1
  355. package/dist/slices/rebuild/recipe-preparation.js +3 -3
  356. package/dist/slices/rebuild/recipe-reference.js +8 -4
  357. package/dist/slices/rebuild/recipe-reviews.js +27 -7
  358. package/dist/slices/rebuild/recipe-save.js +6 -3
  359. package/dist/slices/rebuild/recipe-scenes.js +40 -11
  360. package/dist/slices/rebuild/recipe-shorts.js +8 -8
  361. package/dist/slices/rebuild/recipe-social.js +46 -0
  362. package/dist/slices/rebuild/recipe-text.js +64 -20
  363. package/dist/slices/rebuild/recipe-validation.js +98 -28
  364. package/dist/slices/rebuild/recipe-visual.js +55 -16
  365. package/dist/slices/rebuild/recipe-work.js +15 -3
  366. package/dist/slices/rebuild/recipe-youtube.js +4 -3
  367. package/dist/slices/rebuild/recovery-selection.js +3 -1
  368. package/dist/slices/rebuild/recovery.js +3 -2
  369. package/dist/slices/rebuild/repo.js +4 -3
  370. package/dist/slices/rebuild/review-promise.js +25 -0
  371. package/dist/slices/rebuild/review-redo.js +2 -1
  372. package/dist/slices/rebuild/runtime-admission.js +4 -3
  373. package/dist/slices/rebuild/runtime-agent-review.js +11 -5
  374. package/dist/slices/rebuild/runtime-animate.js +9 -8
  375. package/dist/slices/rebuild/runtime-article.js +3 -2
  376. package/dist/slices/rebuild/runtime-checkpoints.js +5 -4
  377. package/dist/slices/rebuild/runtime-document.js +8 -11
  378. package/dist/slices/rebuild/runtime-export-bed.js +6 -11
  379. package/dist/slices/rebuild/runtime-export-edit.js +6 -15
  380. package/dist/slices/rebuild/runtime-export-inputs.js +7 -7
  381. package/dist/slices/rebuild/runtime-export-short.js +8 -4
  382. package/dist/slices/rebuild/runtime-export.js +34 -12
  383. package/dist/slices/rebuild/runtime-figure-card.js +2 -1
  384. package/dist/slices/rebuild/runtime-image.js +4 -9
  385. package/dist/slices/rebuild/runtime-instructions.js +2 -0
  386. package/dist/slices/rebuild/runtime-keywords.js +59 -0
  387. package/dist/slices/rebuild/runtime-lines.js +2 -11
  388. package/dist/slices/rebuild/runtime-local.js +14 -20
  389. package/dist/slices/rebuild/runtime-narration-publication.js +2 -5
  390. package/dist/slices/rebuild/runtime-narration-reuse.js +9 -13
  391. package/dist/slices/rebuild/runtime-narration-text.js +10 -15
  392. package/dist/slices/rebuild/runtime-pictures.js +2 -4
  393. package/dist/slices/rebuild/runtime-piece-label.js +10 -6
  394. package/dist/slices/rebuild/runtime-plan.js +31 -5
  395. package/dist/slices/rebuild/runtime-provider.js +60 -19
  396. package/dist/slices/rebuild/runtime-publication.js +10 -9
  397. package/dist/slices/rebuild/runtime-review.js +33 -28
  398. package/dist/slices/rebuild/runtime-run.js +4 -1
  399. package/dist/slices/rebuild/runtime-shorts.js +25 -45
  400. package/dist/slices/rebuild/runtime-store.js +3 -8
  401. package/dist/slices/rebuild/runtime-subtitles.js +16 -14
  402. package/dist/slices/rebuild/runtime-voices.js +8 -15
  403. package/dist/slices/rebuild/runtime-youtube.js +11 -29
  404. package/dist/slices/rebuild/transition-repo.js +6 -5
  405. package/dist/slices/rebuild/work-records.js +6 -0
  406. package/dist/slices/reruns/cascade.js +1 -2
  407. package/dist/slices/reruns/index.js +6 -9
  408. package/dist/slices/reviews/external-model.js +3 -0
  409. package/dist/slices/reviews/external-repo.js +2 -1
  410. package/dist/slices/reviews/external-rows.js +2 -3
  411. package/dist/slices/reviews/model.js +26 -8
  412. package/dist/slices/reviews/repo.js +2 -1
  413. package/dist/slices/reviews/rules.js +1 -0
  414. package/dist/slices/reviews/view.js +5 -2
  415. package/dist/slices/revisions/downloads.js +4 -5
  416. package/dist/slices/revisions/mutation-assets.js +7 -4
  417. package/dist/slices/revisions/mutation-cues.js +2 -5
  418. package/dist/slices/revisions/mutations.js +0 -1
  419. package/dist/slices/revisions/repo.js +3 -1
  420. package/dist/slices/revisions/schema.js +1 -0
  421. package/dist/slices/revisions/view.js +20 -0
  422. package/dist/slices/run-cost/limits.js +5 -2
  423. package/dist/slices/run-cost/meter.js +12 -5
  424. package/dist/slices/run-cost/panel.js +8 -74
  425. package/dist/slices/run-cost/plan-readings.js +127 -0
  426. package/dist/slices/run-cost/plan-use.js +169 -0
  427. package/dist/slices/run-cost/pricing.js +3 -3
  428. package/dist/slices/run-cost/week.js +32 -4
  429. package/dist/slices/schedules/agenda.js +11 -2
  430. package/dist/slices/schedules/by-date.js +42 -0
  431. package/dist/slices/schedules/calendar.js +5 -0
  432. package/dist/slices/schedules/design-agenda.js +36 -0
  433. package/dist/slices/schedules/design-check.js +41 -0
  434. package/dist/slices/schedules/design-run.js +126 -0
  435. package/dist/slices/schedules/next-run.js +62 -0
  436. package/dist/slices/schedules/prepare.js +18 -2
  437. package/dist/slices/schedules/repo.js +24 -16
  438. package/dist/slices/schedules/rescore.js +35 -0
  439. package/dist/slices/schedules/scheduler.js +13 -3
  440. package/dist/slices/schedules/schema.js +54 -3
  441. package/dist/slices/schedules/score.js +107 -0
  442. package/dist/slices/schedules/service.js +57 -24
  443. package/dist/slices/schedules/topic-list.js +15 -0
  444. package/dist/slices/schedules/topic-scores.js +104 -0
  445. package/dist/slices/schedules/topics.js +48 -36
  446. package/dist/slices/settings/cli-paths.js +4 -6
  447. package/dist/slices/settings/first-run.js +2 -11
  448. package/dist/slices/settings/model-reach.js +3 -17
  449. package/dist/slices/settings/model.js +10 -0
  450. package/dist/slices/settings/playback.js +33 -22
  451. package/dist/slices/settings/probe-read.js +16 -0
  452. package/dist/slices/settings/repo.js +33 -0
  453. package/dist/slices/settings/tutorial.js +2 -2
  454. package/dist/slices/settings/voices.js +4 -12
  455. package/dist/slices/shorts/pick.js +2 -15
  456. package/dist/slices/shorts/prompts.js +2 -13
  457. package/dist/slices/shorts/render.js +3 -0
  458. package/dist/slices/social/channel.js +44 -0
  459. package/dist/slices/social/defaults.js +17 -0
  460. package/dist/slices/social/due.js +36 -0
  461. package/dist/slices/social/extension.js +18 -0
  462. package/dist/slices/social/fit.js +115 -0
  463. package/dist/slices/social/limits.js +186 -0
  464. package/dist/slices/social/links.js +21 -0
  465. package/dist/slices/social/media.js +131 -0
  466. package/dist/slices/social/model.js +75 -0
  467. package/dist/slices/social/notice.js +19 -0
  468. package/dist/slices/social/pack.js +176 -0
  469. package/dist/slices/social/post-prompt.js +55 -0
  470. package/dist/slices/social/queue.js +31 -0
  471. package/dist/slices/social/repo.js +133 -0
  472. package/dist/slices/social/reshape.js +97 -0
  473. package/dist/slices/social/rules.js +5 -0
  474. package/dist/slices/social/schema.js +70 -0
  475. package/dist/slices/social/serve.js +51 -0
  476. package/dist/slices/social/service.js +128 -0
  477. package/dist/slices/social/settings.js +21 -0
  478. package/dist/slices/social/sources.js +114 -0
  479. package/dist/slices/social/state.js +110 -0
  480. package/dist/slices/social/text.js +149 -0
  481. package/dist/slices/storage/arrange.js +3 -1
  482. package/dist/slices/storage/assets.js +8 -10
  483. package/dist/slices/storage/backup-designs.js +74 -0
  484. package/dist/slices/storage/backup-export.js +8 -3
  485. package/dist/slices/storage/backup-format.js +10 -0
  486. package/dist/slices/storage/backup-import.js +20 -2
  487. package/dist/slices/storage/delete-project.js +6 -5
  488. package/dist/slices/storage/files-location.js +6 -8
  489. package/dist/slices/storage/layout.js +13 -2
  490. package/dist/slices/storage/model.js +3 -0
  491. package/dist/slices/storage/portable.js +16 -11
  492. package/dist/slices/storage/schema.js +2 -0
  493. package/dist/slices/storage/staging-refs.js +2 -1
  494. package/dist/slices/storage/staging.js +6 -9
  495. package/dist/slices/storage/trim.js +10 -3
  496. package/dist/slices/studio/calendar.js +4 -1
  497. package/dist/slices/studio/pick.js +2 -11
  498. package/dist/slices/studio/plan.js +6 -15
  499. package/dist/slices/studio/queue.js +3 -14
  500. package/dist/slices/studio/releases.js +26 -0
  501. package/dist/slices/studio/settings.js +14 -51
  502. package/dist/slices/studio/videos.js +12 -0
  503. package/dist/slices/style-preview/render.js +34 -13
  504. package/dist/slices/style-preview/schema.js +13 -0
  505. package/dist/slices/style-preview/service.js +2 -1
  506. package/dist/slices/style-preview/settings.js +33 -5
  507. package/dist/slices/telemetry/collector-client.js +2 -4
  508. package/dist/slices/telemetry/flush.js +2 -4
  509. package/dist/slices/telemetry/model.js +0 -7
  510. package/dist/slices/telemetry/record.js +10 -3
  511. package/dist/slices/template-studio/anatomy-view.js +118 -0
  512. package/dist/slices/template-studio/anatomy.js +73 -0
  513. package/dist/slices/template-studio/bakeoff-make.js +225 -0
  514. package/dist/slices/template-studio/bakeoff-repo.js +83 -0
  515. package/dist/slices/template-studio/bakeoff.js +333 -0
  516. package/dist/slices/template-studio/budget.js +103 -0
  517. package/dist/slices/template-studio/checks.js +273 -0
  518. package/dist/slices/template-studio/document-sample.js +56 -0
  519. package/dist/slices/template-studio/edit.js +15 -0
  520. package/dist/slices/template-studio/for-all.js +161 -0
  521. package/dist/slices/template-studio/free-video.js +31 -0
  522. package/dist/slices/template-studio/free.js +176 -0
  523. package/dist/slices/template-studio/gate.js +40 -0
  524. package/dist/slices/template-studio/hidden.js +20 -0
  525. package/dist/slices/template-studio/jobs.js +79 -0
  526. package/dist/slices/template-studio/loudness.js +111 -0
  527. package/dist/slices/template-studio/make.js +40 -0
  528. package/dist/slices/template-studio/model.js +54 -0
  529. package/dist/slices/template-studio/pack-import.js +253 -0
  530. package/dist/slices/template-studio/pack-samples.js +60 -0
  531. package/dist/slices/template-studio/pack.js +205 -0
  532. package/dist/slices/template-studio/paid-view.js +66 -0
  533. package/dist/slices/template-studio/paid.js +348 -0
  534. package/dist/slices/template-studio/quote.js +148 -0
  535. package/dist/slices/template-studio/ready.js +104 -0
  536. package/dist/slices/template-studio/repo.js +134 -0
  537. package/dist/slices/template-studio/reviews-path.js +58 -0
  538. package/dist/slices/template-studio/runner.js +84 -0
  539. package/dist/slices/template-studio/save.js +236 -0
  540. package/dist/slices/template-studio/say-it-as.js +248 -0
  541. package/dist/slices/template-studio/schedule-gate.js +34 -0
  542. package/dist/slices/template-studio/schedule-price.js +30 -0
  543. package/dist/slices/template-studio/schedule-sample.js +87 -0
  544. package/dist/slices/template-studio/schema.js +218 -0
  545. package/dist/slices/template-studio/scope.js +124 -0
  546. package/dist/slices/template-studio/sections.js +176 -0
  547. package/dist/slices/template-studio/service.js +173 -0
  548. package/dist/slices/template-studio/status.js +174 -0
  549. package/dist/slices/template-studio/topic.js +223 -0
  550. package/dist/slices/trash/designs.js +76 -0
  551. package/dist/slices/trash/model.js +13 -4
  552. package/dist/slices/trash/service.js +52 -2
  553. package/dist/slices/tutorials/library.js +3 -2
  554. package/dist/slices/video/ass-text.js +17 -0
  555. package/dist/slices/video/audio-export.js +2 -64
  556. package/dist/slices/video/encoding.js +160 -0
  557. package/dist/slices/video/ffmpeg.js +23 -21
  558. package/dist/slices/video/figure-card.js +1 -15
  559. package/dist/slices/video/slideshow.js +43 -18
  560. package/dist/slices/voices/palette.js +0 -3
  561. package/dist/slices/youtube/answer.js +2 -15
  562. package/dist/slices/youtube/edits-repo.js +2 -11
  563. package/dist/tutorials/Agents-MCP.md +2 -1
  564. package/dist/tutorials/Assistant.md +42 -0
  565. package/dist/tutorials/Channels.md +2 -0
  566. package/dist/tutorials/Home-and-Projects.md +19 -0
  567. package/dist/tutorials/Install.md +1 -0
  568. package/dist/tutorials/Keywords-Slopify-Fills.md +87 -0
  569. package/dist/tutorials/Models.md +33 -1
  570. package/dist/tutorials/Notifications.md +6 -0
  571. package/dist/tutorials/Play-Images.md +4 -0
  572. package/dist/tutorials/Posting-to-Social-Sites.md +121 -0
  573. package/dist/tutorials/Prompts.md +10 -6
  574. package/dist/tutorials/Remote-Access.md +63 -0
  575. package/dist/tutorials/Reviews-and-Checkpoints.md +13 -2
  576. package/dist/tutorials/Schedules.md +21 -1
  577. package/dist/tutorials/Settings-Reference.md +50 -8
  578. package/dist/tutorials/Template-Studio.md +125 -0
  579. package/dist/tutorials/Templates.md +3 -0
  580. package/dist/tutorials/_Sidebar.md +5 -0
  581. package/dist/updater/work-in-progress.js +42 -12
  582. package/dist/web/assets/index-CkAI_2E7.css +1 -0
  583. package/dist/web/assets/index-DcD0cdfo.js +334 -0
  584. package/dist/web/assets/{pdf-T7MCwjIv.js → pdf-TODRwF31.js} +1 -1
  585. package/dist/web/index.html +2 -2
  586. package/package.json +5 -1
  587. package/dist/slices/article/provided-entries.js +0 -66
  588. package/dist/slices/article/run.js +0 -145
  589. package/dist/slices/images/run.js +0 -234
  590. package/dist/slices/narration/legacy-plan.js +0 -58
  591. package/dist/slices/narration/run.js +0 -270
  592. package/dist/slices/rebuild/runtime-actions.js +0 -90
  593. package/dist/slices/research/run.js +0 -224
  594. package/dist/slices/subtitles/prepare.js +0 -194
  595. package/dist/slices/subtitles/transcript.js +0 -34
  596. package/dist/slices/thumbnail/run.js +0 -219
  597. package/dist/slices/video/audio-inputs.js +0 -33
  598. package/dist/slices/video/reuse-audio.js +0 -62
  599. package/dist/slices/video/run.js +0 -78
  600. package/dist/slices/video/write-export.js +0 -158
  601. package/dist/slices/video/write-subtitles.js +0 -59
  602. package/dist/web/assets/index-BadsoORk.css +0 -1
  603. package/dist/web/assets/index-D8LqNsCl.js +0 -326
@@ -0,0 +1,239 @@
1
+ import { artworkRequest, castReferences } from "./artwork.js";
2
+ import { createDesignCalls, noProviderMessage } from "./calls.js";
3
+ import { nearestFormat } from "./picture-size.js";
4
+ import { costChanged, costChangedMessage, quoteDesignImage } from "./quote.js";
5
+ import { editPictureSchema, makeArtworkSchema, quoteEditSchema, retryStepSchema, } from "./requests.js";
6
+ import { designView, mutationHash, reusedMessage } from "./service.js";
7
+ import { insertStep, makingSteps, restartStep, settleStep, stepById, stepByMutation, } from "./steps-repo.js";
8
+ const stoppedMessage = "Slopify stopped while this picture was being made. Press Try again.";
9
+ const noProvider = {
10
+ ok: false,
11
+ reason: "no-image-provider",
12
+ message: noProviderMessage,
13
+ };
14
+ const notFound = {
15
+ ok: false,
16
+ reason: "not-found",
17
+ message: "This design no longer exists. Open Library → Designs to choose another.",
18
+ };
19
+ export function createDesignRunner(deps) {
20
+ const calls = createDesignCalls(deps);
21
+ const start = calls.start;
22
+ const view = (designId) => {
23
+ const value = designView(deps.db, designId);
24
+ return value === undefined ? notFound : { ok: true, value };
25
+ };
26
+ return {
27
+ quoteEdit: (designId, raw) => {
28
+ const parsed = quoteEditSchema.safeParse(raw);
29
+ if (!parsed.success)
30
+ return { ok: false, reason: "invalid", message: "Choose an image model to quote." };
31
+ if (designView(deps.db, designId) === undefined)
32
+ return notFound;
33
+ return {
34
+ ok: true,
35
+ value: quoteDesignImage(deps.catalogue(), {
36
+ ...parsed.data,
37
+ purpose: "picture-edit",
38
+ sendsPictures: true,
39
+ }),
40
+ };
41
+ },
42
+ quoteArtwork: (designId) => {
43
+ const design = designView(deps.db, designId);
44
+ if (design === undefined)
45
+ return notFound;
46
+ const request = artworkRequest(design.document, design.values);
47
+ if (!request.ok)
48
+ return request;
49
+ return {
50
+ ok: true,
51
+ value: quoteDesignImage(deps.catalogue(), {
52
+ ...request.value,
53
+ purpose: "design-art",
54
+ sendsPictures: request.value.castMemberIds.length > 0,
55
+ }),
56
+ };
57
+ },
58
+ edit: (designId, raw) => {
59
+ const parsed = editPictureSchema.safeParse(raw);
60
+ if (!parsed.success)
61
+ return {
62
+ ok: false,
63
+ reason: "invalid",
64
+ message: "Write what to change (at most 4,000 characters), then press Edit.",
65
+ };
66
+ const input = parsed.data;
67
+ const { mutationId, acknowledgedHigh: _shown, acknowledgedUnknown: _accepted, ...asked } = input;
68
+ const replay = stepByMutation(deps.db, mutationId);
69
+ if (replay !== undefined)
70
+ return replay.hash === mutationHash(asked) && replay.step.designId === designId
71
+ ? view(designId)
72
+ : { ok: false, reason: "mutation-reused", message: reusedMessage };
73
+ if (designView(deps.db, designId) === undefined)
74
+ return notFound;
75
+ const from = stepById(deps.db, input.fromStepId);
76
+ if (from === undefined ||
77
+ from.designId !== designId ||
78
+ from.state !== "ready" ||
79
+ from.width === null ||
80
+ from.height === null)
81
+ return {
82
+ ok: false,
83
+ reason: "not-ready",
84
+ message: "The version to edit has no picture yet. Wait for it to finish, or continue from another version.",
85
+ };
86
+ if (deps.generate === undefined)
87
+ return noProvider;
88
+ const cast = castReferences(deps.db, input.castMemberIds ?? []);
89
+ if (!cast.ok)
90
+ return cast;
91
+ const quote = quoteDesignImage(deps.catalogue(), {
92
+ ...input,
93
+ purpose: "picture-edit",
94
+ sendsPictures: true,
95
+ });
96
+ if (costChanged(quote, input.acknowledgedHigh, input.acknowledgedUnknown))
97
+ return {
98
+ ok: false,
99
+ reason: "cost-changed",
100
+ message: costChangedMessage(quote, "this edit"),
101
+ current: quote,
102
+ };
103
+ const step = insertStep(deps.db, {
104
+ id: deps.ids.next(),
105
+ designId,
106
+ parentId: from.id,
107
+ kind: "edit",
108
+ instruction: input.instruction,
109
+ provider: input.provider,
110
+ model: input.model,
111
+ inputs: {
112
+ castMemberIds: input.castMemberIds ?? [],
113
+ aspect: nearestFormat({ width: from.width, height: from.height }),
114
+ prompt: input.instruction,
115
+ },
116
+ state: "making",
117
+ sha256: null,
118
+ width: null,
119
+ height: null,
120
+ mutationId,
121
+ mutationHash: mutationHash(asked),
122
+ at: deps.clock.now().toISOString(),
123
+ });
124
+ start(designId, step.id);
125
+ return view(designId);
126
+ },
127
+ artwork: (designId, raw) => {
128
+ const parsed = makeArtworkSchema.safeParse(raw);
129
+ if (!parsed.success)
130
+ return {
131
+ ok: false,
132
+ reason: "invalid",
133
+ message: "Press Make again from the design's page.",
134
+ };
135
+ const replay = stepByMutation(deps.db, parsed.data.mutationId);
136
+ if (replay !== undefined)
137
+ return replay.step.designId === designId
138
+ ? view(designId)
139
+ : { ok: false, reason: "mutation-reused", message: reusedMessage };
140
+ const design = designView(deps.db, designId);
141
+ if (design === undefined)
142
+ return notFound;
143
+ const request = artworkRequest(design.document, design.values);
144
+ if (!request.ok)
145
+ return request;
146
+ if (deps.generate === undefined)
147
+ return noProvider;
148
+ const cast = castReferences(deps.db, request.value.castMemberIds);
149
+ if (!cast.ok)
150
+ return cast;
151
+ const quote = quoteDesignImage(deps.catalogue(), {
152
+ ...request.value,
153
+ purpose: "design-art",
154
+ sendsPictures: request.value.castMemberIds.length > 0,
155
+ });
156
+ if (costChanged(quote, parsed.data.acknowledgedHigh, parsed.data.acknowledgedUnknown))
157
+ return {
158
+ ok: false,
159
+ reason: "cost-changed",
160
+ message: costChangedMessage(quote, "the artwork"),
161
+ current: quote,
162
+ };
163
+ const step = insertStep(deps.db, {
164
+ id: deps.ids.next(),
165
+ designId,
166
+ parentId: null,
167
+ kind: "generate",
168
+ instruction: null,
169
+ provider: request.value.provider,
170
+ model: request.value.model,
171
+ inputs: {
172
+ castMemberIds: request.value.castMemberIds,
173
+ aspect: request.value.aspect,
174
+ prompt: request.value.prompt,
175
+ },
176
+ state: "making",
177
+ sha256: null,
178
+ width: null,
179
+ height: null,
180
+ mutationId: parsed.data.mutationId,
181
+ mutationHash: mutationHash({ artwork: request.value }),
182
+ at: deps.clock.now().toISOString(),
183
+ });
184
+ start(designId, step.id);
185
+ return view(designId);
186
+ },
187
+ retry: (designId, stepId, raw) => {
188
+ const parsed = retryStepSchema.safeParse(raw);
189
+ if (!parsed.success)
190
+ return {
191
+ ok: false,
192
+ reason: "invalid",
193
+ message: "Try again is paid again, and the request didn't say the price you saw. Reload the design and press Try again.",
194
+ };
195
+ const step = stepById(deps.db, stepId);
196
+ if (step === undefined ||
197
+ step.designId !== designId ||
198
+ designView(deps.db, designId) === undefined)
199
+ return {
200
+ ok: false,
201
+ reason: "not-found",
202
+ message: "That version is no longer in this design. Reload the page.",
203
+ };
204
+ if (step.state !== "failed")
205
+ return {
206
+ ok: false,
207
+ reason: "busy",
208
+ message: "This version didn't fail, so there is nothing to try again.",
209
+ };
210
+ if (deps.generate === undefined)
211
+ return noProvider;
212
+ // A step a restart stopped may have been charged already: asked again, it is paid again.
213
+ const quote = quoteDesignImage(deps.catalogue(), {
214
+ provider: step.provider ?? "",
215
+ model: step.model ?? "",
216
+ purpose: step.kind === "edit" ? "picture-edit" : "design-art",
217
+ sendsPictures: step.kind === "edit" || step.inputs.castMemberIds.length > 0,
218
+ });
219
+ if (costChanged(quote, parsed.data.acknowledgedHigh, parsed.data.acknowledgedUnknown))
220
+ return {
221
+ ok: false,
222
+ reason: "cost-changed",
223
+ message: costChangedMessage(quote, "trying this version again"),
224
+ current: quote,
225
+ };
226
+ restartStep(deps.db, stepId);
227
+ start(designId, stepId);
228
+ return view(designId);
229
+ },
230
+ recover: () => {
231
+ let settled = 0;
232
+ for (const step of makingSteps(deps.db))
233
+ if (settleStep(deps.db, step.id, { state: "failed", error: stoppedMessage }, deps.clock.now().toISOString()))
234
+ settled += 1;
235
+ return settled;
236
+ },
237
+ idle: calls.idle,
238
+ };
239
+ }
@@ -0,0 +1,91 @@
1
+ /// <reference lib="esnext.temporal" />
2
+ import { z } from "zod";
3
+ // The one sample job each design template keeps (`design_template_samples`), and what the
4
+ // samples spent today against their daily budget.
5
+ export const sampleStates = ["queued", "making", "done", "failed", "held"];
6
+ const rowSchema = z.object({
7
+ template_id: z.string(),
8
+ design_id: z.string().nullable(),
9
+ version: z.number(),
10
+ state: z.enum(sampleStates),
11
+ held_reason: z.string().nullable(),
12
+ quoted_high: z.number().nullable(),
13
+ updated_at: z.string(),
14
+ });
15
+ export function sampleRow(db, templateId) {
16
+ const raw = db
17
+ .prepare("SELECT * FROM design_template_samples WHERE template_id=?")
18
+ .get(templateId);
19
+ if (raw === undefined)
20
+ return undefined;
21
+ const row = rowSchema.parse(raw);
22
+ return {
23
+ templateId: row.template_id,
24
+ designId: row.design_id,
25
+ version: row.version,
26
+ state: row.state,
27
+ heldReason: row.held_reason,
28
+ quotedHigh: row.quoted_high,
29
+ updatedAt: row.updated_at,
30
+ };
31
+ }
32
+ export function sampleRowOfDesign(db, designId) {
33
+ const raw = db
34
+ .prepare("SELECT template_id FROM design_template_samples WHERE design_id=?")
35
+ .get(designId);
36
+ return raw === undefined ? undefined : sampleRow(db, String(raw.template_id));
37
+ }
38
+ export function queuedTemplates(db) {
39
+ return db
40
+ .prepare("SELECT template_id FROM design_template_samples WHERE state='queued' ORDER BY updated_at")
41
+ .all()
42
+ .map((row) => String(row.template_id));
43
+ }
44
+ export function makingTemplates(db) {
45
+ return db
46
+ .prepare("SELECT template_id FROM design_template_samples WHERE state='making'")
47
+ .all()
48
+ .map((row) => String(row.template_id));
49
+ }
50
+ export function writeSample(db, row) {
51
+ db.prepare(`INSERT INTO design_template_samples (template_id,design_id,version,state,held_reason,quoted_high,updated_at)
52
+ VALUES (?,?,?,?,?,?,?)
53
+ ON CONFLICT(template_id) DO UPDATE SET design_id=excluded.design_id, version=excluded.version,
54
+ state=excluded.state, held_reason=excluded.held_reason, quoted_high=excluded.quoted_high,
55
+ updated_at=excluded.updated_at`).run(row.templateId, row.designId, row.version, row.state, row.heldReason, row.quotedHigh, row.updatedAt);
56
+ }
57
+ // Local midnight of `now` in `timeZone`, as an instant.
58
+ export function startOfDay(now, timeZone) {
59
+ const day = Temporal.Instant.fromEpochMilliseconds(now.valueOf())
60
+ .toZonedDateTimeISO(timeZone)
61
+ .toPlainDate();
62
+ return new Date(day.toZonedDateTime({ timeZone }).epochMilliseconds).toISOString();
63
+ }
64
+ // What every template's samples cost since local midnight, in dollars. Plan usage costs 0.
65
+ export function sampleSpentToday(db, now, timeZone) {
66
+ const row = db
67
+ .prepare(`SELECT COALESCE(SUM(u.cost), 0) AS spent FROM standalone_usage AS u
68
+ JOIN designs AS d ON d.id = u.owner_id
69
+ WHERE u.owner_kind='design' AND d.sample_of IS NOT NULL AND u.created_at >= ?`)
70
+ .get(startOfDay(now, timeZone));
71
+ return Number(row?.spent ?? 0);
72
+ }
73
+ // What samples queued or being made will cost: each one's quoted price, before any of it is
74
+ // spent. A template's own sample is left out, unless its picture is being made now.
75
+ export function sampleReserved(db, templateId) {
76
+ const row = db
77
+ .prepare(`SELECT COALESCE(SUM(quoted_high), 0) AS reserved FROM design_template_samples
78
+ WHERE (state IN ('queued','making') AND template_id IS NOT ?)
79
+ OR (state='making' AND template_id=?)`)
80
+ .get(templateId, templateId);
81
+ return Number(row?.reserved ?? 0);
82
+ }
83
+ // Earlier samples of a template, kept until the day they were made is over so today's spend
84
+ // still counts them; then removed with their pictures' renders by the caller.
85
+ export function oldSamples(db, templateId, keep, before) {
86
+ return db
87
+ .prepare(`SELECT id FROM designs WHERE sample_of=? AND id IS NOT ? AND created_at < ?
88
+ AND NOT EXISTS (SELECT 1 FROM design_steps WHERE design_id=designs.id AND state='making')`)
89
+ .all(templateId, keep, before)
90
+ .map((row) => String(row.id));
91
+ }
@@ -0,0 +1,222 @@
1
+ import { transact } from "../../kernel/db/tx.js";
2
+ import { budgetSentence } from "./budget.js";
3
+ import { missingValues } from "./fields.js";
4
+ import { costChanged, costChangedMessage } from "./quote.js";
5
+ import { deleteRenders } from "./render.js";
6
+ import { oldSamples, queuedTemplates, sampleReserved, sampleRow, sampleRowOfDesign, sampleSpentToday, startOfDay, writeSample, } from "./sample-repo.js";
7
+ import { designAffectedSections, sampleNeedsPicture } from "./sections.js";
8
+ import { designView } from "./service.js";
9
+ import { makeFromTemplate, quoteDocument } from "./template-make.js";
10
+ import { getTemplate, templateRevision } from "./templates.js";
11
+ const holdSlack = (ack) => ack * 1.25 + 0.05;
12
+ const money = (value) => `$${value.toFixed(2)}`;
13
+ function view(row) {
14
+ return {
15
+ state: row.state,
16
+ version: row.version,
17
+ designId: row.designId,
18
+ heldReason: row.heldReason,
19
+ quotedHigh: row.quotedHigh,
20
+ };
21
+ }
22
+ export function createDesignSamples(deps) {
23
+ const db = deps.service.db;
24
+ const now = () => deps.service.clock.now();
25
+ const at = () => now().toISOString();
26
+ const emit = (templateId) => deps.service.emit?.({ type: "design-template.sample", templateId });
27
+ // A making sample whose picture landed or failed settles; true when it changed.
28
+ const settle = (row) => {
29
+ if (row.state !== "making" || row.designId === null)
30
+ return row;
31
+ const design = designView(db, row.designId);
32
+ const last = design?.steps.at(-1);
33
+ if (last?.state === "making")
34
+ return row;
35
+ const next = last === undefined || last.state === "failed" || design === undefined
36
+ ? {
37
+ ...row,
38
+ state: "failed",
39
+ heldReason: last?.error ?? "The sample's picture was lost. Press Make sample.",
40
+ updatedAt: at(),
41
+ }
42
+ : { ...row, state: "done", heldReason: null, updatedAt: at() };
43
+ writeSample(db, next);
44
+ return next;
45
+ };
46
+ const sweep = (templateId, keep) => {
47
+ for (const id of oldSamples(db, templateId, keep, startOfDay(now(), deps.timeZone()))) {
48
+ db.prepare("DELETE FROM designs WHERE id=?").run(id);
49
+ deleteRenders(deps.service.paths, id);
50
+ }
51
+ };
52
+ // Why a sample a Save asked for waits instead of starting: a CLI plan samples may not use, a
53
+ // price nobody knows, more than the person saw, or past today's budget counting what other
54
+ // samples already queued or making will cost. Undefined when it may start.
55
+ const holdOf = (templateId, quote, acknowledgedHigh) => {
56
+ const spent = sampleSpentToday(db, now(), deps.timeZone()) + sampleReserved(db, templateId);
57
+ const budget = deps.budget();
58
+ return quote.rows.some((one) => one.onPlan === true) && !deps.plans()
59
+ ? "Waiting: this sample's picture runs on your CLI plan, and samples may not use CLI plans. Turn on Samples may use my CLI plans in Settings → Template studio, or press Make sample to make it anyway."
60
+ : quote.unknown > 0
61
+ ? "Waiting: the price of this sample's picture isn't known, so Slopify didn't make it. Press Make sample to make it anyway."
62
+ : quote.high > holdSlack(acknowledgedHigh)
63
+ ? `Waiting: this sample now costs about ${money(quote.high)}, more than the ${money(acknowledgedHigh)} you saw. Press Make sample (${money(quote.high)}) to make it.`
64
+ : spent + quote.high > budget + 0.005
65
+ ? budgetSentence(spent + quote.high, budget)
66
+ : null;
67
+ };
68
+ // The templates whose queued sample the person started with Make sample: it starts on their
69
+ // say, without the Save's holds. In memory: after a restart it is checked again.
70
+ const pressed = new Set();
71
+ // Starts a queued job once the picture before it is done.
72
+ const start = (templateId) => {
73
+ const row = sampleRow(db, templateId);
74
+ if (row === undefined || row.state !== "queued")
75
+ return;
76
+ const current = row.designId === null ? undefined : designView(db, row.designId);
77
+ if (current?.steps.some((step) => step.state === "making") === true)
78
+ return;
79
+ const revision = templateRevision(db, templateId, row.version);
80
+ if (revision === undefined || getTemplate(db, templateId) === undefined)
81
+ return;
82
+ const asked = pressed.delete(templateId);
83
+ // What a Save queued may have waited behind another picture: checked again as it starts.
84
+ if (!asked) {
85
+ const held = holdOf(templateId, quoteDocument(deps.catalogue(), revision.document), row.quotedHigh ?? 0);
86
+ if (held !== null) {
87
+ writeSample(db, { ...row, state: "held", heldReason: held, updatedAt: at() });
88
+ emit(templateId);
89
+ return;
90
+ }
91
+ }
92
+ const made = makeFromTemplate(deps, templateId, {
93
+ mutationId: `sample-${deps.service.ids.next()}`,
94
+ values: revision.document.samples,
95
+ acknowledgedHigh: row.quotedHigh ?? 0,
96
+ // Checked just above, or accepted on Make sample.
97
+ acknowledgedUnknown: true,
98
+ version: row.version,
99
+ }, { sampleOf: templateId });
100
+ if (!made.ok) {
101
+ const held = made.reason === "cost-changed";
102
+ const missing = missingValues(revision.document, revision.document.samples);
103
+ writeSample(db, {
104
+ ...row,
105
+ state: held ? "held" : "failed",
106
+ heldReason: made.reason === "missing-values"
107
+ ? `The sample wasn't made: ${missing.map((name) => `{{${name}}}`).join(", ")} ${missing.length === 1 ? "has" : "have"} no sample value. Fill ${missing.length === 1 ? "it" : "them"} in under Text layout → Sample values, then press Make sample.`
108
+ : made.message,
109
+ updatedAt: at(),
110
+ });
111
+ }
112
+ else {
113
+ const making = made.value.steps.some((step) => step.state === "making");
114
+ writeSample(db, {
115
+ ...row,
116
+ designId: made.value.id,
117
+ state: making ? "making" : "done",
118
+ heldReason: null,
119
+ updatedAt: at(),
120
+ });
121
+ sweep(templateId, made.value.id);
122
+ }
123
+ emit(templateId);
124
+ };
125
+ const queue = (templateId, version, quote, held) => {
126
+ const before = sampleRow(db, templateId);
127
+ const row = {
128
+ templateId,
129
+ designId: before?.designId ?? null,
130
+ version,
131
+ state: held === null ? "queued" : "held",
132
+ heldReason: held,
133
+ quotedHigh: quote,
134
+ updatedAt: at(),
135
+ };
136
+ // A picture being made keeps its state until it lands; the queued job waits behind it.
137
+ writeSample(db, before?.state === "making" && held === null ? { ...row, state: "queued" } : row);
138
+ if (held === null)
139
+ start(templateId);
140
+ emit(templateId);
141
+ return view(sampleRow(db, templateId) ?? row);
142
+ };
143
+ // Text-only changes: the sample keeps its artwork and takes the new settings at once.
144
+ const redraw = (templateId, version, doc) => {
145
+ const row = sampleRow(db, templateId);
146
+ if (row === undefined)
147
+ return null;
148
+ transact(db, () => {
149
+ if (row.designId !== null)
150
+ db.prepare("UPDATE designs SET document_json=?, values_json=?, template_version=?, updated_at=? WHERE id=?").run(JSON.stringify(doc), JSON.stringify(doc.samples), version, at(), row.designId);
151
+ writeSample(db, { ...row, version, updatedAt: at() });
152
+ });
153
+ if (row.designId !== null)
154
+ deps.service.emit?.({ type: "design.changed", designId: row.designId });
155
+ emit(templateId);
156
+ return view(sampleRow(db, templateId) ?? row);
157
+ };
158
+ return {
159
+ afterSave: (input) => {
160
+ if (input.sample === "skip")
161
+ return { ok: true, value: current(input.templateId) };
162
+ const row = sampleRow(db, input.templateId);
163
+ const affected = designAffectedSections(input.before, input.after);
164
+ const hasArtwork = row?.designId != null && row.state !== "failed";
165
+ if (hasArtwork && !sampleNeedsPicture(affected))
166
+ return { ok: true, value: redraw(input.templateId, input.version, input.after) };
167
+ const quote = quoteDocument(deps.catalogue(), input.after);
168
+ const held = holdOf(input.templateId, quote, input.acknowledgedHigh);
169
+ pressed.delete(input.templateId);
170
+ return { ok: true, value: queue(input.templateId, input.version, quote.high, held) };
171
+ },
172
+ request: (templateId, version, acknowledgedHigh, acknowledgedUnknown = false) => {
173
+ const revision = templateRevision(db, templateId, version);
174
+ if (getTemplate(db, templateId) === undefined || revision === undefined)
175
+ return {
176
+ ok: false,
177
+ reason: "not-found",
178
+ message: "That version of the template doesn't exist. Reload the template.",
179
+ };
180
+ const row = sampleRow(db, templateId);
181
+ if (row?.state === "making" && row.version === version)
182
+ return { ok: true, value: view(row) };
183
+ const quote = quoteDocument(deps.catalogue(), revision.document);
184
+ // The person's own press: the price they saw is the limit, unknown prices included.
185
+ if (costChanged(quote, acknowledgedHigh, acknowledgedUnknown))
186
+ return {
187
+ ok: false,
188
+ reason: "cost-changed",
189
+ message: costChangedMessage(quote, "the sample"),
190
+ current: quote,
191
+ };
192
+ pressed.add(templateId);
193
+ return { ok: true, value: queue(templateId, version, quote.high, null) };
194
+ },
195
+ state: (templateId) => current(templateId),
196
+ designChanged: (designId) => {
197
+ const row = sampleRowOfDesign(db, designId);
198
+ if (row === undefined)
199
+ return;
200
+ const settled = settle(row);
201
+ if (settled !== row)
202
+ emit(row.templateId);
203
+ start(row.templateId);
204
+ },
205
+ recover: () => {
206
+ for (const id of db
207
+ .prepare("SELECT template_id FROM design_template_samples WHERE state='making'")
208
+ .all()
209
+ .map((row) => String(row.template_id))) {
210
+ const row = sampleRow(db, id);
211
+ if (row !== undefined)
212
+ settle(row);
213
+ }
214
+ for (const id of queuedTemplates(db))
215
+ start(id);
216
+ },
217
+ };
218
+ function current(templateId) {
219
+ const row = sampleRow(db, templateId);
220
+ return row === undefined ? null : view(settle(row));
221
+ }
222
+ }
@@ -0,0 +1,87 @@
1
+ import { z } from "zod";
2
+ import { documentSettingsSchema } from "../document/theme-schema.js";
3
+ import { blockRoles, blocksMax, blockTextMax, castPerDesignMax, daysBeforeMax, designFormats, layoutPresets, promptMax, scrims, } from "./model.js";
4
+ // A designed piece's settings, the same for a one-off design and a design template. Pure and
5
+ // browser-safe, so the web page validates with the server's own schema. Text may hold fields
6
+ // ({{Event}}), filled from the design's values when it is drawn.
7
+ const fontId = z.string().regex(/^(?:default|(?:system|uploaded)-[a-f0-9]{64})$/);
8
+ const hexColor = z.string().regex(/^#[0-9A-Fa-f]{6}$/);
9
+ const unit = z.number().min(0).max(1);
10
+ export const designPictureSchema = z
11
+ .object({
12
+ // "generate": the AI draws the artwork from `prompt`. "picture": the design's own picture
13
+ // (an upload or an edit's result) is the artwork as it is.
14
+ source: z.enum(["generate", "picture"]),
15
+ prompt: z.string().max(promptMax),
16
+ // A template's fixed picture, kept in image_blobs.
17
+ sha256: z
18
+ .string()
19
+ .regex(/^[a-f0-9]{64}$/)
20
+ .optional(),
21
+ castMemberIds: z.array(z.string().min(1).max(64)).max(castPerDesignMax),
22
+ provider: z.string().min(1).max(64),
23
+ model: z.string().min(1).max(200),
24
+ // The AI draws the text into the artwork itself; Slopify's text layer is then off.
25
+ aiText: z.boolean(),
26
+ })
27
+ .strict();
28
+ export const textBlockSchema = z
29
+ .object({
30
+ id: z.string().min(1).max(64),
31
+ role: z.enum(blockRoles),
32
+ text: z.string().max(blockTextMax),
33
+ fontId,
34
+ color: hexColor,
35
+ })
36
+ .strict();
37
+ export const designTextSchema = z
38
+ .object({
39
+ preset: z.enum(layoutPresets),
40
+ scrim: z.enum(scrims),
41
+ blocks: z.array(textBlockSchema).max(blocksMax),
42
+ })
43
+ .strict()
44
+ .refine((text) => new Set(text.blocks.map((block) => block.id)).size === text.blocks.length, {
45
+ message: "Two text blocks share an id.",
46
+ });
47
+ export const designFormatSchema = z
48
+ .object({ format: z.enum(designFormats), focus: z.object({ x: unit, y: unit }).strict() })
49
+ .strict();
50
+ export const brochureSchema = z
51
+ .object({
52
+ settings: documentSettingsSchema,
53
+ title: z.string().min(1).max(200),
54
+ // The inner pages, as markdown with fields. Written by the person, not the AI, in v1.
55
+ body: z.string().min(1).max(100_000),
56
+ })
57
+ .strict();
58
+ export const designScheduleSchema = z
59
+ .object({
60
+ // The field holding each row's date, typed YYYY-MM-DD.
61
+ dateField: z.string().min(1).max(100),
62
+ daysBefore: z.number().int().min(0).max(daysBeforeMax),
63
+ time: z.string().regex(/^(?:[01]\d|2[0-3]):[0-5]\d$/),
64
+ })
65
+ .strict();
66
+ export const designDocumentSchema = z
67
+ .object({
68
+ version: z.literal(1),
69
+ picture: designPictureSchema,
70
+ text: designTextSchema,
71
+ formats: z
72
+ .array(designFormatSchema)
73
+ .min(1)
74
+ .max(designFormats.length)
75
+ .refine((formats) => new Set(formats.map((one) => one.format)).size === formats.length, {
76
+ message: "A format is listed twice.",
77
+ }),
78
+ brochure: brochureSchema.optional(),
79
+ schedule: designScheduleSchema.optional(),
80
+ // Example values the template editor's sample is drawn with.
81
+ samples: z.record(z.string().min(1).max(100), z.string().max(2000)),
82
+ })
83
+ .strict()
84
+ .refine((doc) => doc.formats.some((one) => one.format === "brochure") === (doc.brochure !== undefined), {
85
+ message: "A brochure needs its pages: add the brochure's title and text under Formats, or untick Brochure.",
86
+ path: ["brochure"],
87
+ });
@@ -0,0 +1,44 @@
1
+ import { detectSlots } from "../admission/substitute.js";
2
+ // The design template editor's sections, and which ones a Save changed. Pure and browser-safe:
3
+ // the page shows the cost on Save only when the Picture changed, because that is the only
4
+ // section whose sample is paid (one artwork); text, formats and the schedule redraw for free.
5
+ export const designSections = [
6
+ { id: "picture", label: "Picture" },
7
+ { id: "text", label: "Text layout" },
8
+ { id: "formats", label: "Formats" },
9
+ { id: "schedule", label: "Schedule" },
10
+ ];
11
+ // Every key of the document belongs to one section; a new key must be added here, which the
12
+ // test that walks the schema's keys enforces.
13
+ export const sectionOfKey = {
14
+ version: null,
15
+ picture: "picture",
16
+ text: "text",
17
+ samples: "text",
18
+ formats: "formats",
19
+ brochure: "formats",
20
+ schedule: "schedule",
21
+ };
22
+ const same = (a, b) => JSON.stringify(a) === JSON.stringify(b);
23
+ export function designAffectedSections(before, after) {
24
+ const changed = new Set();
25
+ for (const key of Object.keys(sectionOfKey)) {
26
+ const section = sectionOfKey[key];
27
+ if (section !== null && !same(before[key], after[key]))
28
+ changed.add(section);
29
+ }
30
+ // The artwork's prompt is filled from the sample values, so a changed sample that the
31
+ // prompt uses changes the picture too.
32
+ if (!same(before.samples, after.samples) && promptUsesChangedSample(before, after))
33
+ changed.add("picture");
34
+ return designSections.map((section) => section.id).filter((id) => changed.has(id));
35
+ }
36
+ function promptUsesChangedSample(before, after) {
37
+ if (after.picture.source !== "generate")
38
+ return false;
39
+ return detectSlots(after.picture.prompt).names.some((name) => before.samples[name] !== after.samples[name]);
40
+ }
41
+ // Whether a Save with these changes makes a paid picture for the sample.
42
+ export function sampleNeedsPicture(affected) {
43
+ return affected.includes("picture");
44
+ }