@gentbajko/slopify 3.25.8 → 4.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (604) hide show
  1. package/dist/adapters/account-limits.js +30 -0
  2. package/dist/adapters/alignment/cache.js +3 -5
  3. package/dist/adapters/alignment/ctc.js +1 -2
  4. package/dist/adapters/alignment/lock.js +4 -3
  5. package/dist/adapters/alignment/multilingual.js +0 -3
  6. package/dist/adapters/alignment/prefetch.js +2 -1
  7. package/dist/adapters/alignment/worker.js +2 -1
  8. package/dist/adapters/explain.js +0 -3
  9. package/dist/adapters/ffmpeg.js +19 -1
  10. package/dist/adapters/host-cli/index.js +19 -3
  11. package/dist/adapters/http-failure.js +53 -0
  12. package/dist/adapters/image/codex.js +36 -4
  13. package/dist/adapters/image/fal.js +17 -50
  14. package/dist/adapters/image/google.js +31 -55
  15. package/dist/adapters/image/openai.js +20 -47
  16. package/dist/adapters/image/reference.js +36 -0
  17. package/dist/adapters/image/replicate.js +18 -47
  18. package/dist/adapters/interest/search-interest.js +113 -0
  19. package/dist/adapters/key-probes.js +53 -2
  20. package/dist/adapters/llm/antigravity.js +4 -0
  21. package/dist/adapters/llm/assistant-cli.js +388 -0
  22. package/dist/adapters/llm/child-session.js +83 -0
  23. package/dist/adapters/llm/claude-code-models.js +51 -98
  24. package/dist/adapters/llm/claude-code.js +13 -2
  25. package/dist/adapters/llm/codex-limits.js +27 -68
  26. package/dist/adapters/llm/codex-server-models.js +61 -109
  27. package/dist/adapters/llm/codex.js +12 -1
  28. package/dist/adapters/llm/openrouter.js +10 -47
  29. package/dist/adapters/llm/run-cli.js +6 -22
  30. package/dist/adapters/llm/sse-lines.js +5 -1
  31. package/dist/adapters/model-access.js +37 -0
  32. package/dist/adapters/tts/cartesia.js +15 -46
  33. package/dist/adapters/tts/elevenlabs.js +20 -52
  34. package/dist/adapters/tts/gemini.js +19 -40
  35. package/dist/adapters/tts/inworld-async.js +4 -1
  36. package/dist/adapters/tts/inworld.js +3 -0
  37. package/dist/adapters/tts/openai.js +20 -50
  38. package/dist/assets/models-lifecycle.yaml +6 -0
  39. package/dist/assistant-wiring.js +60 -0
  40. package/dist/autopilot-timers.js +59 -0
  41. package/dist/catalog/lifecycle.js +52 -0
  42. package/dist/catalog/propose.js +204 -0
  43. package/dist/catalog/registry.js +3 -1
  44. package/dist/catalog/schema.js +2 -0
  45. package/dist/catalog/store.js +39 -0
  46. package/dist/edge/autostart/native.js +3 -5
  47. package/dist/edge/autostart/prompt.js +2 -1
  48. package/dist/edge/autostart/service.js +3 -4
  49. package/dist/edge/cli-args.js +3 -4
  50. package/dist/edge/cli.js +2 -1
  51. package/dist/edge/docker-install/apply.js +11 -11
  52. package/dist/edge/docker-install/run.js +2 -1
  53. package/dist/edge/docker-install/state.js +4 -3
  54. package/dist/edge/docker-install/tree.js +2 -4
  55. package/dist/edge/docker-install/volume.js +3 -2
  56. package/dist/edge/docker.js +5 -4
  57. package/dist/edge/events/hub.js +5 -5
  58. package/dist/edge/http/actions.js +4 -25
  59. package/dist/edge/http/actor.js +5 -3
  60. package/dist/edge/http/app.js +85 -10
  61. package/dist/edge/http/assistant.js +127 -0
  62. package/dist/edge/http/audio-preview.js +4 -11
  63. package/dist/edge/http/auditions.js +1 -1
  64. package/dist/edge/http/autostart.js +5 -5
  65. package/dist/edge/http/backups.js +1 -3
  66. package/dist/edge/http/channel-memory.js +7 -20
  67. package/dist/edge/http/channels.js +13 -26
  68. package/dist/edge/http/checkpoints.js +33 -17
  69. package/dist/edge/http/design-templates.js +167 -0
  70. package/dist/edge/http/designs.js +178 -0
  71. package/dist/edge/http/diagnostics.js +5 -1
  72. package/dist/edge/http/document-themes.js +2 -11
  73. package/dist/edge/http/draft-files.js +1 -3
  74. package/dist/edge/http/draft-problem.js +4 -9
  75. package/dist/edge/http/drafts.js +3 -3
  76. package/dist/edge/http/entries.js +6 -9
  77. package/dist/edge/http/extension-auth.js +27 -0
  78. package/dist/edge/http/external-reviews.js +12 -11
  79. package/dist/edge/http/files.js +5 -11
  80. package/dist/edge/http/folder-location.js +1 -4
  81. package/dist/edge/http/fonts.js +0 -7
  82. package/dist/edge/http/home.js +38 -13
  83. package/dist/edge/http/host-cli.js +18 -8
  84. package/dist/edge/http/ids.js +8 -0
  85. package/dist/edge/http/keywords.js +35 -0
  86. package/dist/edge/http/model-checks.js +132 -0
  87. package/dist/edge/http/narration-peaks.js +5 -13
  88. package/dist/edge/http/onboarding.js +1 -14
  89. package/dist/edge/http/open-folder.js +5 -10
  90. package/dist/edge/http/outputs.js +4 -12
  91. package/dist/edge/http/patch-notes.js +4 -10
  92. package/dist/edge/http/planning.js +2 -6
  93. package/dist/edge/http/problem.js +2 -4
  94. package/dist/edge/http/project-create.js +1 -0
  95. package/dist/edge/http/project-templates.js +29 -17
  96. package/dist/edge/http/projects.js +2 -12
  97. package/dist/edge/http/prompts.js +7 -14
  98. package/dist/edge/http/pronunciations.js +1 -2
  99. package/dist/edge/http/providers.js +39 -24
  100. package/dist/edge/http/refusal.js +29 -0
  101. package/dist/edge/http/remote-access.js +40 -0
  102. package/dist/edge/http/remote-guard.js +71 -0
  103. package/dist/edge/http/request-guard.js +91 -0
  104. package/dist/edge/http/reviews.js +11 -24
  105. package/dist/edge/http/revision-files.js +5 -10
  106. package/dist/edge/http/revisions.js +10 -15
  107. package/dist/edge/http/run-cost.js +1 -2
  108. package/dist/edge/http/same-page.js +80 -0
  109. package/dist/edge/http/schedules.js +59 -32
  110. package/dist/edge/http/settings.js +22 -13
  111. package/dist/edge/http/social.js +282 -0
  112. package/dist/edge/http/staging.js +2 -4
  113. package/dist/edge/http/storage-files.js +5 -7
  114. package/dist/edge/http/storage.js +23 -23
  115. package/dist/edge/http/studio.js +23 -50
  116. package/dist/edge/http/style-preview.js +1 -4
  117. package/dist/edge/http/subtitles.js +3 -17
  118. package/dist/edge/http/template-studio.js +433 -0
  119. package/dist/edge/http/trash.js +10 -11
  120. package/dist/edge/http/tutorial.js +1 -2
  121. package/dist/edge/http/tutorials.js +5 -11
  122. package/dist/edge/http/update.js +7 -13
  123. package/dist/edge/http/waveform.js +3 -6
  124. package/dist/edge/http/youtube-edits.js +2 -3
  125. package/dist/edge/mcp/assistant.js +279 -0
  126. package/dist/edge/mcp/attribution.js +8 -2
  127. package/dist/edge/mcp/call-route.js +11 -2
  128. package/dist/edge/mcp/excluded-routes.js +25 -0
  129. package/dist/edge/mcp/http.js +11 -2
  130. package/dist/edge/mcp/instructions.js +5 -3
  131. package/dist/edge/mcp/library-tools.js +1 -0
  132. package/dist/edge/mcp/route-tool.js +35 -15
  133. package/dist/edge/mcp/server.js +13 -8
  134. package/dist/edge/mcp/tools-channels.js +2 -0
  135. package/dist/edge/mcp/tools-designs.js +165 -0
  136. package/dist/edge/mcp/tools-library.js +14 -2
  137. package/dist/edge/mcp/tools-planning.js +23 -4
  138. package/dist/edge/mcp/tools-projects.js +28 -1
  139. package/dist/edge/mcp/tools-runs.js +14 -1
  140. package/dist/edge/mcp/tools-schedules.js +28 -4
  141. package/dist/edge/mcp/tools-setup.js +61 -3
  142. package/dist/edge/mcp/tools-social.js +46 -0
  143. package/dist/edge/mcp/tools-storage.js +8 -0
  144. package/dist/edge/mcp/tools-template-studio.js +166 -0
  145. package/dist/edge/mcp/tools.js +6 -0
  146. package/dist/edge/remote-access/cli.js +71 -0
  147. package/dist/edge/remote-access/model.js +32 -0
  148. package/dist/edge/remote-access/rules.js +207 -0
  149. package/dist/edge/remote-access/service.js +203 -0
  150. package/dist/edge/remote-access/store.js +44 -0
  151. package/dist/edge/remote-access/views.js +43 -0
  152. package/dist/extension/slopify-studio-chrome.zip +0 -0
  153. package/dist/extension/slopify-studio-firefox.zip +0 -0
  154. package/dist/host-cli/install.js +2 -2
  155. package/dist/host-cli/open-folder.js +2 -2
  156. package/dist/host-cli/paths.js +2 -4
  157. package/dist/host-cli/server.js +4 -4
  158. package/dist/host-cli/service.js +3 -3
  159. package/dist/host-cli/status.js +7 -7
  160. package/dist/kernel/db/migrations/0063-model-checks.sql +20 -0
  161. package/dist/kernel/db/migrations/0064-template-studio.sql +46 -0
  162. package/dist/kernel/db/migrations/0065-promise-and-assistant-reviews.sql +58 -0
  163. package/dist/kernel/db/migrations/0066-assistant.sql +44 -0
  164. package/dist/kernel/db/migrations/0067-autopilot.sql +6 -0
  165. package/dist/kernel/db/migrations/0068-topic-scores.sql +17 -0
  166. package/dist/kernel/db/migrations/0070-designs.sql +53 -0
  167. package/dist/kernel/db/migrations/0071-design-usage.sql +33 -0
  168. package/dist/kernel/db/migrations/0072-design-templates.sql +33 -0
  169. package/dist/kernel/db/migrations/0073-schedule-design-kind.sql +5 -0
  170. package/dist/kernel/db/migrations/0074-template-bakeoffs.sql +44 -0
  171. package/dist/kernel/db/migrations/0075-social-posts.sql +25 -0
  172. package/dist/kernel/db/migrations/0076-channel-social.sql +5 -0
  173. package/dist/kernel/db/migrations/0077-post-prompts.sql +17 -0
  174. package/dist/kernel/db/tx.js +2 -4
  175. package/dist/kernel/errors.js +27 -1
  176. package/dist/kernel/json-answer.js +15 -0
  177. package/dist/kernel/lock.js +4 -9
  178. package/dist/kernel/paths.js +8 -0
  179. package/dist/kernel/ports/assistant-cli.js +4 -0
  180. package/dist/kernel/ports/host-cli.js +3 -0
  181. package/dist/kernel/ports/interest.js +4 -0
  182. package/dist/kernel/ports/languages.js +0 -3
  183. package/dist/kernel/ports/model.js +2 -0
  184. package/dist/kernel/runner/attempt.js +3 -1
  185. package/dist/kernel/runner/audition.js +3 -1
  186. package/dist/kernel/runner/checkpoint-authority.js +2 -1
  187. package/dist/kernel/runner/index.js +8 -10
  188. package/dist/kernel/runner/model-access.js +40 -0
  189. package/dist/kernel/runner/model-check.js +153 -0
  190. package/dist/kernel/runner/providers.js +15 -10
  191. package/dist/kernel/runner/standalone.js +9 -5
  192. package/dist/main.js +334 -19
  193. package/dist/model-checks.js +166 -0
  194. package/dist/patch-notes/3.25.9.md +5 -0
  195. package/dist/patch-notes/4.0.0.md +51 -0
  196. package/dist/patch-notes/index.json +12 -0
  197. package/dist/slices/admission/heads.js +2 -1
  198. package/dist/slices/admission/repo.js +2 -1
  199. package/dist/slices/admission/rules.js +43 -8
  200. package/dist/slices/admission/schema.js +8 -0
  201. package/dist/slices/admission/stage-writes.js +0 -3
  202. package/dist/slices/admission/start.js +48 -31
  203. package/dist/slices/admission/substitute.js +7 -4
  204. package/dist/slices/agents/attention-posts.js +31 -0
  205. package/dist/slices/agents/attention-templates.js +33 -0
  206. package/dist/slices/agents/attention.js +10 -1
  207. package/dist/slices/agents/connection.js +2 -6
  208. package/dist/slices/agents/model.js +6 -3
  209. package/dist/slices/agents/repo.js +15 -7
  210. package/dist/slices/article/continuation.js +0 -63
  211. package/dist/slices/article/plain.js +3 -2
  212. package/dist/slices/article/segments.js +2 -62
  213. package/dist/slices/assistant/access.js +1 -0
  214. package/dist/slices/assistant/asks.js +80 -0
  215. package/dist/slices/assistant/cost.js +54 -0
  216. package/dist/slices/assistant/gate.js +222 -0
  217. package/dist/slices/assistant/model.js +79 -0
  218. package/dist/slices/assistant/names.js +23 -0
  219. package/dist/slices/assistant/prompt.js +74 -0
  220. package/dist/slices/assistant/repo.js +74 -0
  221. package/dist/slices/assistant/tokens.js +18 -0
  222. package/dist/slices/assistant/turn.js +342 -0
  223. package/dist/slices/autopilot/buffer.js +76 -0
  224. package/dist/slices/autopilot/model.js +14 -0
  225. package/dist/slices/autopilot/notify.js +27 -0
  226. package/dist/slices/autopilot/repo.js +34 -0
  227. package/dist/slices/autopilot/signals.js +194 -0
  228. package/dist/slices/autopilot/warnings.js +85 -0
  229. package/dist/slices/autopilot/weekly.js +100 -0
  230. package/dist/slices/backups/files.js +2 -6
  231. package/dist/slices/backups/repo.js +3 -15
  232. package/dist/slices/backups/service.js +4 -3
  233. package/dist/slices/channels/cast-images.js +2 -1
  234. package/dist/slices/channels/model.js +0 -1
  235. package/dist/slices/channels/rebrand.js +0 -3
  236. package/dist/slices/channels/repo.js +4 -0
  237. package/dist/slices/channels/schema.js +6 -0
  238. package/dist/slices/channels/service.js +10 -0
  239. package/dist/slices/channels/videos.js +2 -14
  240. package/dist/slices/checkpoints/index.js +0 -1
  241. package/dist/slices/control/providers.js +0 -26
  242. package/dist/slices/designs/artwork.js +66 -0
  243. package/dist/slices/designs/budget.js +23 -0
  244. package/dist/slices/designs/calls.js +124 -0
  245. package/dist/slices/designs/check-context.js +38 -0
  246. package/dist/slices/designs/check-text.js +43 -0
  247. package/dist/slices/designs/fields.js +88 -0
  248. package/dist/slices/designs/frames.js +34 -0
  249. package/dist/slices/designs/gate.js +128 -0
  250. package/dist/slices/designs/layout.js +293 -0
  251. package/dist/slices/designs/model.js +35 -0
  252. package/dist/slices/designs/pdf-job.js +23 -0
  253. package/dist/slices/designs/pdf-pages.js +30 -0
  254. package/dist/slices/designs/pdf-runner.js +68 -0
  255. package/dist/slices/designs/pdf-worker.js +24 -0
  256. package/dist/slices/designs/picture-size.js +79 -0
  257. package/dist/slices/designs/pictures.js +55 -0
  258. package/dist/slices/designs/quote.js +24 -0
  259. package/dist/slices/designs/render-args.js +36 -0
  260. package/dist/slices/designs/render-input.js +59 -0
  261. package/dist/slices/designs/render.js +215 -0
  262. package/dist/slices/designs/repo.js +101 -0
  263. package/dist/slices/designs/requests.js +80 -0
  264. package/dist/slices/designs/runner.js +239 -0
  265. package/dist/slices/designs/sample-repo.js +91 -0
  266. package/dist/slices/designs/sample.js +222 -0
  267. package/dist/slices/designs/schema.js +87 -0
  268. package/dist/slices/designs/sections.js +44 -0
  269. package/dist/slices/designs/service.js +233 -0
  270. package/dist/slices/designs/steps-repo.js +102 -0
  271. package/dist/slices/designs/studio-kind.js +39 -0
  272. package/dist/slices/designs/template-checks.js +44 -0
  273. package/dist/slices/designs/template-make.js +118 -0
  274. package/dist/slices/designs/templates.js +218 -0
  275. package/dist/slices/document/fonts.js +2 -1
  276. package/dist/slices/episodes/summarize.js +5 -1
  277. package/dist/slices/estimate/index.js +45 -7
  278. package/dist/slices/fonts/catalog.js +3 -2
  279. package/dist/slices/fonts/discovery.js +3 -2
  280. package/dist/slices/fonts/files.js +2 -6
  281. package/dist/slices/fonts/index.js +0 -1
  282. package/dist/slices/fonts/sfnt.js +45 -10
  283. package/dist/slices/images/appearance.js +6 -15
  284. package/dist/slices/images/scenes.js +100 -27
  285. package/dist/slices/keywords/digest.js +84 -0
  286. package/dist/slices/keywords/facts.js +92 -0
  287. package/dist/slices/keywords/fill.js +80 -0
  288. package/dist/slices/keywords/model.js +75 -0
  289. package/dist/slices/keywords/rules.js +187 -0
  290. package/dist/slices/keywords/schema.js +28 -0
  291. package/dist/slices/keywords/specs.js +260 -0
  292. package/dist/slices/keywords/values.js +74 -0
  293. package/dist/slices/library/lint.js +29 -12
  294. package/dist/slices/library/model.js +2 -0
  295. package/dist/slices/library/photorealistic.js +4 -12
  296. package/dist/slices/library/rename.js +9 -7
  297. package/dist/slices/library/save.js +14 -10
  298. package/dist/slices/library/slots.js +37 -11
  299. package/dist/slices/library/transfer.js +2 -1
  300. package/dist/slices/library/used-by.js +25 -10
  301. package/dist/slices/loudness/loudnorm.js +51 -11
  302. package/dist/slices/model-checks/cost.js +61 -0
  303. package/dist/slices/model-checks/listing.js +124 -0
  304. package/dist/slices/model-checks/model.js +20 -0
  305. package/dist/slices/model-checks/record.js +32 -0
  306. package/dist/slices/model-checks/repo.js +120 -0
  307. package/dist/slices/model-checks/rules.js +112 -0
  308. package/dist/slices/model-checks/run.js +143 -0
  309. package/dist/slices/model-checks/service.js +198 -0
  310. package/dist/slices/model-upkeep/switch.js +70 -25
  311. package/dist/slices/model-upkeep/usage.js +26 -11
  312. package/dist/slices/narration/breaths.js +2 -1
  313. package/dist/slices/narration/clean-pieces.js +2 -1
  314. package/dist/slices/narration/concat.js +2 -1
  315. package/dist/slices/narration/peaks.js +3 -4
  316. package/dist/slices/narration/pronunciation-chunks.js +4 -3
  317. package/dist/slices/narration/pronunciation.js +3 -2
  318. package/dist/slices/narration/steering.js +4 -3
  319. package/dist/slices/notifications/notifier.js +4 -3
  320. package/dist/slices/notifications/rules.js +5 -2
  321. package/dist/slices/notifications/settings.js +4 -12
  322. package/dist/slices/notifications/subject.js +2 -1
  323. package/dist/slices/onboarding/copy.js +2 -1
  324. package/dist/slices/onboarding/first-run.js +3 -2
  325. package/dist/slices/onboarding/full-video.js +3 -2
  326. package/dist/slices/onboarding/install.js +4 -3
  327. package/dist/slices/onboarding/model.js +0 -16
  328. package/dist/slices/onboarding/packs.js +22 -22
  329. package/dist/slices/onboarding/quick-short.js +2 -1
  330. package/dist/slices/onboarding/sample.js +0 -1
  331. package/dist/slices/onboarding/state.js +2 -11
  332. package/dist/slices/onboarding/template.js +2 -1
  333. package/dist/slices/patch-notes/library.js +3 -2
  334. package/dist/slices/play-drafts/convert.js +4 -0
  335. package/dist/slices/play-drafts/review-inputs.js +8 -2
  336. package/dist/slices/play-drafts/review.js +5 -4
  337. package/dist/slices/play-drafts/schema.js +4 -0
  338. package/dist/slices/project-templates/from-project.js +9 -2
  339. package/dist/slices/project-templates/repo.js +6 -1
  340. package/dist/slices/project-templates/setup.js +8 -0
  341. package/dist/slices/project-templates/stand-ins.js +22 -0
  342. package/dist/slices/rebuild/activity.js +2 -0
  343. package/dist/slices/rebuild/admission-repo.js +3 -2
  344. package/dist/slices/rebuild/narration-history.js +4 -5
  345. package/dist/slices/rebuild/narration-retry.js +2 -1
  346. package/dist/slices/rebuild/preview-details.js +27 -9
  347. package/dist/slices/rebuild/preview-plan.js +3 -8
  348. package/dist/slices/rebuild/recipe-build.js +8 -3
  349. package/dist/slices/rebuild/recipe-describe.js +3 -3
  350. package/dist/slices/rebuild/recipe-digest.js +58 -0
  351. package/dist/slices/rebuild/recipe-input-schema.js +10 -0
  352. package/dist/slices/rebuild/recipe-keywords.js +71 -0
  353. package/dist/slices/rebuild/recipe-legacy.js +5 -1
  354. package/dist/slices/rebuild/recipe-model.js +4 -0
  355. package/dist/slices/rebuild/recipe-narration-text.js +2 -1
  356. package/dist/slices/rebuild/recipe-preparation.js +3 -3
  357. package/dist/slices/rebuild/recipe-reference.js +8 -4
  358. package/dist/slices/rebuild/recipe-reviews.js +27 -7
  359. package/dist/slices/rebuild/recipe-save.js +6 -3
  360. package/dist/slices/rebuild/recipe-scenes.js +40 -11
  361. package/dist/slices/rebuild/recipe-shorts.js +8 -8
  362. package/dist/slices/rebuild/recipe-social.js +46 -0
  363. package/dist/slices/rebuild/recipe-text.js +64 -20
  364. package/dist/slices/rebuild/recipe-validation.js +98 -28
  365. package/dist/slices/rebuild/recipe-visual.js +55 -16
  366. package/dist/slices/rebuild/recipe-work.js +15 -3
  367. package/dist/slices/rebuild/recipe-youtube.js +4 -3
  368. package/dist/slices/rebuild/recovery-selection.js +3 -1
  369. package/dist/slices/rebuild/recovery.js +3 -2
  370. package/dist/slices/rebuild/repo.js +4 -3
  371. package/dist/slices/rebuild/review-promise.js +25 -0
  372. package/dist/slices/rebuild/review-redo.js +2 -1
  373. package/dist/slices/rebuild/runtime-admission.js +4 -3
  374. package/dist/slices/rebuild/runtime-agent-review.js +11 -5
  375. package/dist/slices/rebuild/runtime-animate.js +9 -8
  376. package/dist/slices/rebuild/runtime-article.js +3 -2
  377. package/dist/slices/rebuild/runtime-checkpoints.js +5 -4
  378. package/dist/slices/rebuild/runtime-document.js +8 -11
  379. package/dist/slices/rebuild/runtime-export-bed.js +6 -11
  380. package/dist/slices/rebuild/runtime-export-edit.js +6 -15
  381. package/dist/slices/rebuild/runtime-export-inputs.js +7 -7
  382. package/dist/slices/rebuild/runtime-export-short.js +8 -4
  383. package/dist/slices/rebuild/runtime-export.js +34 -12
  384. package/dist/slices/rebuild/runtime-figure-card.js +2 -1
  385. package/dist/slices/rebuild/runtime-image.js +4 -9
  386. package/dist/slices/rebuild/runtime-instructions.js +2 -0
  387. package/dist/slices/rebuild/runtime-keywords.js +59 -0
  388. package/dist/slices/rebuild/runtime-lines.js +2 -11
  389. package/dist/slices/rebuild/runtime-local.js +14 -20
  390. package/dist/slices/rebuild/runtime-narration-publication.js +2 -5
  391. package/dist/slices/rebuild/runtime-narration-reuse.js +9 -13
  392. package/dist/slices/rebuild/runtime-narration-text.js +10 -15
  393. package/dist/slices/rebuild/runtime-pictures.js +2 -4
  394. package/dist/slices/rebuild/runtime-piece-label.js +10 -6
  395. package/dist/slices/rebuild/runtime-plan.js +31 -5
  396. package/dist/slices/rebuild/runtime-provider.js +60 -19
  397. package/dist/slices/rebuild/runtime-publication.js +10 -9
  398. package/dist/slices/rebuild/runtime-review.js +33 -28
  399. package/dist/slices/rebuild/runtime-run.js +4 -1
  400. package/dist/slices/rebuild/runtime-shorts.js +34 -50
  401. package/dist/slices/rebuild/runtime-store.js +3 -8
  402. package/dist/slices/rebuild/runtime-subtitles.js +16 -14
  403. package/dist/slices/rebuild/runtime-voices.js +8 -15
  404. package/dist/slices/rebuild/runtime-youtube.js +11 -29
  405. package/dist/slices/rebuild/transition-repo.js +6 -5
  406. package/dist/slices/rebuild/work-records.js +6 -0
  407. package/dist/slices/reruns/cascade.js +1 -2
  408. package/dist/slices/reruns/index.js +6 -9
  409. package/dist/slices/reviews/external-model.js +3 -0
  410. package/dist/slices/reviews/external-repo.js +2 -1
  411. package/dist/slices/reviews/external-rows.js +2 -3
  412. package/dist/slices/reviews/model.js +26 -8
  413. package/dist/slices/reviews/repo.js +2 -1
  414. package/dist/slices/reviews/rules.js +1 -0
  415. package/dist/slices/reviews/view.js +5 -2
  416. package/dist/slices/revisions/downloads.js +4 -5
  417. package/dist/slices/revisions/mutation-assets.js +7 -4
  418. package/dist/slices/revisions/mutation-cues.js +2 -5
  419. package/dist/slices/revisions/mutations.js +0 -1
  420. package/dist/slices/revisions/repo.js +3 -1
  421. package/dist/slices/revisions/schema.js +1 -0
  422. package/dist/slices/revisions/view.js +20 -0
  423. package/dist/slices/run-cost/limits.js +5 -2
  424. package/dist/slices/run-cost/meter.js +12 -5
  425. package/dist/slices/run-cost/panel.js +8 -74
  426. package/dist/slices/run-cost/plan-readings.js +127 -0
  427. package/dist/slices/run-cost/plan-use.js +169 -0
  428. package/dist/slices/run-cost/pricing.js +3 -3
  429. package/dist/slices/run-cost/week.js +32 -4
  430. package/dist/slices/schedules/agenda.js +11 -2
  431. package/dist/slices/schedules/by-date.js +42 -0
  432. package/dist/slices/schedules/calendar.js +5 -0
  433. package/dist/slices/schedules/design-agenda.js +36 -0
  434. package/dist/slices/schedules/design-check.js +41 -0
  435. package/dist/slices/schedules/design-run.js +126 -0
  436. package/dist/slices/schedules/next-run.js +62 -0
  437. package/dist/slices/schedules/prepare.js +18 -2
  438. package/dist/slices/schedules/repo.js +24 -16
  439. package/dist/slices/schedules/rescore.js +35 -0
  440. package/dist/slices/schedules/scheduler.js +13 -3
  441. package/dist/slices/schedules/schema.js +54 -3
  442. package/dist/slices/schedules/score.js +107 -0
  443. package/dist/slices/schedules/service.js +57 -24
  444. package/dist/slices/schedules/topic-list.js +15 -0
  445. package/dist/slices/schedules/topic-scores.js +104 -0
  446. package/dist/slices/schedules/topics.js +48 -36
  447. package/dist/slices/settings/cli-paths.js +4 -6
  448. package/dist/slices/settings/first-run.js +2 -11
  449. package/dist/slices/settings/model-reach.js +3 -17
  450. package/dist/slices/settings/model.js +10 -0
  451. package/dist/slices/settings/playback.js +33 -22
  452. package/dist/slices/settings/probe-read.js +16 -0
  453. package/dist/slices/settings/repo.js +33 -0
  454. package/dist/slices/settings/tutorial.js +2 -2
  455. package/dist/slices/settings/voices.js +4 -12
  456. package/dist/slices/shorts/pick.js +2 -15
  457. package/dist/slices/shorts/prompts.js +2 -13
  458. package/dist/slices/shorts/render.js +3 -0
  459. package/dist/slices/social/channel.js +44 -0
  460. package/dist/slices/social/defaults.js +17 -0
  461. package/dist/slices/social/due.js +36 -0
  462. package/dist/slices/social/extension.js +18 -0
  463. package/dist/slices/social/fit.js +115 -0
  464. package/dist/slices/social/limits.js +186 -0
  465. package/dist/slices/social/links.js +21 -0
  466. package/dist/slices/social/media.js +131 -0
  467. package/dist/slices/social/model.js +75 -0
  468. package/dist/slices/social/notice.js +19 -0
  469. package/dist/slices/social/pack.js +176 -0
  470. package/dist/slices/social/post-prompt.js +55 -0
  471. package/dist/slices/social/queue.js +31 -0
  472. package/dist/slices/social/repo.js +133 -0
  473. package/dist/slices/social/reshape.js +97 -0
  474. package/dist/slices/social/rules.js +5 -0
  475. package/dist/slices/social/schema.js +70 -0
  476. package/dist/slices/social/serve.js +51 -0
  477. package/dist/slices/social/service.js +128 -0
  478. package/dist/slices/social/settings.js +21 -0
  479. package/dist/slices/social/sources.js +114 -0
  480. package/dist/slices/social/state.js +110 -0
  481. package/dist/slices/social/text.js +149 -0
  482. package/dist/slices/storage/arrange.js +3 -1
  483. package/dist/slices/storage/assets.js +8 -10
  484. package/dist/slices/storage/backup-designs.js +74 -0
  485. package/dist/slices/storage/backup-export.js +8 -3
  486. package/dist/slices/storage/backup-format.js +10 -0
  487. package/dist/slices/storage/backup-import.js +20 -2
  488. package/dist/slices/storage/delete-project.js +6 -5
  489. package/dist/slices/storage/files-location.js +6 -8
  490. package/dist/slices/storage/layout.js +13 -2
  491. package/dist/slices/storage/model.js +3 -0
  492. package/dist/slices/storage/portable.js +16 -11
  493. package/dist/slices/storage/schema.js +2 -0
  494. package/dist/slices/storage/staging-refs.js +2 -1
  495. package/dist/slices/storage/staging.js +6 -9
  496. package/dist/slices/storage/trim.js +10 -3
  497. package/dist/slices/studio/calendar.js +4 -1
  498. package/dist/slices/studio/pick.js +2 -11
  499. package/dist/slices/studio/plan.js +6 -15
  500. package/dist/slices/studio/queue.js +3 -14
  501. package/dist/slices/studio/releases.js +26 -0
  502. package/dist/slices/studio/settings.js +14 -51
  503. package/dist/slices/studio/videos.js +12 -0
  504. package/dist/slices/style-preview/render.js +34 -13
  505. package/dist/slices/style-preview/schema.js +13 -0
  506. package/dist/slices/style-preview/service.js +2 -1
  507. package/dist/slices/style-preview/settings.js +33 -5
  508. package/dist/slices/telemetry/collector-client.js +2 -4
  509. package/dist/slices/telemetry/flush.js +2 -4
  510. package/dist/slices/telemetry/model.js +0 -7
  511. package/dist/slices/telemetry/record.js +10 -3
  512. package/dist/slices/template-studio/anatomy-view.js +118 -0
  513. package/dist/slices/template-studio/anatomy.js +73 -0
  514. package/dist/slices/template-studio/bakeoff-make.js +225 -0
  515. package/dist/slices/template-studio/bakeoff-repo.js +83 -0
  516. package/dist/slices/template-studio/bakeoff.js +333 -0
  517. package/dist/slices/template-studio/budget.js +103 -0
  518. package/dist/slices/template-studio/checks.js +273 -0
  519. package/dist/slices/template-studio/document-sample.js +56 -0
  520. package/dist/slices/template-studio/edit.js +15 -0
  521. package/dist/slices/template-studio/for-all.js +161 -0
  522. package/dist/slices/template-studio/free-video.js +31 -0
  523. package/dist/slices/template-studio/free.js +176 -0
  524. package/dist/slices/template-studio/gate.js +40 -0
  525. package/dist/slices/template-studio/hidden.js +20 -0
  526. package/dist/slices/template-studio/jobs.js +79 -0
  527. package/dist/slices/template-studio/loudness.js +111 -0
  528. package/dist/slices/template-studio/make.js +40 -0
  529. package/dist/slices/template-studio/model.js +54 -0
  530. package/dist/slices/template-studio/pack-import.js +253 -0
  531. package/dist/slices/template-studio/pack-samples.js +60 -0
  532. package/dist/slices/template-studio/pack.js +205 -0
  533. package/dist/slices/template-studio/paid-view.js +66 -0
  534. package/dist/slices/template-studio/paid.js +348 -0
  535. package/dist/slices/template-studio/quote.js +148 -0
  536. package/dist/slices/template-studio/ready.js +104 -0
  537. package/dist/slices/template-studio/repo.js +134 -0
  538. package/dist/slices/template-studio/reviews-path.js +58 -0
  539. package/dist/slices/template-studio/runner.js +84 -0
  540. package/dist/slices/template-studio/save.js +236 -0
  541. package/dist/slices/template-studio/say-it-as.js +248 -0
  542. package/dist/slices/template-studio/schedule-gate.js +34 -0
  543. package/dist/slices/template-studio/schedule-price.js +30 -0
  544. package/dist/slices/template-studio/schedule-sample.js +87 -0
  545. package/dist/slices/template-studio/schema.js +218 -0
  546. package/dist/slices/template-studio/scope.js +124 -0
  547. package/dist/slices/template-studio/sections.js +176 -0
  548. package/dist/slices/template-studio/service.js +173 -0
  549. package/dist/slices/template-studio/status.js +174 -0
  550. package/dist/slices/template-studio/topic.js +223 -0
  551. package/dist/slices/trash/designs.js +76 -0
  552. package/dist/slices/trash/model.js +13 -4
  553. package/dist/slices/trash/service.js +52 -2
  554. package/dist/slices/tutorials/library.js +3 -2
  555. package/dist/slices/video/ass-text.js +17 -0
  556. package/dist/slices/video/audio-export.js +2 -64
  557. package/dist/slices/video/encoding.js +160 -0
  558. package/dist/slices/video/ffmpeg.js +23 -21
  559. package/dist/slices/video/figure-card.js +1 -15
  560. package/dist/slices/video/slideshow.js +43 -18
  561. package/dist/slices/voices/palette.js +0 -3
  562. package/dist/slices/youtube/answer.js +2 -15
  563. package/dist/slices/youtube/edits-repo.js +2 -11
  564. package/dist/tutorials/Agents-MCP.md +2 -1
  565. package/dist/tutorials/Assistant.md +42 -0
  566. package/dist/tutorials/Channels.md +2 -0
  567. package/dist/tutorials/Home-and-Projects.md +19 -0
  568. package/dist/tutorials/Install.md +1 -0
  569. package/dist/tutorials/Keywords-Slopify-Fills.md +87 -0
  570. package/dist/tutorials/Models.md +33 -1
  571. package/dist/tutorials/Notifications.md +6 -0
  572. package/dist/tutorials/Play-Images.md +4 -0
  573. package/dist/tutorials/Posting-to-Social-Sites.md +121 -0
  574. package/dist/tutorials/Prompts.md +10 -6
  575. package/dist/tutorials/Remote-Access.md +63 -0
  576. package/dist/tutorials/Reviews-and-Checkpoints.md +13 -2
  577. package/dist/tutorials/Schedules.md +21 -1
  578. package/dist/tutorials/Settings-Reference.md +50 -8
  579. package/dist/tutorials/Template-Studio.md +125 -0
  580. package/dist/tutorials/Templates.md +3 -0
  581. package/dist/tutorials/_Sidebar.md +5 -0
  582. package/dist/updater/work-in-progress.js +42 -12
  583. package/dist/web/assets/index-CkAI_2E7.css +1 -0
  584. package/dist/web/assets/index-DcD0cdfo.js +334 -0
  585. package/dist/web/assets/{pdf-T7MCwjIv.js → pdf-TODRwF31.js} +1 -1
  586. package/dist/web/index.html +2 -2
  587. package/package.json +5 -1
  588. package/dist/slices/article/provided-entries.js +0 -66
  589. package/dist/slices/article/run.js +0 -145
  590. package/dist/slices/images/run.js +0 -234
  591. package/dist/slices/narration/legacy-plan.js +0 -58
  592. package/dist/slices/narration/run.js +0 -270
  593. package/dist/slices/rebuild/runtime-actions.js +0 -90
  594. package/dist/slices/research/run.js +0 -224
  595. package/dist/slices/subtitles/prepare.js +0 -194
  596. package/dist/slices/subtitles/transcript.js +0 -34
  597. package/dist/slices/thumbnail/run.js +0 -219
  598. package/dist/slices/video/audio-inputs.js +0 -33
  599. package/dist/slices/video/reuse-audio.js +0 -62
  600. package/dist/slices/video/run.js +0 -78
  601. package/dist/slices/video/write-export.js +0 -158
  602. package/dist/slices/video/write-subtitles.js +0 -59
  603. package/dist/web/assets/index-BadsoORk.css +0 -1
  604. package/dist/web/assets/index-D8LqNsCl.js +0 -326
