@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,74 @@
1
+ import { rowsOf } from "./backup-export.js";
2
+ // Designs and design templates in a backup's library part. Their pictures travel with every
3
+ // other picture in image_blobs; the files drawn from them are a cache and never travel.
4
+ export const designTables = [
5
+ "designs",
6
+ "design_steps",
7
+ "design_templates",
8
+ "design_template_revisions",
9
+ "design_template_samples",
10
+ ];
11
+ export function exportDesigns(db, put) {
12
+ put("designs", rowsOf(db, "SELECT * FROM designs ORDER BY rowid"));
13
+ put("design_steps", rowsOf(db, "SELECT * FROM design_steps ORDER BY design_id, n"));
14
+ put("design_templates", rowsOf(db, "SELECT * FROM design_templates ORDER BY rowid"));
15
+ put("design_template_revisions", rowsOf(db, "SELECT * FROM design_template_revisions ORDER BY template_id, version"));
16
+ put("design_template_samples", rowsOf(db, "SELECT * FROM design_template_samples ORDER BY rowid"));
17
+ }
18
+ // The project a design links to, when the one here is the backup's own (it came in with this
19
+ // import, or with an earlier one: same id, same creation time); a different project that only
20
+ // shares the id is not linked.
21
+ function sameProject(db, scratch, id) {
22
+ if (typeof id !== "string")
23
+ return null;
24
+ const here = db.prepare("SELECT created_at FROM projects WHERE id=?").get(id);
25
+ const there = scratch.prepare("SELECT created_at FROM projects WHERE id=?").get(id);
26
+ return here !== undefined && there !== undefined && here.created_at === there.created_at
27
+ ? id
28
+ : null;
29
+ }
30
+ // Added beside what is here: a design or template this install already has (same id) is kept
31
+ // as it is. A picture the other install was still making never lands here; a sample job it was
32
+ // running starts over.
33
+ export function mergeDesigns(db, scratch, insert) {
34
+ const has = (sql, id) => db.prepare(sql).get(id) !== undefined;
35
+ const templates = { added: 0, skipped: 0 };
36
+ for (const row of rowsOf(scratch, "SELECT * FROM design_templates ORDER BY rowid")) {
37
+ const id = String(row.id);
38
+ if (has("SELECT 1 FROM design_templates WHERE id=?", id)) {
39
+ templates.skipped += 1;
40
+ continue;
41
+ }
42
+ insert("design_templates", row);
43
+ for (const revision of rowsOf(scratch, "SELECT * FROM design_template_revisions WHERE template_id=? ORDER BY version", id))
44
+ insert("design_template_revisions", revision);
45
+ templates.added += 1;
46
+ }
47
+ const designs = { added: 0, skipped: 0 };
48
+ for (const row of rowsOf(scratch, "SELECT * FROM designs ORDER BY rowid")) {
49
+ const id = String(row.id);
50
+ // A template's hidden sample is not one of the person's designs, so it isn't counted.
51
+ if (has("SELECT 1 FROM designs WHERE id=?", id)) {
52
+ if (row.sample_of === null)
53
+ designs.skipped += 1;
54
+ continue;
55
+ }
56
+ insert("designs", { ...row, project_id: sameProject(db, scratch, row.project_id) });
57
+ for (const step of rowsOf(scratch, "SELECT * FROM design_steps WHERE design_id=? ORDER BY n", id))
58
+ insert("design_steps", step.state === "making"
59
+ ? {
60
+ ...step,
61
+ state: "failed",
62
+ error: "Stopped by the backup. Press Try again.",
63
+ settled_at: step.created_at ?? null,
64
+ }
65
+ : step);
66
+ if (row.sample_of === null)
67
+ designs.added += 1;
68
+ }
69
+ for (const row of rowsOf(scratch, "SELECT * FROM design_template_samples ORDER BY rowid"))
70
+ if (has("SELECT 1 FROM design_templates WHERE id=?", String(row.template_id)) &&
71
+ !has("SELECT 1 FROM design_template_samples WHERE template_id=?", String(row.template_id)))
72
+ insert("design_template_samples", row.state === "making" ? { ...row, state: "queued" } : row);
73
+ return { designs, templates };
74
+ }
@@ -3,7 +3,10 @@ import { constants, lstatSync, readdirSync } from "node:fs";
3
3
  import { open } from "node:fs/promises";
4
4
  import { join } from "node:path";
5
5
  import { transact } from "../../kernel/db/tx.js";
6
+ import { hasErrnoCode } from "../../kernel/errors.js";
7
+ import { notTemplateSample } from "../template-studio/repo.js";
6
8
  import { projectMarker } from "./arrange.js";
9
+ import { exportDesigns } from "./backup-designs.js";
7
10
  import { backupFormat, backupSchemaVersion, checksumsMember, fontMember, imageMember, libraryMember, manifestMember, projectFileMember, projectMember, projectTables, safeRelativePath, stagedMember, usageMember, } from "./backup-format.js";
8
11
  import { outputPath, projectDir, stagingPath } from "./layout.js";
9
12
  import { exportableSettings, installedUploadedFontFiles } from "./portable.js";
@@ -36,7 +39,7 @@ export function busyProjects(deps) {
36
39
  OR EXISTS(SELECT 1 FROM revision_work w WHERE w.project_id=p.id AND w.state='running')
37
40
  OR EXISTS(SELECT 1 FROM project_queue q WHERE q.project_id=p.id AND q.state='queued')
38
41
  AS busy
39
- FROM projects p ORDER BY p.created_at, p.id`)
42
+ FROM projects p WHERE ${notTemplateSample("p")} ORDER BY p.created_at, p.id`)
40
43
  .all()
41
44
  .flatMap((row) => {
42
45
  const id = String(row.id);
@@ -60,7 +63,7 @@ export function planBackup(deps) {
60
63
  },
61
64
  },
62
65
  projects: deps.db
63
- .prepare("SELECT id,title FROM projects ORDER BY created_at,id")
66
+ .prepare(`SELECT id,title FROM projects WHERE ${notTemplateSample()} ORDER BY created_at,id`)
64
67
  .all()
65
68
  .map((row) => projectSnapshot(deps, String(row.id), String(row.title))),
66
69
  }));
@@ -238,7 +241,7 @@ function projectFiles(root) {
238
241
  entries = readdirSync(join(root, relative), { withFileTypes: true });
239
242
  }
240
243
  catch (error) {
241
- if (error instanceof Error && "code" in error && error.code === "ENOENT")
244
+ if (hasErrnoCode(error, "ENOENT"))
242
245
  return;
243
246
  throw error;
244
247
  }