@@ -0,0 +1,223 @@
1
+ import { createHash } from "node:crypto";
2
+ import { z } from "zod";
3
+ import { transact } from "../../kernel/db/tx.js";
4
+ import { templateById } from "../project-templates/repo.js";
5
+ import { readJsonSetting, writeSetting } from "../settings/repo.js";
6
+ import { budgetWords } from "./budget.js";
7
+ import { enqueueJob, queuedJob, runningJob } from "./jobs.js";
8
+ import { quoteSample } from "./quote.js";
9
+ import { sampleOf, sampleVersions, setSampleVersion, upsertSample } from "./repo.js";
10
+ import { exceedsAcknowledged } from "./save.js";
11
+ import { withDependents } from "./sections.js";
12
+ import { defaultSampleTopic, templateSchedules } from "./service.js";
13
+ // The sample's topic and its full script, changed in the studio. Either remakes paid samples,
14
+ // so it is priced first and refused, with the new quote, at a higher price, unaccepted unknown
15
+ // prices or past today's budget, exactly as Save is.
16
+ const price = {
17
+ // The price the button showed for the change.
18
+ acknowledgedHigh: z.number().finite().min(0).max(10_000),
19
+ acknowledgedUnknown: z.boolean().optional(),
20
+ };
21
+ export const topicSchema = z
22
+ .object({
23
+ // A topic typed by the person, "next-queued" (the next topic a schedule of this template
24
+ // has queued) or "fixed" (back to the topic every template's sample starts on).
25
+ topic: z.string().trim().min(1).max(300),
26
+ // The same id sent again (a retry after a lost answer) answers what it did the first time.
27
+ mutationId: z.uuid().optional(),
28
+ ...price,
29
+ })
30
+ .strict()
31
+ .readonly();
32
+ export const fullScriptSchema = z
33
+ .object({ fullScript: z.boolean(), ...price })
34
+ .strict()
35
+ .readonly();
36
+ const money = (value) => `$${value.toFixed(2)}`;
37
+ // The topic a request names, with where it came from, or why there is none.
38
+ export function topicOf(deps, templateId, asked) {
39
+ if (asked === "fixed")
40
+ return { ok: true, topic: defaultSampleTopic(deps.db), source: "fixed" };
41
+ if (asked === "next-queued") {
42
+ const queued = templateSchedules(deps.db, templateId)
43
+ .flatMap((schedule) => schedule.items)
44
+ .find((item) => item.title.trim() !== "");
45
+ return queued === undefined
46
+ ? {
47
+ ok: false,
48
+ message: "No schedule of this template has a topic queued. Add topics in Calendar → Schedules, or type a topic for the sample.",
49
+ }
50
+ : { ok: true, topic: queued.title.trim(), source: "queued" };
51
+ }
52
+ return { ok: true, topic: asked, source: "fixed" };
53
+ }
54
+ async function change(deps, id, next, sections, ack, kind) {
55
+ const template = templateById(deps.db, id);
56
+ if (template === undefined)
57
+ return {
58
+ ok: false,
59
+ reason: "not-found",
60
+ message: "This template no longer exists; it may have been deleted. Go to Library → Templates to see your templates.",
61
+ };
62
+ const quoted = await quoteSample(deps, id, {
63
+ topic: next.topic,
64
+ fullScript: next.fullScript,
65
+ sections,
66
+ });
67
+ if (!quoted.ok)
68
+ return quoted;
69
+ const quote = quoted.value;
70
+ const value = { topic: next.topic, topicSource: next.source, fullScript: next.fullScript, quote };
71
+ // A second press (a double click) of what the sample is already made, or being made, of:
72
+ // nothing more is made or spent.
73
+ if (alreadyMade(deps, id, template.version, next))
74
+ return { ok: true, value };
75
+ if (quote.problem !== null)
76
+ return { ok: false, reason: "invalid-input", message: quote.problem, quote };
77
+ if (exceedsAcknowledged(quote.high, ack.acknowledgedHigh))
78
+ return {
79
+ ok: false,
80
+ reason: "cost-changed",
81
+ message: `This change now costs about ${money(quote.high)}, more than the ${money(ack.acknowledgedHigh)} you saw. Press it again to accept the new price.`,
82
+ quote,
83
+ };
84
+ if (quote.unknown > 0 && ack.acknowledgedUnknown !== true)
85
+ return {
86
+ ok: false,
87
+ reason: "cost-changed",
88
+ message: `${String(quote.unknown)} ${quote.unknown === 1 ? "step of this sample has" : "steps of this sample have"} no known price. Tick that you accept unknown costs, then press it again.`,
89
+ quote,
90
+ };
91
+ if (!quote.fits)
92
+ return {
93
+ ok: false,
94
+ reason: "over-budget",
95
+ message: `${budgetWords(quote.budget, quote, "This")}, or try again tomorrow.`,
96
+ quote,
97
+ };
98
+ const at = deps.clock.now().toISOString();
99
+ transact(deps.db, () => {
100
+ const record = sampleOf(deps.db, id);
101
+ // A template with no sample yet keeps the topic for its first one, under the id its
102
+ // project will get.
103
+ upsertSample(deps.db, id, {
104
+ projectId: record?.projectId ?? deps.review.ids.next(),
105
+ topic: next.topic,
106
+ topicSource: next.source,
107
+ fullScript: next.fullScript,
108
+ at,
109
+ });
110
+ setSampleVersion(deps.db, id, template.version, {
111
+ state: "queued",
112
+ at,
113
+ quotedHigh: quote.high,
114
+ heldReason: null,
115
+ });
116
+ if (ack.acknowledgedUnknown === true || quote.unknown === 0)
117
+ deps.acks.accept(id, template.version);
118
+ enqueueJob(deps.db, {
119
+ templateId: id,
120
+ templateVersion: template.version,
121
+ ackHigh: ack.acknowledgedHigh,
122
+ kind,
123
+ requestedAt: at,
124
+ });
125
+ });
126
+ return { ok: true, value };
127
+ }
128
+ function alreadyMade(deps, id, version, next) {
129
+ const record = sampleOf(deps.db, id);
130
+ if (record === undefined ||
131
+ record.topic !== next.topic ||
132
+ record.topicSource !== next.source ||
133
+ record.fullScript !== next.fullScript)
134
+ return false;
135
+ const job = queuedJob(deps.db, id) ?? runningJob(deps.db);
136
+ if (job !== undefined &&
137
+ job.templateId === id &&
138
+ job.templateVersion === version &&
139
+ job.kind !== "free")
140
+ return true;
141
+ const state = sampleVersions(deps.db, id).find((one) => one.templateVersion === version)?.state;
142
+ return state === "queued" || state === "making" || state === "done";
143
+ }
144
+ // PUT /:id/topic: a new topic remakes every section's sample.
145
+ export async function setSampleTopic(deps, id, raw) {
146
+ const parsed = topicSchema.safeParse(raw);
147
+ if (!parsed.success)
148
+ return {
149
+ ok: false,
150
+ reason: "invalid-input",
151
+ message: "Type a topic of 1 to 300 characters for the sample, then press Use topic.",
152
+ };
153
+ const { mutationId, ...ask } = parsed.data;
154
+ const hash = createHash("sha256").update(JSON.stringify(ask)).digest("hex");
155
+ const key = `${topicMutationKey}${id}`;
156
+ const last = readJsonSetting(deps.db, key, topicMutationSchema, null);
157
+ if (mutationId !== undefined && last?.mutationId === mutationId) {
158
+ if (last.hash !== hash)
159
+ return {
160
+ ok: false,
161
+ reason: "conflict",
162
+ message: "This topic change was already sent with another topic or price. Reload the studio to see the sample's topic, then change it again.",
163
+ };
164
+ const replayed = await quoteSample(deps, id, {
165
+ topic: last.topic,
166
+ fullScript: last.fullScript,
167
+ sections: "all",
168
+ });
169
+ if (!replayed.ok)
170
+ return replayed;
171
+ return {
172
+ ok: true,
173
+ value: {
174
+ topic: last.topic,
175
+ topicSource: last.topicSource,
176
+ fullScript: last.fullScript,
177
+ quote: replayed.value,
178
+ },
179
+ };
180
+ }
181
+ const named = topicOf(deps, id, ask.topic);
182
+ if (!named.ok)
183
+ return { ok: false, reason: "invalid-input", message: named.message };
184
+ const record = sampleOf(deps.db, id);
185
+ const result = await change(deps, id, { topic: named.topic, source: named.source, fullScript: record?.fullScript ?? false }, "all", ask, "topic");
186
+ if (result.ok && mutationId !== undefined)
187
+ writeSetting(deps.db, key, JSON.stringify({
188
+ mutationId,
189
+ hash,
190
+ topic: named.topic,
191
+ topicSource: named.source,
192
+ fullScript: record?.fullScript ?? false,
193
+ }));
194
+ return result;
195
+ }
196
+ // The last topic change of each template that named a mutation id, to answer its retry.
197
+ const topicMutationKey = "templateStudio.topicMutation.";
198
+ const topicMutationSchema = z
199
+ .object({
200
+ mutationId: z.string(),
201
+ hash: z.string(),
202
+ topic: z.string(),
203
+ topicSource: z.enum(["fixed", "queued"]),
204
+ fullScript: z.boolean(),
205
+ })
206
+ .nullable();
207
+ // POST /:id/full-script: the whole script, or back to the outline and first chapter, remakes
208
+ // the script and what is made from it.
209
+ export async function setFullScript(deps, id, raw) {
210
+ const parsed = fullScriptSchema.safeParse(raw);
211
+ if (!parsed.success)
212
+ return {
213
+ ok: false,
214
+ reason: "invalid-input",
215
+ message: "The studio sent a request Slopify couldn't read. Reload the page and try again.",
216
+ };
217
+ const record = sampleOf(deps.db, id);
218
+ return change(deps, id, {
219
+ topic: record?.topic ?? defaultSampleTopic(deps.db),
220
+ source: record?.topicSource ?? "fixed",
221
+ fullScript: parsed.data.fullScript,
222
+ }, withDependents(["script"]), parsed.data, "full-script");
223
+ }
@@ -0,0 +1,76 @@
1
+ import { transact } from "../../kernel/db/tx.js";
2
+ import { deleteRenders } from "../designs/render.js";
3
+ // Designs and design templates in the trash. A design keeps its stamp in its own `deleted_at`;
4
+ // removing it for good removes its versions (they cascade) and every file drawn from it. The
5
+ // pictures stay in image_blobs, as a cast member's do: a schedule run or another design may
6
+ // still name them. A template's hidden sample goes with the template.
7
+ export function trashDesign(deps, id) {
8
+ const changed = deps.db
9
+ .prepare("UPDATE designs SET deleted_at=?, updated_at=? WHERE id=? AND deleted_at IS NULL AND sample_of IS NULL")
10
+ .run(deps.clock.now().toISOString(), deps.clock.now().toISOString(), id).changes;
11
+ return Number(changed) === 0 ? { ok: false, reason: "not-found" } : { ok: true, value: null };
12
+ }
13
+ export function trashDesignTemplate(deps, id) {
14
+ const changed = deps.db
15
+ .prepare("UPDATE design_templates SET deleted_at=? WHERE id=? AND deleted_at IS NULL")
16
+ .run(deps.clock.now().toISOString(), id).changes;
17
+ return Number(changed) === 0 ? { ok: false, reason: "not-found" } : { ok: true, value: null };
18
+ }
19
+ export function restoreDesign(deps, id) {
20
+ const found = deps.db
21
+ .prepare("SELECT name FROM designs WHERE id=? AND deleted_at IS NOT NULL")
22
+ .get(id);
23
+ if (typeof found?.name !== "string")
24
+ return { ok: false, reason: "not-found" };
25
+ deps.db
26
+ .prepare("UPDATE designs SET deleted_at=NULL, updated_at=? WHERE id=?")
27
+ .run(deps.clock.now().toISOString(), id);
28
+ return { ok: true, value: { kind: "design", id, name: found.name, renamedFrom: null } };
29
+ }
30
+ export function restoreDesignTemplate(deps, id) {
31
+ const found = deps.db
32
+ .prepare(`SELECT r.name FROM design_templates t
33
+ JOIN design_template_revisions r ON r.template_id = t.id AND r.version = t.head_version
34
+ WHERE t.id=? AND t.deleted_at IS NOT NULL`)
35
+ .get(id);
36
+ if (typeof found?.name !== "string")
37
+ return { ok: false, reason: "not-found" };
38
+ deps.db.prepare("UPDATE design_templates SET deleted_at=NULL WHERE id=?").run(id);
39
+ return { ok: true, value: { kind: "design-template", id, name: found.name, renamedFrom: null } };
40
+ }
41
+ export function deleteDesignNow(deps, id) {
42
+ const changed = deps.db
43
+ .prepare("DELETE FROM designs WHERE id=? AND deleted_at IS NOT NULL")
44
+ .run(id).changes;
45
+ if (Number(changed) === 0)
46
+ return { ok: false, reason: "not-found" };
47
+ deleteRenders(deps.paths, id);
48
+ return { ok: true, value: null };
49
+ }
50
+ export function deleteDesignTemplateNow(deps, id) {
51
+ const samples = transact(deps.db, () => {
52
+ const changed = deps.db
53
+ .prepare("DELETE FROM design_templates WHERE id=? AND deleted_at IS NOT NULL")
54
+ .run(id).changes;
55
+ if (Number(changed) === 0)
56
+ return undefined;
57
+ const ids = deps.db
58
+ .prepare("SELECT id FROM designs WHERE sample_of=?")
59
+ .all(id)
60
+ .map((row) => String(row.id));
61
+ deps.db.prepare("DELETE FROM designs WHERE sample_of=?").run(id);
62
+ return ids;
63
+ });
64
+ if (samples === undefined)
65
+ return { ok: false, reason: "not-found" };
66
+ for (const sample of samples)
67
+ deleteRenders(deps.paths, sample);
68
+ return { ok: true, value: null };
69
+ }
70
+ export const trashedDesignRows = `
71
+ UNION ALL SELECT 'design', id, name, kind, deleted_at FROM designs
72
+ WHERE deleted_at IS NOT NULL AND sample_of IS NULL
73
+ UNION ALL SELECT 'design-template', t.id, r.name, NULL, t.deleted_at
74
+ FROM design_templates t
75
+ JOIN design_template_revisions r ON r.template_id = t.id AND r.version = t.head_version
76
+ WHERE t.deleted_at IS NOT NULL`;
@@ -1,15 +1,24 @@
1
1
  import { z } from "zod";