@@ -271,6 +274,7 @@ function librarySnapshot(deps) {
271
274
  put("narration_aliases", rowsOf(db, "SELECT * FROM narration_aliases ORDER BY position,rowid"));
272
275
  put("project_templates", rowsOf(db, "SELECT * FROM project_templates ORDER BY rowid"));
273
276
  put("project_template_revisions", rowsOf(db, "SELECT * FROM project_template_revisions ORDER BY template_id,version"));
277
+ put("template_gates", rowsOf(db, "SELECT * FROM template_gates ORDER BY template_id,version"));
274
278
  put("schedules", rowsOf(db, "SELECT * FROM schedules ORDER BY rowid"));
275
279
  put("schedule_runs", rowsOf(db, "SELECT * FROM schedule_runs ORDER BY rowid"));
276
280
  put("schedule_topics", rowsOf(db, "SELECT * FROM schedule_topics ORDER BY rowid"));
@@ -281,6 +285,7 @@ function librarySnapshot(deps) {
281
285
  put("episode_memories", rowsOf(db, "SELECT * FROM episode_memories ORDER BY rowid"));
282
286
  put("channel_videos", rowsOf(db, "SELECT * FROM channel_videos ORDER BY rowid"));
283
287
  put("standalone_usage", rowsOf(db, "SELECT * FROM standalone_usage ORDER BY rowid"));
288
+ exportDesigns(db, put);
284
289
  // Every picture, not only the cast's: a project's config names the pictures it was started
285
290
  // with by hash, and a Rebuild reads them from here.
286
291
  const images = db
@@ -75,6 +75,8 @@ export const projectTables = [
75
75
  "plan_limit_readings",
76
76
  "project_channels",
77
77
  "project_trash",
78
+ // Since 4.0.0: each project's posts to other sites, their hand edits and links.
79
+ "social_posts",
78
80
  ];
79
81
  export const libraryTables = [
80
82
  "settings",
@@ -103,6 +105,14 @@ export const libraryTables = [
103
105
  // Since 3.0.0: what topic generation, episode summaries and cast pictures cost, which
104
106
  // belongs to a schedule or channel rather than a project.
105
107
  "standalone_usage",
108
+ // Since 4.0.0: which template versions the template studio marked ready.
109
+ "template_gates",
110
+ // Since 4.0.0: designs with every version of their picture, and design templates.
111
+ "designs",
112
+ "design_steps",
113
+ "design_templates",
114
+ "design_template_revisions",
115
+ "design_template_samples",
106
116
  ];
107
117
  export const usageTables = ["telemetry_events"];
108
118
  // Ceilings a well-formed backup of a real install stays far below. They bound what a hostile
@@ -16,6 +16,7 @@ import { projectTemplateSchema } from "../project-templates/schema.js";
16
16
  import { revisionById } from "../revisions/repo.js";
17
17
  import { runsForSchedule, scheduleRows } from "../schedules/repo.js";
18
18
  import { allTelemetryEvents } from "../telemetry/repo.js";
19
+ import { mergeDesigns } from "./backup-designs.js";
19
20
  import { projectRows, rowsOf } from "./backup-export.js";
20
21
  import { backupMaxJsonBytes, backupMaxManifestBytes, backupSchemaVersion, checksumsMember, checksumsSchema, fontMember, imageMember, libraryMember, libraryPartSchema, libraryTables, manifestHead, manifestMember, manifestSchema, projectFileMember, projectMember, projectPartSchema, projectTables, stagedMember, usageMember, usagePartSchema, usageTables, } from "./backup-format.js";
21
22
  import { outputPath, projectDir, stagingPath } from "./layout.js";
@@ -382,6 +383,8 @@ function loadScratch(clock, manifest, library, usage, parts) {
382
383
  // broken fails there, and the upgrade's own message would talk about downgrading.
383
384
  throw damaged("some of its records point at records it doesn't hold.");
384
385
  }
386
+ // A design only links to a project; one whose project didn't travel stays, unlinked.
387
+ scratch.exec("UPDATE designs SET project_id=NULL WHERE project_id NOT IN (SELECT id FROM projects)");
385
388
  if (scratch.prepare("PRAGMA foreign_key_check").all().length > 0)
386
389
  throw damaged("some of its records point at records it doesn't hold.");
387
390
  return scratch;
@@ -633,6 +636,8 @@ function commit(deps, work, manifest, prepared) {
633
636
  }
634
637
  insertRow(db, "project_template_revisions", next);
635
638
  }
639
+ for (const gate of rowsOf(scratch, "SELECT * FROM template_gates WHERE template_id=? ORDER BY version", id))
640
+ insertRow(db, "template_gates", gate);
636
641
  if (renamed)
637
642
  templates.renamed += 1;
638
643
  else
@@ -640,8 +645,15 @@ function commit(deps, work, manifest, prepared) {
640
645
  }
641
646
  const schedules = { added: 0, renamed: 0, skipped: 0, paused: 0 };
642
647
  for (const schedule of rowsOf(scratch, "SELECT * FROM schedules ORDER BY rowid")) {
643
- if (has("SELECT 1 FROM schedules WHERE id=?", String(schedule.id)) ||
644
- !has("SELECT 1 FROM project_template_revisions WHERE template_id=? AND version=?", String(schedule.template_id), Number(schedule.template_version))) {
648
+ // A design schedule's template comes in with the designs, further down.
649
+ const design = schedule.template_kind === "design";
650
+ const templateHere = design
651
+ ? scratch
652
+ .prepare("SELECT 1 FROM design_templates WHERE id=?")
653
+ .get(String(schedule.template_id)) !== undefined ||
654
+ has("SELECT 1 FROM design_templates WHERE id=?", String(schedule.template_id))
655
+ : has("SELECT 1 FROM project_template_revisions WHERE template_id=? AND version=?", String(schedule.template_id), Number(schedule.template_version));
656
+ if (has("SELECT 1 FROM schedules WHERE id=?", String(schedule.id)) || !templateHere) {
645
657
  schedules.skipped += 1;
646
658
  continue;
647
659
  }
@@ -732,6 +744,10 @@ function commit(deps, work, manifest, prepared) {
732
744
  move(folder, projectDir(paths, listed.id));
733
745
  imported.push({ id: listed.id, title: listed.title });
734
746
  }
747
+ // After the projects, so a design keeps its link to one that came in with it.
748
+ const merged = mergeDesigns(db, scratch, (table, row) => {
749
+ insertRow(db, table, row);
750
+ });
735
751
  const summary = {
736
752
  backup: {
737
753
  id: manifest.backupId,
@@ -748,6 +764,8 @@ function commit(deps, work, manifest, prepared) {
748
764
  schedules,
749
765
  voices: { added: voicesAdded, renamed: 0, skipped: voicesSkipped },
750
766
  drafts,
767
+ designs: { ...merged.designs, renamed: 0 },
768
+ designTemplates: { ...merged.templates, renamed: 0 },
751
769
  settings: { added: settingsAdded, kept: settingsKept },
752
770
  fonts,
753
771
  usage: { events, alreadyImported },
@@ -1,6 +1,8 @@
1
1
  import { rmSync } from "node:fs";
2
+ import { errorText } from "../../kernel/errors.js";
2
3
  import { derive } from "../../kernel/runner/graph.js";
3
4
  import { stagesOf } from "../admission/repo.js";
5
+ import { dropReshaped } from "../social/reshape.js";
4
6
  import { projectDir, renderCacheDir } from "./layout.js";
5
7
  // A run in flight: a call the runner holds, or a stage running or waiting out a retry.
6
8
  export function projectBusy(deps, projectId) {
@@ -19,13 +21,15 @@ export function deleteProject(deps, projectId) {
19
21
  try {
20
22
  rmSync(dir, { recursive: true, force: true });
21
23
  rmSync(renderCacheDir(deps.paths, projectId), { recursive: true, force: true });
24
+ // The pictures cut for its posts (`slices/social/reshape.ts`).
25
+ dropReshaped(deps.paths, projectId);
22
26
  }
23
27
  catch (error) {
24
28
  deps.log.write("warn", "project.delete", {
25
29
  projectId,
26
- detail: messageOf(error),
30
+ detail: errorText(error),
27
31
  });
28
- return { ok: false, reason: "files", detail: messageOf(error) };
32
+ return { ok: false, reason: "files", detail: errorText(error) };
29
33
  }
30
34
  // Stages, attempts, pieces and outputs go with it: every one of those tables declares
31
35
  // ON DELETE CASCADE on the project, and the connection runs with foreign keys on
@@ -36,6 +40,3 @@ export function deleteProject(deps, projectId) {
36
40
  places.forget(projectId);
37
41
  return { ok: true };
38
42
  }
39
- function messageOf(error) {
40
- return error instanceof Error ? error.message : String(error);
41
- }
@@ -3,8 +3,9 @@ import { createReadStream, lstatSync, readdirSync } from "node:fs";
3
3
  import { access, constants, copyFile, lstat, mkdir, readdir, rename, statfs, utimes, } from "node:fs/promises";
4
4
  import { dirname, isAbsolute, join, parse, relative, resolve, sep } from "node:path";
5
5
  import { z } from "zod";
6
+ import { errorText, hasErrnoCode } from "../../kernel/errors.js";
6
7
  import { repoint } from "../../kernel/paths.js";
7
- import { readSetting, writeSetting } from "../settings/repo.js";
8
+ import { deleteSetting, readSetting, writeSetting } from "../settings/repo.js";
8
9
  import { backupsFolderName, renderCacheFolder } from "./layout.js";
9
10
  // Where the files a user looks at live: project folders, scheduled backups and anything else
10
11
  // Slopify saves for them. New installs keep them in <Documents>/Slopify; installs from before
@@ -73,7 +74,7 @@ export async function ensureFilesFolders(layout) {
73
74
  }
74
75
  async function emptyOrMissing(path) {
75
76
  const entries = await readdir(path).catch((error) => {
76
- if (hasCode(error, "ENOENT"))
77
+ if (hasErrnoCode(error, "ENOENT"))
77
78
  return [];
78
79
  throw error;
79
80
  });
@@ -172,7 +173,7 @@ export function createFilesService(deps) {
172
173
  finished = {
173
174
  ...current.progress,
174
175
  phase: "failed",
175
- error: error instanceof Error ? error.message : String(error),
176
+ error: errorText(error),
176
177
  };
177
178
  })
178
179
  .finally(() => {
@@ -294,7 +295,7 @@ function sourceFiles(pair) {
294
295
  entries = readdirSync(dir, { withFileTypes: true });
295
296
  }
296
297
  catch (error) {
297
- if (hasCode(error, "ENOENT"))
298
+ if (hasErrnoCode(error, "ENOENT"))
298
299
  return;
299
300
  throw error;
300
301
  }
@@ -383,7 +384,7 @@ async function runMove(deps, from, target, pairs, update) {
383
384
  }
384
385
  const to = { kind: "root", root: target };
385
386
  writeSetting(deps.db, filesLocationKey, JSON.stringify(to));
386
- deps.db.prepare("DELETE FROM settings WHERE key=?").run(filesMoveKey);
387
+ deleteSetting(deps.db, filesMoveKey);
387
388
  const old = from.kind === "root" ? from.root : deps.paths.projects;
388
389
  repoint(deps.paths, filesLayoutOf(to, deps.paths.dataDir));
389
390
  return old;
@@ -514,9 +515,6 @@ function within(root, path) {
514
515
  function samePath(a, b) {
515
516
  return relative(resolve(a), resolve(b)) === "";
516
517
  }
517
- function hasCode(error, code) {
518
- return typeof error === "object" && error !== null && "code" in error && error.code === code;
519
- }
520
518
  function formatBytes(bytes) {
521
519
  if (!Number.isFinite(bytes))
522
520
  return "unknown";
@@ -1,6 +1,11 @@
1
1
  import { extname, isAbsolute, relative, resolve } from "node:path";
2
- // A project's folder: its readable name once it has one (`places.ts`), its id until then.
2
+ import { internalError } from "../../kernel/errors.js";
3
+ import { templateSamplesDir } from "../../kernel/paths.js";
4
+ // A project's folder: its readable name once it has one (`places.ts`), its id until then. A
5
+ // template's studio sample is kept in the data dir by its id, out of the user's Projects folder.
3
6
  export function projectDir(paths, projectId) {
7
+ if (paths.samples?.has(projectId) === true)
8
+ return contained(templateSamplesDir(paths), projectId);
4
9
  return contained(paths.projects, paths.places?.folderOf(projectId) ?? projectId);
5
10
  }
6
11
  // The clips of each project's last render (`slices/video/clip-cache.ts`). Hidden, so
@@ -8,10 +13,14 @@ export function projectDir(paths, projectId) {
8
13
  // files leaves it behind and a render simply encodes its clips again.
9
14
  export const renderCacheFolder = ".render-cache";
10
15
  export function renderCacheDir(paths, projectId) {
16
+ if (paths.samples?.has(projectId) === true)
17
+ return contained(templateSamplesDir(paths), `${renderCacheFolder}/${projectId}`);
11
18
  return contained(paths.projects, `${renderCacheFolder}/${projectId}`);
12
19
  }
13
20
  // A stored path on disk: where it was moved to (`places.ts`), or the stored path itself.
14
21
  export function outputPath(paths, projectId, relativePath) {
22
+ if (paths.samples?.has(projectId) === true)
23
+ return contained(projectDir(paths, projectId), relativePath);
15
24
  return contained(projectDir(paths, projectId), paths.places?.placeOf(projectId, relativePath) ?? relativePath);
16
25
  }
17
26
  // Scheduled backups (slices/backups) land in paths.backups unless the user picks another
@@ -31,7 +40,7 @@ function contained(root, path) {
31
40
  const target = resolve(root, path);
32
41
  const inside = relative(root, target);
33
42
  if (inside === "" || inside.startsWith("..") || isAbsolute(inside)) {
34
- throw new Error(`Slopify hit an internal error (the saved file path ${path} resolves outside ${root}). Try again; if it happens again, use Download diagnostics in Settings and report it.`);
43
+ throw new Error(internalError(`the saved file path ${path} resolves outside ${root}`));
35
44
  }
36
45
  return target;
37
46
  }
@@ -111,6 +120,8 @@ export function outputFileName(role, index, extension, stageKind) {
111
120
  return `audio-levelled${extension}`;
112
121
  case "reference":
113
122
  return `reference${extension}`;
123
+ case "social_post":
124
+ return `social/post-${String(index).padStart(2, "0")}.txt`;
114
125
  case "image":
115
126
  return `images/${String(index).padStart(3, "0")}${extension}`;
116
127
  }
@@ -52,5 +52,8 @@ export const outputRoles = [
52
52
  // Level the volume: a narration segment joined again from its pieces, each brought to one
53
53
  // loudness first (`meta.segment` says which). The exports play it in place of the plain join.
54
54
  "audio_levelled",
55
+ // A post for another site written by a Library prompt of kind "post" (`slices/social`);
56
+ // `meta.platform` says which site.
57
+ "social_post",
55
58
  ];
56
59
  export const stagedFileStates = ["copying", "staged"];
@@ -5,10 +5,12 @@ import { crc32 } from "node:zlib";
5
5
  import { strFromU8, strToU8, Unzip, UnzipInflate, zipSync } from "fflate";
6
6
  import { z } from "zod";
7
7
  import { transact } from "../../kernel/db/tx.js";
8
+ import { isUnreachablePath } from "../../kernel/errors.js";
8
9
  import { liveProject } from "../admission/repo.js";
9
10
  import { silenceGapSecondsMax } from "../admission/rules.js";
10
11
  import { detectSlots } from "../admission/substitute.js";
11
12
  import { agentConnectionKey } from "../agents/connection.js";
13
+ import { dailySampleBudgetKey, dailySampleBudgetSchema } from "../designs/budget.js";
12
14
  import { fontMaxBytes } from "../fonts/model.js";
13
15
  import { readFontMetadata } from "../fonts/sfnt.js";
14
16
  import { lintEntry, lintPrompt } from "../library/lint.js";
@@ -29,6 +31,8 @@ import { postingPlanKey, postingPlanSchema } from "../studio/plan.js";
29
31
  import { leadHoursMax } from "../studio/plan-model.js";
30
32
  import { leadHoursKey } from "../studio/releases.js";
31
33
  import { autoCommentKey, storedPlaylistsSchema, storedProjectPlaylistsSchema, studioChannelPlaylistPrefix, studioFillQueuePrefix, studioPairingKey, studioPlaylistKey, studioProjectPlaylistsKey, studioRealFootageKey, } from "../studio/settings.js";
34
+ import { dailyBudgetKey, dailyBudgetMax, samplesUsePlansKey } from "../template-studio/budget.js";
35
+ import { notTemplateSample } from "../template-studio/repo.js";
32
36
  import { channelLinksKey } from "../youtube/edits-repo.js";
33
37
  import { defaultBackupsDir, projectDir, stagingPath } from "./layout.js";
34
38
  import { stageKinds } from "./model.js";
@@ -299,7 +303,7 @@ function planStagedExport(deps) {
299
303
  stat = lstatSync(path);
300
304
  }
301
305
  catch (error) {
302
- if (unavailable(error))
306
+ if (isUnreachablePath(error))
303
307
  continue;
304
308
  throw error;
305
309
  }
@@ -410,6 +414,12 @@ function portableSettings(settings) {
410
414
  storedProjectPlaylistsSchema.parse(parsed);
411
415
  else if (key === channelLinksKey)
412
416
  storedChannelLinks.parse(parsed);
417
+ else if (key === dailySampleBudgetKey)
418
+ dailySampleBudgetSchema.parse(parsed);
419
+ else if (key === dailyBudgetKey)
420
+ z.number().finite().min(0).max(dailyBudgetMax).parse(parsed);
421
+ else if (key === samplesUsePlansKey)
422
+ z.boolean().parse(parsed);
413
423
  else if (key === realPersonVoicesKey ||
414
424
  key === photorealisticPromptsKey ||
415
425
  key === studioRealFootageKey)
@@ -462,7 +472,7 @@ function validateLibraryRows(manifest) {
462
472
  const promptIds = new Set();
463
473
  const promptNames = new Set();
464
474
  for (const row of manifest.prompts) {
465
- if (row.name !== row.name.trim() || lintPrompt(row).length > 0)
475
+ if (row.name !== row.name.trim() || lintPrompt(row, detectSlots(row.body).names).length > 0)
466
476
  throw new Error("The backup contains an invalid prompt.");
467
477
  uniquePortableRow(promptIds, row.id, "prompt id");
468
478
  uniquePortableRow(promptNames, `${row.kind}\0${row.name.toLowerCase()}`, "prompt name");
@@ -471,7 +481,7 @@ function validateLibraryRows(manifest) {
471
481
  const entryIds = new Set();
472
482
  const entryNames = new Set();
473
483
  for (const row of manifest.entries) {
474
- if (row.name !== row.name.trim() || lintEntry(row).length > 0)
484
+ if (row.name !== row.name.trim() || lintEntry(row, detectSlots(row.body).names).length > 0)
475
485
  throw new Error("The backup contains an invalid entry.");
476
486
  uniquePortableRow(entryIds, row.id, "entry id");
477
487
  uniquePortableRow(entryNames, `${row.category}\0${row.name.toLowerCase()}`, "entry name");
@@ -825,7 +835,7 @@ function installedUploadedFonts(paths) {
825
835
  names = readdirSync(root);
826
836
  }
827
837
  catch (error) {
828
- if (unavailable(error))
838
+ if (isUnreachablePath(error))
829
839
  return [];
830
840
  throw error;
831
841
  }
@@ -884,7 +894,7 @@ function readStoredFont(path) {
884
894
  return readFileSync(descriptor);
885
895
  }
886
896
  catch (error) {
887
- if (unavailable(error))
897
+ if (isUnreachablePath(error))
888
898
  return undefined;
889
899
  throw error;
890
900
  }
@@ -903,11 +913,6 @@ function writeImportedFile(deps, path, content, created) {
903
913
  closeSync(descriptor);
904
914
  }
905
915
  }
906
- function unavailable(error) {
907
- return (error instanceof Error &&
908
- "code" in error &&
909
- ["ENOENT", "ENOTDIR", "EACCES", "EPERM", "ELOOP"].includes(String(error.code)));
910
- }
911
916
  function planStagedImport(deps, archive, files) {
912
917
  const sourceIds = new Set();
913
918
  const archivePaths = new Set();
@@ -963,7 +968,7 @@ export function storageBytes(paths) {
963
968
  export function storageUsage(deps) {
964
969
  const totals = storageBytes(deps.paths);
965
970
  const byProject = deps.db
966
- .prepare(`SELECT id,title FROM projects WHERE ${liveProject()} ORDER BY created_at DESC, id DESC`)
971
+ .prepare(`SELECT id,title FROM projects WHERE ${liveProject()} AND ${notTemplateSample()} ORDER BY created_at DESC, id DESC`)
967
972
  .all()
968
973
  .flatMap((row) => {
969
974
  if (typeof row.id !== "string" || typeof row.title !== "string")
@@ -1,4 +1,5 @@
1
1
  import { z } from "zod";
2
+ import { socialPlatforms } from "../social/model.js";
2
3
  import { outputRoles, stagedFileStates, stageKinds } from "./model.js";
3
4
  export const metaSchema = z.object({
4
5
  segment: z.enum(["body", "intro", "outro"]).optional(),
@@ -26,6 +27,7 @@ export const metaSchema = z.object({
26
27
  })
27
28
  .optional(),
28
29
  master: z.object({ target: z.number(), integrated: z.number(), truePeak: z.number() }).optional(),
30
+ platform: z.enum(socialPlatforms).optional(),
29
31
  });
30
32
  export const outputSchema = z.object({
31
33
  id: z.string(),
@@ -1,4 +1,5 @@
1
1
  import { rmSync } from "node:fs";
2
+ import { errorText } from "../../kernel/errors.js";
2
3
  import { stagingPath } from "./layout.js";
3
4
  import { deleteStagedFile, stagedFileById } from "./repo.js";
4
5
  export function stagedFileReferenced(db, id) {
@@ -18,7 +19,7 @@ export function releaseStagedFile(deps, id) {
18
19
  }
19
20
  catch (error) {
20
21
  deps.log.write("warn", "staging.release", {
21
- detail: `${id}: ${error instanceof Error ? error.message : String(error)}`,
22
+ detail: `${id}: ${errorText(error)}`,
22
23
  });
23
24
  return;
24
25
  }
@@ -3,10 +3,10 @@ import { basename, dirname } from "node:path";
3
3
  import { pipeline } from "node:stream/promises";
4
4
  import { z } from "zod";
5
5
  import { transact } from "../../kernel/db/tx.js";
6
+ import { errorText } from "../../kernel/errors.js";
6
7
  import { extensionOf, outputFileName, outputPath, stagingPath } from "./layout.js";
7
8
  import { deleteStagedFile, insertOutput, insertStagedFile, markStagedFileCopied, stagedFileById, } from "./repo.js";
8
9
  import { releaseStagedFile, stagedFileReferenced } from "./staging-refs.js";
9
- export { prepareStagedFile, prepareText } from "./prepare.js";
10
10
  // The upload's own name is data, never a path component: the file is stored under a
11
11
  // generated id, and anything with a separator, a traversal, or a control character is
12
12
  // refused rather than sanitised, so nothing silently lands under a name nobody chose.
@@ -53,7 +53,7 @@ export async function stageUpload(deps, input) {
53
53
  deps.emit(event);
54
54
  }
55
55
  catch (error) {
56
- deps.log.write("warn", "staging.emit", { detail: messageOf(error) });
56
+ deps.log.write("warn", "staging.emit", { detail: errorText(error) });
57
57
  }
58
58
  };
59
59
  const progress = (state) => {
@@ -92,10 +92,10 @@ export async function stageUpload(deps, input) {
92
92
  stagedFileId: id,
93
93
  stageKind: input.stageKind,
94
94
  originalFilename,
95
- detail: messageOf(error),
95
+ detail: errorText(error),
96
96
  });
97
97
  deps.log.write("error", "staging.failed", {
98
- detail: `${id} ${originalFilename}: ${messageOf(error)}`,
98
+ detail: `${id} ${originalFilename}: ${errorText(error)}`,
99
99
  });
100
100
  throw error;
101
101
  }
@@ -152,7 +152,7 @@ export function attachStagedFile(deps, input) {
152
152
  catch (error) {
153
153
  deps.log.write("error", "storage.attach", {
154
154
  projectId: input.projectId,
155
- detail: `${staged.id} → ${name}: ${messageOf(error)}`,
155
+ detail: `${staged.id} → ${name}: ${errorText(error)}`,
156
156
  });
157
157
  throw error;
158
158
  }
@@ -210,7 +210,7 @@ function discard(deps, id, target, event) {
210
210
  }
211
211
  catch (error) {
212
212
  // The row stays `copying`, so the next boot's reconcile removes the file instead.
213
- deps.log.write("warn", event, { detail: `${id}: ${messageOf(error)}` });
213
+ deps.log.write("warn", event, { detail: `${id}: ${errorText(error)}` });
214
214
  return;
215
215
  }
216
216
  deleteStagedFile(deps.db, id);
@@ -222,6 +222,3 @@ function closed(sink) {
222
222
  sink.once("close", resolve);
223
223
  });
224
224
  }
225
- function messageOf(error) {
226
- return error instanceof Error ? error.message : String(error);
227
- }
@@ -2,6 +2,7 @@ import { existsSync, readdirSync, rmdirSync, statSync, unlinkSync } from "node:f
2
2
  import { join, relative, sep } from "node:path";
3
3
  import { derive } from "../../kernel/runner/graph.js";
4
4
  import { liveProject, projectExists, projectPaused, stagesOf } from "../admission/repo.js";
5
+ import { notTemplateSample } from "../template-studio/repo.js";
5
6
  import { projectSettled } from "./arrange.js";
6
7
  import { backupsFolderName, outputPath, projectDir } from "./layout.js";
7
8
  import { pieceFile } from "./reconcile.js";
@@ -32,6 +33,7 @@ export const outputRoles = [
32
33
  "image",
33
34
  "animated_image",
34
35
  "short_image",
36
+ "social_post",
35
37
  ];
36
38
  export function projectStorage(deps, projectId) {
37
39
  const files = filesOf(deps.paths, projectId);
@@ -105,14 +107,17 @@ export function deleteOldVersions(deps, projectId) {
105
107
  removeEmptyUnder(join(projectDir(deps.paths, projectId), "History"));
106
108
  return { ok: true, files: count, bytesFreed };
107
109
  }
108
- // Every project's old versions, skipping any that runs or waits to run.
110
+ // Every project's old versions, skipping any that runs or waits to run. A template studio
111
+ // sample keeps its files (its bake-offs and resamples reuse them), and is no project to count.
109
112
  export function deleteAllOldVersions(deps) {
110
113
  const busyIds = [];
111
114
  let projects = 0;
112
115
  let files = 0;
113
116
  let bytesFreed = 0;
114
117
  let busy = 0;
115
- for (const row of deps.db.prepare(`SELECT id FROM projects WHERE ${liveProject()}`).all()) {
118
+ for (const row of deps.db
119
+ .prepare(`SELECT id FROM projects WHERE ${liveProject()} AND ${notTemplateSample()}`)
120
+ .all()) {
116
121
  const id = String(row.id);
117
122
  if (placedFiles(deps.paths, id).size === 0)
118
123
  continue;
@@ -138,7 +143,9 @@ export function cleanUpAll(deps) {
138
143
  let files = old.files;
139
144
  let bytesFreed = old.bytesFreed;
140
145
  let skipped = old.busy;
141
- for (const row of deps.db.prepare(`SELECT id FROM projects WHERE ${liveProject()}`).all()) {
146
+ for (const row of deps.db
147
+ .prepare(`SELECT id FROM projects WHERE ${liveProject()} AND ${notTemplateSample()}`)
148
+ .all()) {
142
149
  const id = String(row.id);
143
150
  const result = keepOutputsOnly(deps, id);
144
151
  if (result.ok) {
@@ -2,7 +2,7 @@ import { listProjects } from "../admission/repo.js";
2
2
  import { uploadPack } from "./pack.js";
3
3
  import { readPlan } from "./plan.js";
4
4
  import { seriesOf } from "./plan-model.js";
5
- import { allReleases, lineSlots, readLeadHours, releasesOf } from "./releases.js";
5
+ import { allReleases, lineShortTimes, lineSlots, lineZone, readLeadHours, releasesOf, } from "./releases.js";
6
6
  import { videoOf } from "./videos.js";
7
7
  const hourMs = 60 * 60 * 1000;
8
8
  export function releaseCalendar(deps, options) {
@@ -30,6 +30,7 @@ export function releaseCalendar(deps, options) {
30
30
  return {
31
31
  at: long.at,
32
32
  line: long.line,
33
+ timeZone: lineZone(plan, long.line),
33
34
  series: project === undefined ? "" : seriesOf(project.config),
34
35
  project: { id: long.projectId, title: project?.title ?? "" },
35
36
  items: items.map((item) => {
@@ -66,6 +67,8 @@ export function releaseCalendar(deps, options) {
66
67
  entries.push({
67
68
  at: slot.longAt,
68
69
  line: slot.row,
70
+ timeZone: lineZone(plan, slot.row),
71
+ shortsAt: lineShortTimes(plan, slot.row, slot.longAt),
69
72
  series: seriesOfLine(slot.row),
70
73
  project: null,
71
74
  items: [],
@@ -1,5 +1,5 @@
1
1
  import { z } from "zod";
2
- import { readSetting, writeSetting } from "../settings/repo.js";
2
+ import { readJsonSetting, writeSetting } from "../settings/repo.js";
3
3
  export const uploadPickSchema = z.object({
4
4
  title: z.number().int().min(0).max(9),
5
5
  thumbnail: z.number().int().min(0).max(9),
@@ -7,16 +7,7 @@ export const uploadPickSchema = z.object({
7
7
  export const uploadPickPrefix = "studio.uploadPick.";
8
8
  const keyOf = (projectId) => `${uploadPickPrefix}${projectId}`;
9
9
  export function readUploadPick(db, projectId) {
10
- const stored = readSetting(db, keyOf(projectId));
11
- if (stored === undefined)
12
- return { title: 0, thumbnail: 0 };
13
- try {
14
- const parsed = uploadPickSchema.safeParse(JSON.parse(stored));
15
- return parsed.success ? parsed.data : { title: 0, thumbnail: 0 };
16
- }
17
- catch {
18
- return { title: 0, thumbnail: 0 };
19
- }
10
+ return readJsonSetting(db, keyOf(projectId), uploadPickSchema, { title: 0, thumbnail: 0 });
20
11
  }
21
12
  export function writeUploadPick(db, projectId, pick) {
22
13
  writeSetting(db, keyOf(projectId), JSON.stringify(uploadPickSchema.parse(pick)));