2
- // Settings → Trash. Deleting a project, a Library prompt or intro/outro, a template or a
3
- // schedule stamps it instead of removing it; it stays here this long, then the daily purge
2
+ // Settings → Trash. Deleting a project, a Library prompt or intro/outro, a template, a
3
+ // schedule, a design or a design template stamps it instead of removing it; it stays here this long, then the daily purge
4
4
  // removes it for good (a project's folder with it).
5
5
  export const trashDays = 30;
6
6
  export const trashMs = trashDays * 24 * 60 * 60_000;
7
- export const trashKinds = ["project", "prompt", "entry", "template", "schedule"];
7
+ export const trashKinds = [
8
+ "project",
9
+ "prompt",
10
+ "entry",
11
+ "template",
12
+ "schedule",
13
+ "design",
14
+ "design-template",
15
+ ];
8
16
  export const trashItemSchema = z.object({
9
17
  kind: z.enum(trashKinds),
10
18
  id: z.string(),
11
19
  name: z.string(),
12
- // What sort of prompt ("article", "image"…) or entry ("intro", "outro"); null otherwise.
20
+ // What sort of prompt ("article", "image"…), entry ("intro", "outro") or design ("edit",
21
+ // "design"); null otherwise.
13
22
  detail: z.string().nullable(),
14
23
  deletedAt: z.string(),
15
24
  // When the daily purge removes it for good.
@@ -4,6 +4,11 @@ import { deleteVersions, recordVersion } from "../library/history.js";
4
4
  import { nameMax } from "../library/model.js";
5
5
  import { entryById, promptById } from "../library/repo.js";
6
6
  import { deleteProject, projectBusy } from "../storage/delete-project.js";
7
+ import { bakeoffOf, clonesOf } from "../template-studio/bakeoff-repo.js";
8
+ import { removeHiddenProject } from "../template-studio/hidden.js";
9
+ import { removePackSamples } from "../template-studio/pack-samples.js";
10
+ import { carryGateOnto, notTemplateSample, sampleOf } from "../template-studio/repo.js";
11
+ import { deleteDesignNow, deleteDesignTemplateNow, restoreDesign, restoreDesignTemplate, trashedDesignRows, } from "./designs.js";
7
12
  import { trashKinds, trashMs, } from "./model.js";
8
13
  // Settings → Trash: what was deleted in the last 30 days, put back or removed for good.
9
14
  //
@@ -24,6 +29,7 @@ const row = z.object({
24
29
  const trashedRows = `
25
30
  SELECT 'project' AS kind, p.id, p.title AS name, NULL AS detail, t.deleted_at
26
31
  FROM project_trash t JOIN projects p ON p.id = t.project_id
32
+ WHERE ${notTemplateSample("p")}
27
33
  UNION ALL SELECT 'prompt', id, name, kind, deleted_at FROM prompts WHERE deleted_at IS NOT NULL
28
34
  UNION ALL SELECT 'entry', id, name, category, deleted_at FROM entries WHERE deleted_at IS NOT NULL
29
35
  UNION ALL SELECT 'template', t.id, r.name, NULL, t.deleted_at
@@ -31,7 +37,7 @@ const trashedRows = `
31
37
  JOIN project_template_revisions r ON r.template_id = t.id AND r.version = t.head_version
32
38
  WHERE t.deleted_at IS NOT NULL
33
39
  UNION ALL SELECT 'schedule', id, name, NULL, deleted_at FROM schedules
34
- WHERE deleted_at IS NOT NULL AND purged_at IS NULL`;
40
+ WHERE deleted_at IS NOT NULL AND purged_at IS NULL${trashedDesignRows}`;
35
41
  export function listTrash(deps) {
36
42
  const now = deps.clock.now().valueOf();
37
43
  return deps.db
@@ -90,6 +96,10 @@ export function restoreItem(deps, kind, id) {
90
96
  return restoreTemplate(deps, id);
91
97
  case "schedule":
92
98
  return restoreSchedule(deps, id);
99
+ case "design":
100
+ return restoreDesign(deps, id);
101
+ case "design-template":
102
+ return restoreDesignTemplate(deps, id);
93
103
  }
94
104
  });
95
105
  }
@@ -151,6 +161,8 @@ function restoreTemplate(deps, id) {
151
161
  .prepare("INSERT INTO project_template_revisions(template_id,version,name,document_json,created_at) VALUES (?,?,?,?,?)")
152
162
  .run(id, version, name, found.document_json, deps.clock.now().toISOString());
153
163
  deps.db.prepare("UPDATE project_templates SET head_version = ? WHERE id = ?").run(version, id);
164
+ // Only the name changed: a schedule running the version it was keeps running.
165
+ carryGateOnto(deps.db, id, found.version, version);
154
166
  }
155
167
  return {
156
168
  ok: true,
@@ -210,12 +222,46 @@ export function deleteNow(deps, kind, id) {
210
222
  return { ok: true, value: null };
211
223
  });
212
224
  case "template": {
225
+ if (deps.db
226
+ .prepare("SELECT 1 FROM project_templates WHERE id = ? AND deleted_at IS NOT NULL")
227
+ .get(id) === undefined)
228
+ return { ok: false, reason: "not-found" };
229
+ // Its studio sample goes first, folder and all, while its row still says the folder is
230
+ // in the data dir; a sample still being made keeps the template in the trash.
231
+ const sample = sampleOf(deps.db, id);
232
+ if (sample !== undefined) {
233
+ const removed = removeHiddenProject(deps, sample.projectId);
234
+ if (!removed.ok && removed.reason !== "no-project")
235
+ return {
236
+ ok: false,
237
+ reason: removed.reason,
238
+ ...(removed.detail === undefined ? {} : { detail: removed.detail }),
239
+ };
240
+ }
241
+ // Its bake-off copies go the same way: their rows go with the template, and a copy
242
+ // left behind would show up as an ordinary project.
243
+ const bakeoff = bakeoffOf(deps.db, id);
244
+ for (const clone of bakeoff === undefined ? [] : clonesOf(deps.db, bakeoff.id)) {
245
+ if (clone.projectId === null)
246
+ continue;
247
+ const removed = removeHiddenProject(deps, clone.projectId);
248
+ if (!removed.ok && removed.reason !== "no-project")
249
+ return {
250
+ ok: false,
251
+ reason: removed.reason,
252
+ ...(removed.detail === undefined ? {} : { detail: removed.detail }),
253
+ };
254
+ }
213
255
  // Revisions cascade. A schedule in the trash may still name it: that schedule can then
214
256
  // no longer be restored (`restoreSchedule`), only removed.
215
257
  const changed = deps.db
216
258
  .prepare("DELETE FROM project_templates WHERE id = ? AND deleted_at IS NOT NULL")
217
259
  .run(id).changes;
218
- return Number(changed) === 0 ? { ok: false, reason: "not-found" } : { ok: true, value: null };
260
+ if (Number(changed) === 0)
261
+ return { ok: false, reason: "not-found" };
262
+ // The samples an imported pack came with go with it.
263
+ removePackSamples(deps.paths, id);
264
+ return { ok: true, value: null };
219
265
  }
220
266
  case "schedule": {
221
267
  // Gone from the trash; the row stays as the history Schedules lists under Deleted.
@@ -224,6 +270,10 @@ export function deleteNow(deps, kind, id) {
224
270
  .run(deps.clock.now().toISOString(), id).changes;
225
271
  return Number(changed) === 0 ? { ok: false, reason: "not-found" } : { ok: true, value: null };
226
272
  }
273
+ case "design":
274
+ return deleteDesignNow(deps, id);
275
+ case "design-template":
276
+ return deleteDesignTemplateNow(deps, id);
227
277
  }
228
278
  }
229
279
  // The daily purge: everything deleted more than 30 days ago goes for good. A failure on one
@@ -2,6 +2,7 @@ import { existsSync } from "node:fs";
2
2
  import { readdir, readFile } from "node:fs/promises";
3
3
  import { join } from "node:path";
4
4
  import { fileURLToPath } from "node:url";
5
+ import { hasErrnoCode } from "../../kernel/errors.js";
5
6
  import { pageTitleOf, tutorialHeadings, tutorialPageIdPattern } from "./anchors.js";
6
7
  // dist/tutorials when running the build; the repository's docs/wiki when running from source
7
8
  // (tests, `tsx`).
@@ -43,7 +44,7 @@ export async function loadTutorials(dir) {
43
44
  names = await readdir(dir);
44
45
  }
45
46
  catch (error) {
46
- if (error.code === "ENOENT")
47
+ if (hasErrnoCode(error, "ENOENT"))
47
48
  throw new TutorialsMissing("This copy of Slopify has no tutorials: its docs/wiki folder is missing. Reinstall Slopify (or run npm run build) and reload the page.");
48
49
  throw error;
49
50
  }
@@ -57,7 +58,7 @@ export async function loadTutorials(dir) {
57
58
  if (!pages.has("Home"))
58
59
  throw new TutorialsMissing("This copy of Slopify has damaged tutorials: docs/wiki/Home.md is missing. Reinstall Slopify (or run npm run build) and reload the page.");
59
60
  const extra = async (name) => readFile(join(dir, name), "utf8").catch((error) => {
60
- if (error.code === "ENOENT")
61
+ if (hasErrnoCode(error, "ENOENT"))
61
62
  return "";
62
63
  throw error;
63
64
  });
@@ -0,0 +1,17 @@
1
+ // Text as libass draws it literally: braces and backslashes escaped rather than read as
2
+ // override tags; code keeps its spaces, prose has them collapsed. Shared by the figure cards
3
+ // and the designs' text layer, so a name, date or price is drawn exactly as typed.
4
+ export function assText(value, keepSpaces = false) {
5
+ const escaped = value.replace(/\\/g, "\\\\").replace(/\{/g, "\\{").replace(/\}/g, "\\}");
6
+ return keepSpaces
7
+ ? escaped.replace(/ {2,}/g, (run) => "\\h".repeat(run.length))
8
+ : escaped.replace(/\s+/g, " ");
9
+ }
10
+ // #RRGGBB as ASS writes a colour: &HBBGGRR&. Anything else draws white.
11
+ export function assColour(hex) {
12
+ const match = /^#([0-9A-Fa-f]{2})([0-9A-Fa-f]{2})([0-9A-Fa-f]{2})$/.exec(hex);
13
+ if (match === null)
14
+ return "&HFFFFFF&";
15
+ const [, r, g, b] = match;
16
+ return `&H${(b ?? "").toUpperCase()}${(g ?? "").toUpperCase()}${(r ?? "").toUpperCase()}&`;
17
+ }
@@ -1,65 +1,3 @@
1
- import { createHash } from "node:crypto";
2
- import { readFileSync } from "node:fs";
3
- import { relative } from "node:path";
4
- import { outputPath, projectDir } from "../storage/layout.js";
5
- import { outputsOf } from "../storage/repo.js";
6
- import { prepareSubtitles } from "../subtitles/prepare.js";
7
- import { audioExportArgs } from "./audio-export-args.js";
8
- import { audioInputs } from "./audio-inputs.js";
9
- import { runFfmpeg } from "./ffmpeg.js";
10
- import { audioTimeline } from "./plan.js";
11
- import { reusableAudioExport } from "./reuse-audio.js";
12
- import { writeExport } from "./write-export.js";
13
- import { writeSubtitles } from "./write-subtitles.js";
1
+ // `rebuild/runtime-export.ts` still imports the arguments from here; new code imports
2
+ // `audio-export-args.ts`.
14
3
  export { audioExportArgs } from "./audio-export-args.js";
15
- const rate = 48000;
16
- export async function exportAudioWav(deps, context, gapSeconds, edgeSeconds) {
17
- const { projectId } = context.stage;
18
- const outputs = outputsOf(deps.db, projectId);
19
- const input = await audioInputs(deps, projectId, outputs, context.signal);
20
- const audio = audioTimeline({ ...input, gapSeconds, edgeSeconds }, 1 / rate);
21
- const totalSeconds = audio.reduce((sum, segment) => sum + segment.seconds, 0);
22
- const dir = projectDir(deps.paths, projectId);
23
- const sources = outputs.filter((output) => ["audio_body", "audio_intro", "audio_outro"].includes(output.role));
24
- const plan = {
25
- sampleRate: rate,
26
- channels: 2,
27
- codec: "pcm_s16le",
28
- gapSeconds,
29
- edgeSeconds,
30
- totalSeconds,
31
- audio: audio.map((segment) => ({
32
- ...segment,
33
- path: segment.path === null ? null : relative(dir, segment.path),
34
- })),
35
- output: "audio.wav",
36
- sourceIds: sources.map((output) => output.id),
37
- sourceHashes: sources.map((output) => ({
38
- id: output.id,
39
- sha256: createHash("sha256")
40
- .update(readFileSync(outputPath(deps.paths, projectId, output.path)))
41
- .digest("hex"),
42
- })),
43
- };
44
- const existing = reusableAudioExport(deps.paths, projectId, outputs, plan);
45
- const subtitles = await prepareSubtitles(deps, context, audio, { width: 1920, height: 1080 });
46
- if (existing !== undefined) {
47
- writeSubtitles(deps, context, existing, subtitles);
48
- return;
49
- }
50
- await writeExport(deps, context, {
51
- role: "audio_export",
52
- filename: "audio.wav",
53
- partName: "audio.part.wav",
54
- totalSeconds,
55
- record: plan,
56
- subtitles,
57
- render: (part, onProgress) => runFfmpeg({
58
- bin: deps.ffmpeg,
59
- args: audioExportArgs(audio, part),
60
- signal: context.signal,
61
- log: deps.log,
62
- onProgress,
63
- }),
64
- });
65
- }